Blob Blame History Raw
From 0d93cd92bd616474da0c842bc4e88f6921da18f1 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 3.0
Git-commit: 0d93cd92bd616474da0c842bc4e88f6921da18f1
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>

---
 .../nouveau/include/nvkm/subdev/bios/init.h   |   6 +-
 .../gpu/drm/nouveau/nvkm/engine/disp/gf119.c  | 125 +------------
 .../gpu/drm/nouveau/nvkm/engine/disp/ior.h    |   1 +
 .../gpu/drm/nouveau/nvkm/engine/disp/nv50.c   | 168 ++----------------
 .../gpu/drm/nouveau/nvkm/engine/disp/nv50.h   |   1 +
 .../gpu/drm/nouveau/nvkm/engine/disp/outp.h   |   4 -
 .../gpu/drm/nouveau/nvkm/engine/disp/sorg94.c |  63 ++-----
 .../gpu/drm/nouveau/nvkm/subdev/bios/init.c   |   5 -
 8 files changed, 35 insertions(+), 338 deletions(-)

diff --git a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h
index c7383e1f0966..06ab48052128 100644
--- a/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h
+++ b/drivers/gpu/drm/nouveau/include/nvkm/subdev/bios/init.h
@@ -3,16 +3,12 @@
 
 struct nvbios_init {
 	struct nvkm_subdev *subdev;
-	struct nvkm_bios *bios;
 	u32 offset;
 
 	struct dcb_output *outp;
 	int or;
 	int link;
-	union {
-		int head;
-		int crtc;
-	};
+	int head;
 
 	/* internal state used during parsing */
 	u8 execute;
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
index f4a05549f642..d8765b57180b 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/gf119.c
@@ -26,128 +26,6 @@
 #include "ior.h"
 #include "rootnv50.h"
 
-#include <subdev/bios.h>
-#include <subdev/bios/disp.h>
-#include <subdev/bios/init.h>
-#include <subdev/bios/pll.h>
-#include <subdev/devinit.h>
-
-static struct nvkm_output *
-exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
-	    u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
-	    struct nvbios_outp *info)
-{
-	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-	struct nvkm_bios *bios = subdev->device->bios;
-	struct nvkm_output *outp;
-	u16 mask, type;
-
-	if (or < 4) {
-		type = DCB_OUTPUT_ANALOG;
-		mask = 0;
-	} else {
-		or -= 4;
-		switch (ctrl & 0x00000f00) {
-		case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
-		case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
-		case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
-		case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
-		case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
-		case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
-		default:
-			nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
-			return NULL;
-		}
-	}
-
-	mask  = 0x00c0 & (mask << 6);
-	mask |= 0x0001 << or;
-	mask |= 0x0100 << head;
-
-	list_for_each_entry(outp, &disp->base.outp, head) {
-		if ((outp->info.hasht & 0xff) == type &&
-		    (outp->info.hashm & mask) == mask) {
-			*data = nvbios_outp_match(bios, outp->info.hasht, mask,
-						  ver, hdr, cnt, len, info);
-			if (!*data)
-				return NULL;
-			return outp;
-		}
-	}
-
-	return NULL;
-}
-
-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;
-	struct nvkm_device *device = subdev->device;
-	struct nvkm_bios *bios = device->bios;
-	struct nvkm_output *outp;
-	struct nvbios_outp info1;
-	struct nvbios_ocfg info2;
-	u8  ver, hdr, cnt, len;
-	u32 data, ctrl = 0;
-	int or;
-
-	for (or = 0; !(ctrl & (1 << head)) && or < 8; or++) {
-		ctrl = nvkm_rd32(device, 0x660180 + (or * 0x20));
-		if (ctrl & (1 << head))
-			break;
-	}
-
-	if (or == 8)
-		return NULL;
-
-	outp = exec_lookup(disp, head, or, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
-	if (!outp)
-		return NULL;
-
-	*conf = (ctrl & 0x00000f00) >> 8;
-	switch (outp->info.type) {
-	case DCB_OUTPUT_TMDS:
-		if (*conf == 5)
-			*conf |= 0x0100;
-		break;
-	case DCB_OUTPUT_LVDS:
-		*conf |= disp->sor.lvdsconf;
-		break;
-	default:
-		break;
-	}
-
-	data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8,
-				 &ver, &hdr, &cnt, &len, &info2);
-	if (data && id < 0xff) {
-		data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
-		if (data) {
-			struct nvbios_init init = {
-				.subdev = subdev,
-				.bios = bios,
-				.offset = data,
-				.outp = &outp->info,
-				.crtc = head,
-				.execute = 1,
-			};
-
-			nvbios_exec(&init);
-		}
-	}
-
-	return outp;
-}
-
-static void
-gf119_disp_intr_unk4_0(struct nv50_disp *disp, int head)
-{
-	struct nvkm_device *device = disp->base.engine.subdev.device;
-	u32 pclk = nvkm_rd32(device, 0x660450 + (head * 0x300)) / 1000;
-	u32 conf;
-
-	exec_clkcmp(disp, head, 1, pclk, &conf);
-}
-
 void
 gf119_disp_super(struct work_struct *work)
 {
@@ -195,8 +73,7 @@ gf119_disp_super(struct work_struct *work)
 		list_for_each_entry(head, &disp->base.head, head) {
 			if (!(mask[head->id] & 0x00001000))
 				continue;
-			nvkm_debug(subdev, "supervisor 3.0 - head %d\n", head->id);
-			gf119_disp_intr_unk4_0(disp, head->id);
+			nv50_disp_super_3_0(disp, head);
 		}
 	}
 
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
index d6bfaa53f96b..1e11e25a41f1 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/ior.h
@@ -52,6 +52,7 @@ struct nvkm_ior_func {
 	int (*sense)(struct nvkm_ior *, u32 loadval);
 	void (*clock)(struct nvkm_ior *);
 	void (*war_2)(struct nvkm_ior *);
+	void (*war_3)(struct nvkm_ior *);
 
 	struct {
 		void (*ctrl)(struct nvkm_ior *, int head, bool enable,
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
index f21e0e92d6f1..0c570dbd3021 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.c
@@ -238,162 +238,23 @@ nv50_disp_super_ior_arm(struct nvkm_head *head)
 	return NULL;
 }
 
-static struct nvkm_output *
-exec_lookup(struct nv50_disp *disp, int head, int or, u32 ctrl,
-	    u32 *data, u8 *ver, u8 *hdr, u8 *cnt, u8 *len,
-	    struct nvbios_outp *info)
-{
-	struct nvkm_subdev *subdev = &disp->base.engine.subdev;
-	struct nvkm_bios *bios = subdev->device->bios;
-	struct nvkm_output *outp;
-	u16 mask, type;
-
-	if (or < 4) {
-		type = DCB_OUTPUT_ANALOG;
-		mask = 0;
-	} else
-	if (or < 8) {
-		switch (ctrl & 0x00000f00) {
-		case 0x00000000: type = DCB_OUTPUT_LVDS; mask = 1; break;
-		case 0x00000100: type = DCB_OUTPUT_TMDS; mask = 1; break;
-		case 0x00000200: type = DCB_OUTPUT_TMDS; mask = 2; break;
-		case 0x00000500: type = DCB_OUTPUT_TMDS; mask = 3; break;
-		case 0x00000800: type = DCB_OUTPUT_DP; mask = 1; break;
-		case 0x00000900: type = DCB_OUTPUT_DP; mask = 2; break;
-		default:
-			nvkm_error(subdev, "unknown SOR mc %08x\n", ctrl);
-			return NULL;
-		}
-		or  -= 4;
-	} else {
-		or   = or - 8;
-		type = 0x0010;
-		mask = 0;
-		switch (ctrl & 0x00000f00) {
-		case 0x00000000: type |= disp->pior.type[or]; break;
-		default:
-			nvkm_error(subdev, "unknown PIOR mc %08x\n", ctrl);
-			return NULL;
-		}
-	}
-
-	mask  = 0x00c0 & (mask << 6);
-	mask |= 0x0001 << or;
-	mask |= 0x0100 << head;
-
-	list_for_each_entry(outp, &disp->base.outp, head) {
-		if ((outp->info.hasht & 0xff) == type &&
-		    (outp->info.hashm & mask) == mask) {
-			*data = nvbios_outp_match(bios, outp->info.hasht, mask,
-						  ver, hdr, cnt, len, info);
-			if (!*data)
-				return NULL;
-			return outp;
-		}
-	}
-
-	return NULL;
-}
-
-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;
-	struct nvkm_device *device = subdev->device;
-	struct nvkm_bios *bios = device->bios;
-	struct nvkm_output *outp;
-	struct nvbios_outp info1;
-	struct nvbios_ocfg info2;
-	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, 0x610b58 + (i * 8));
-
-	/* SOR */
-	if (!(ctrl & (1 << head))) {
-		if (device->chipset  < 0x90 ||
-		    device->chipset == 0x92 ||
-		    device->chipset == 0xa0) {
-			reg = 0x610b70;
-		} else {
-			reg = 0x610794;
-		}
-		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, 0x610b80 + (i * 8));
-		i += 8;
-	}
-
-	if (!(ctrl & (1 << head)))
-		return NULL;
-	i--;
-
-	outp = exec_lookup(disp, head, i, ctrl, &data, &ver, &hdr, &cnt, &len, &info1);
-	if (!outp)
-		return NULL;
-
-	*conf = (ctrl & 0x00000f00) >> 8;
-	if (outp->info.location == 0) {
-		switch (outp->info.type) {
-		case DCB_OUTPUT_TMDS:
-			if (*conf == 5)
-				*conf |= 0x0100;
-			break;
-		case DCB_OUTPUT_LVDS:
-			*conf |= disp->sor.lvdsconf;
-			break;
-		default:
-			break;
-		}
-	} else {
-		*conf = (ctrl & 0x00000f00) >> 8;
-		pclk = pclk / 2;
-	}
-
-	data = nvbios_ocfg_match(bios, data, *conf & 0xff, *conf >> 8,
-				 &ver, &hdr, &cnt, &len, &info2);
-	if (data && id < 0xff) {
-		data = nvbios_oclk_match(bios, info2.clkcmp[id], pclk);
-		if (data) {
-			struct nvbios_init init = {
-				.subdev = subdev,
-				.bios = bios,
-				.offset = data,
-				.outp = &outp->info,
-				.crtc = head,
-				.execute = 1,
-			};
-
-			nvbios_exec(&init);
-		}
-	}
-
-	return outp;
-}
-
-static void
-nv50_disp_intr_unk40_0(struct nv50_disp *disp, int head)
+void
+nv50_disp_super_3_0(struct nv50_disp *disp, struct nvkm_head *head)
 {
-	struct nvkm_device *device = disp->base.engine.subdev.device;
-	struct nvkm_output *outp;
-	u32 pclk = nvkm_rd32(device, 0x610ad0 + (head * 0x540)) & 0x3fffff;
-	u32 conf;
+	struct nvkm_ior *ior;
 
-	outp = exec_clkcmp(disp, head, 1, pclk, &conf);
-	if (!outp)
+	/* Determine which OR, if any, we're attaching to the head. */
+	HEAD_DBG(head, "supervisor 3.0");
+	ior = nv50_disp_super_ior_asy(head);
+	if (!ior)
 		return;
 
-	nv50_disp_dptmds_war_3(disp, &outp->info);
+	/* Execute OnInt3 IED script. */
+	nv50_disp_super_ied_on(head, ior, 1, head->asy.hz / 1000);
+
+	/* OR-specific handling. */
+	if (ior->func->war_3)
+		ior->func->war_3(ior);
 }
 
 static void
@@ -660,9 +521,8 @@ 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_unk40_0(disp, head->id);
+			nv50_disp_super_3_0(disp, head);
 		}
-		nv50_disp_update_sppll1(disp);
 	}
 
 	nvkm_wr32(device, 0x610030, 0x80000000);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
index b6092b8b528a..19c635663399 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/nv50.h
@@ -30,6 +30,7 @@ void nv50_disp_super_1_0(struct nv50_disp *, struct nvkm_head *);
 void nv50_disp_super_2_0(struct nv50_disp *, struct nvkm_head *);
 void nv50_disp_super_2_1(struct nv50_disp *, struct nvkm_head *);
 void nv50_disp_super_2_2(struct nv50_disp *, struct nvkm_head *);
+void nv50_disp_super_3_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 **);
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
index 7655d9293911..146d101d4891 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/outp.h
@@ -43,10 +43,6 @@ struct nvkm_outp_func {
 	void (*release)(struct nvkm_outp *, struct nvkm_ior *);
 };
 
-#define nvkm_output nvkm_outp
-#define nvkm_output_func nvkm_outp_func
-#define nvkm_output_new_ nvkm_outp_new_
-
 #define OUTP_MSG(o,l,f,a...) do {                                              \
 	struct nvkm_outp *_outp = (o);                                         \
 	nvkm_##l(&_outp->disp->engine.subdev, "outp %02x:%04x:%04x: "f"\n",    \
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
index bdace3852b37..771a1abbdf15 100644
--- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
+++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/sorg94.c
@@ -22,7 +22,6 @@
  * Authors: Ben Skeggs
  */
 #include "ior.h"
-#include "nv50.h"
 
 #include <subdev/timer.h>
 
@@ -120,38 +119,6 @@ g94_sor_dp_links(struct nvkm_ior *sor, struct nvkm_i2c_aux *aux)
 	return 0;
 }
 
-static bool
-nv50_disp_dptmds_war(struct nvkm_device *device)
-{
-	switch (device->chipset) {
-	case 0x94:
-	case 0x96:
-	case 0x98:
-		return true;
-	default:
-		break;
-	}
-	return false;
-}
-
-static bool
-nv50_disp_dptmds_war_needed(struct nv50_disp *disp, struct dcb_output *outp)
-{
-	struct nvkm_device *device = disp->base.engine.subdev.device;
-	const u32 soff = __ffs(outp->or) * 0x800;
-	if (nv50_disp_dptmds_war(device) && outp->type == DCB_OUTPUT_TMDS) {
-		switch (nvkm_rd32(device, 0x614300 + soff) & 0x00030000) {
-		case 0x00000000:
-		case 0x00030000:
-			return true;
-		default:
-			break;
-		}
-	}
-	return false;
-
-}
-
 static bool
 g94_sor_war_needed(struct nvkm_ior *sor)
 {
@@ -169,18 +136,19 @@ g94_sor_war_needed(struct nvkm_ior *sor)
 	return false;
 }
 
-void
-nv50_disp_update_sppll1(struct nv50_disp *disp)
+static void
+g94_sor_war_update_sppll1(struct nvkm_disp *disp)
 {
-	struct nvkm_device *device = disp->base.engine.subdev.device;
+	struct nvkm_device *device = disp->engine.subdev.device;
+	struct nvkm_ior *ior;
 	bool used = false;
-	int sor;
+	u32 clksor;
 
-	if (!nv50_disp_dptmds_war(device))
-		return;
+	list_for_each_entry(ior, &disp->ior, head) {
+		if (ior->type != SOR)
+			continue;
 
-	for (sor = 0; sor < disp->func->sor.nr; sor++) {
-		u32 clksor = nvkm_rd32(device, 0x614300 + (sor * 0x800));
+		clksor = nvkm_rd32(device, 0x614300 + nv50_ior_base(ior));
 		switch (clksor & 0x03000000) {
 		case 0x02000000:
 		case 0x03000000:
@@ -197,14 +165,14 @@ nv50_disp_update_sppll1(struct nv50_disp *disp)
 	nvkm_mask(device, 0x00e840, 0x80000000, 0x00000000);
 }
 
-void
-nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
+static void
+g94_sor_war_3(struct nvkm_ior *sor)
 {
-	struct nvkm_device *device = disp->base.engine.subdev.device;
-	const u32 soff = __ffs(outp->or) * 0x800;
+	struct nvkm_device *device = sor->disp->engine.subdev.device;
+	const u32 soff = nv50_ior_base(sor);
 	u32 sorpwr;
 
-	if (!nv50_disp_dptmds_war_needed(disp, outp))
+	if (!g94_sor_war_needed(sor))
 		return;
 
 	sorpwr = nvkm_rd32(device, 0x61c004 + soff);
@@ -235,6 +203,8 @@ nv50_disp_dptmds_war_3(struct nv50_disp *disp, struct dcb_output *outp)
 	if (sorpwr & 0x00000001) {
 		nvkm_mask(device, 0x61c004 + soff, 0x80000001, 0x80000001);
 	}
+
+	g94_sor_war_update_sppll1(sor->disp);
 }
 
 static void
@@ -293,6 +263,7 @@ g94_sor = {
 	.power = nv50_sor_power,
 	.clock = nv50_sor_clock,
 	.war_2 = g94_sor_war_2,
+	.war_3 = g94_sor_war_3,
 	.dp = {
 		.lanes = { 2, 1, 0, 3},
 		.links = g94_sor_dp_links,
diff --git a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
index 2095f43e16ac..b58ee99f7bfc 100644
--- a/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
+++ b/drivers/gpu/drm/nouveau/nvkm/subdev/bios/init.c
@@ -2278,11 +2278,6 @@ nvbios_exec(struct nvbios_init *init)
 {
 	struct nvkm_bios *bios = init->subdev->device->bios;
 
-	if (init->bios) {
-		init->or = init->outp ? ffs(init->outp->or) - 1 : -1;
-		init->link = init->outp ? init->outp->sorconf.link : 0;
-	}
-
 	init->nested++;
 	while (init->offset) {
 		u8 opcode = nvbios_rd08(bios, init->offset);
-- 
2.17.0