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>

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

--- 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;
--- 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 *wor
 		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);
 		}
 	}
 
--- 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,
--- 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
 	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);
--- 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_dis
 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 **);
--- 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",    \
--- 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>
 
@@ -121,38 +120,6 @@ g94_sor_dp_links(struct nvkm_ior *sor, s
 }
 
 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)
 {
 	struct nvkm_device *device = sor->disp->engine.subdev.device;
@@ -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
 	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
 	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,
--- 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);