small update to my last version I sent out. Patches 3-6 are optional and should only improve detecting the max clocks for HDMI and DP, but they didn't underwent big testing and I am a bit concerned, that it might break detecting the DP limits on some boards. Karol Herbst (6): kms/nv50: move nv50_mstm out of the dp union in nouveau_encoder kms/nv50: reject interlaced modes if the hardware doesn't support it kms/nv50: add core957d class kms/nv50: read out display max clocks kms/nv50: detect HDMI max MHz correctly kms/nv50: detect LVDS max MHz correctly drm/nouveau/dispnv50/Kbuild | 1 + drm/nouveau/dispnv50/core.c | 6 ++-- drm/nouveau/dispnv50/core.h | 23 +++++++++++++ drm/nouveau/dispnv50/core507d.c | 25 ++++++++++++++ drm/nouveau/dispnv50/core907d.c | 27 +++++++++++++++ drm/nouveau/dispnv50/core917d.c | 2 ++ drm/nouveau/dispnv50/core957d.c | 60 +++++++++++++++++++++++++++++++++ drm/nouveau/dispnv50/disp.c | 53 ++++++++++++++++++++--------- drm/nouveau/nouveau_connector.c | 26 +++++++++----- drm/nouveau/nouveau_dp.c | 2 +- drm/nouveau/nouveau_encoder.h | 11 +++++- 11 files changed, 208 insertions(+), 28 deletions(-) create mode 100644 drm/nouveau/dispnv50/core957d.c -- 2.17.1
Karol Herbst
2018-Aug-03  12:19 UTC
[Nouveau] [PATCH v3 1/6] kms/nv50: move nv50_mstm out of the dp union in nouveau_encoder
This field isn't only used for DP ones, but for all. Prevents memory
corruption when adding more structs to the union.
Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
 drm/nouveau/dispnv50/disp.c     | 12 ++++++------
 drm/nouveau/nouveau_connector.c |  4 ++--
 drm/nouveau/nouveau_dp.c        |  2 +-
 drm/nouveau/nouveau_encoder.h   |  2 +-
 4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c
index 42f47ac9..f04ab219 100644
--- a/drm/nouveau/dispnv50/disp.c
+++ b/drm/nouveau/dispnv50/disp.c
@@ -1398,7 +1398,7 @@ static void
 nv50_sor_destroy(struct drm_encoder *encoder)
 {
 	struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
-	nv50_mstm_del(&nv_encoder->dp.mstm);
+	nv50_mstm_del(&nv_encoder->mstm);
 	drm_encoder_cleanup(encoder);
 	kfree(encoder);
 }
@@ -1466,7 +1466,7 @@ nv50_sor_create(struct drm_connector *connector, struct
dcb_output *dcbe)
 		    ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04)) {
 			ret = nv50_mstm_new(nv_encoder, &nv_connector->aux, 16,
 					    nv_connector->base.base.id,
-					    &nv_encoder->dp.mstm);
+					    &nv_encoder->mstm);
 			if (ret)
 				return ret;
 		}
@@ -1622,7 +1622,7 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state
*state, u32 *interlock)
 
 	drm_for_each_encoder(encoder, drm->dev) {
 		if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
-			mstm = nouveau_encoder(encoder)->dp.mstm;
+			mstm = nouveau_encoder(encoder)->mstm;
 			if (mstm && mstm->modified)
 				nv50_mstm_prepare(mstm);
 		}
@@ -1636,7 +1636,7 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state
*state, u32 *interlock)
 
 	drm_for_each_encoder(encoder, drm->dev) {
 		if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
-			mstm = nouveau_encoder(encoder)->dp.mstm;
+			mstm = nouveau_encoder(encoder)->mstm;
 			if (mstm && mstm->modified)
 				nv50_mstm_cleanup(mstm);
 		}
@@ -2103,7 +2103,7 @@ nv50_display_fini(struct drm_device *dev)
 	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 		if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
 			nv_encoder = nouveau_encoder(encoder);
-			nv50_mstm_fini(nv_encoder->dp.mstm);
+			nv50_mstm_fini(nv_encoder->mstm);
 		}
 	}
 }
@@ -2121,7 +2121,7 @@ nv50_display_init(struct drm_device *dev)
 		if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) {
 			struct nouveau_encoder *nv_encoder  				nouveau_encoder(encoder);
-			nv50_mstm_init(nv_encoder->dp.mstm);
+			nv50_mstm_init(nv_encoder->mstm);
 		}
 	}
 
diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c
index af68eae4..5cc94944 100644
--- a/drm/nouveau/nouveau_connector.c
+++ b/drm/nouveau/nouveau_connector.c
@@ -1124,14 +1124,14 @@ nouveau_connector_hotplug(struct nvif_notify *notify)
 	if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) {
 		NV_DEBUG(drm, "service %s\n", name);
 		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP)))
-			nv50_mstm_service(nv_encoder->dp.mstm);
+			nv50_mstm_service(nv_encoder->mstm);
 	} else {
 		bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG);
 
 		NV_DEBUG(drm, "%splugged %s\n", plugged ? "" :
"un", name);
 		if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) {
 			if (!plugged)
-				nv50_mstm_remove(nv_encoder->dp.mstm);
+				nv50_mstm_remove(nv_encoder->mstm);
 		}
 
 		drm_helper_hpd_irq_event(connector->dev);
diff --git a/drm/nouveau/nouveau_dp.c b/drm/nouveau/nouveau_dp.c
index 0d052e16..7f71dff0 100644
--- a/drm/nouveau/nouveau_dp.c
+++ b/drm/nouveau/nouveau_dp.c
@@ -92,7 +92,7 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder)
 
 	nouveau_dp_probe_oui(dev, aux, dpcd);
 
-	ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst);
+	ret = nv50_mstm_detect(nv_encoder->mstm, dpcd, nouveau_mst);
 	if (ret == 1)
 		return NOUVEAU_DP_MST;
 	if (ret == 0)
diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h
index 3517f920..dfe095ad 100644
--- a/drm/nouveau/nouveau_encoder.h
+++ b/drm/nouveau/nouveau_encoder.h
@@ -58,9 +58,9 @@ struct nouveau_encoder {
 
 	struct nv04_output_reg restore;
 
+	struct nv50_mstm *mstm;
 	union {
 		struct {
-			struct nv50_mstm *mstm;
 			int link_nr;
 			int link_bw;
 		} dp;
-- 
2.17.1
Karol Herbst
2018-Aug-03  12:19 UTC
[Nouveau] [PATCH v3 2/6] kms/nv50: reject interlaced modes if the hardware doesn't support it
I ran into this issue on a gm204 GPU with a display reporting interlaced
modes. Nvidia dropped those modelines for DP, but not HDMI.
We should do the same on hardware where interlaced modes aren't supported
via DP.
v2: save caps for each sor seperatly
    rework setting the caps in nouveau_encoder
v3: rework struct structure
Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
 drm/nouveau/dispnv50/core.h     | 12 ++++++++++++
 drm/nouveau/dispnv50/core507d.c | 25 +++++++++++++++++++++++++
 drm/nouveau/dispnv50/core907d.c | 22 ++++++++++++++++++++++
 drm/nouveau/dispnv50/core917d.c |  2 ++
 drm/nouveau/dispnv50/disp.c     | 31 +++++++++++++++++++++++--------
 drm/nouveau/nouveau_connector.c |  2 ++
 drm/nouveau/nouveau_encoder.h   |  1 +
 7 files changed, 87 insertions(+), 8 deletions(-)
diff --git a/drm/nouveau/dispnv50/core.h b/drm/nouveau/dispnv50/core.h
index 8470df9d..62ef0d24 100644
--- a/drm/nouveau/dispnv50/core.h
+++ b/drm/nouveau/dispnv50/core.h
@@ -8,6 +8,14 @@ struct nv50_core {
 	struct nv50_dmac chan;
 };
 
+struct nv50_core_caps {
+	struct {
+		struct {
+			bool no_interlace;
+		} dp;
+	} sor[8];
+};
+
 int nv50_core_new(struct nouveau_drm *, struct nv50_core **);
 void nv50_core_del(struct nv50_core **);
 
@@ -17,6 +25,8 @@ struct nv50_core_func {
 	int (*ntfy_wait_done)(struct nouveau_bo *, u32 offset,
 			      struct nvif_device *);
 	void (*update)(struct nv50_core *, u32 *interlock, bool ntfy);
+	bool (*caps_fetch)(struct nv50_disp *);
+	bool (*caps_parse)(struct nv50_disp *, struct nv50_core_caps *);
 
 	const struct nv50_head_func *head;
 	const struct nv50_outp_func {
@@ -32,6 +42,7 @@ void core507d_init(struct nv50_core *);
 void core507d_ntfy_init(struct nouveau_bo *, u32);
 int core507d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *);
 void core507d_update(struct nv50_core *, u32 *, bool);
+bool core507d_caps_fetch(struct nv50_disp *);
 
 extern const struct nv50_outp_func dac507d;
 extern const struct nv50_outp_func sor507d;
@@ -42,6 +53,7 @@ int core827d_new(struct nouveau_drm *, s32, struct nv50_core
**);
 int core907d_new(struct nouveau_drm *, s32, struct nv50_core **);
 extern const struct nv50_outp_func dac907d;
 extern const struct nv50_outp_func sor907d;
+bool core907d_caps_parse(struct nv50_disp *, struct nv50_core_caps *);
 
 int core917d_new(struct nouveau_drm *, s32, struct nv50_core **);
 
diff --git a/drm/nouveau/dispnv50/core507d.c b/drm/nouveau/dispnv50/core507d.c
index e7fcfa6e..116b19db 100644
--- a/drm/nouveau/dispnv50/core507d.c
+++ b/drm/nouveau/dispnv50/core507d.c
@@ -43,6 +43,31 @@ core507d_update(struct nv50_core *core, u32 *interlock, bool
ntfy)
 	}
 }
 
+bool
+core507d_caps_fetch(struct nv50_disp *disp)
+{
+	struct nv50_core *core = disp->core;
+	u32 *push;
+	int i;
+
+	push = evo_wait(&core->chan, 6);
+	if (!push)
+		return false;
+
+	for (i = 0; i < 512; ++i)
+		nouveau_bo_wr32(disp->sync, i, 0);
+
+	evo_mthd(push, 0x0088, 1);
+	evo_data(push, core->chan.sync.handle);
+	evo_mthd(push, 0x0084, 1);
+	evo_data(push, 0xc0000000);
+	evo_mthd(push, 0x008c, 1);
+	evo_data(push, 0x00000000);
+	evo_kick(push, &core->chan);
+
+	return true;
+}
+
 int
 core507d_ntfy_wait_done(struct nouveau_bo *bo, u32 offset,
 			struct nvif_device *device)
diff --git a/drm/nouveau/dispnv50/core907d.c b/drm/nouveau/dispnv50/core907d.c
index ef822f81..854aa48e 100644
--- a/drm/nouveau/dispnv50/core907d.c
+++ b/drm/nouveau/dispnv50/core907d.c
@@ -22,12 +22,34 @@
 #include "core.h"
 #include "head.h"
 
+#include "nouveau_bo.h"
+
+bool
+core907d_caps_parse(struct nv50_disp *disp, struct nv50_core_caps *caps)
+{
+	struct nv50_core *core = disp->core;
+	int i;
+
+	if (core->func->ntfy_wait_done(disp->sync, 0x10,
+				       disp->core->chan.base.device))
+		return false;
+
+	for (i = 0; i < 8; ++i) {
+		uint32_t data = nouveau_bo_rd32(disp->sync, 0x14 + i * 2);
+		caps->sor[i].dp.no_interlace |= !(data & (1 << 26));
+	}
+
+	return true;
+}
+
 static const struct nv50_core_func
 core907d = {
 	.init = core507d_init,
 	.ntfy_init = core507d_ntfy_init,
 	.ntfy_wait_done = core507d_ntfy_wait_done,
 	.update = core507d_update,
+	.caps_fetch = core507d_caps_fetch,
+	.caps_parse = core907d_caps_parse,
 	.head = &head907d,
 	.dac = &dac907d,
 	.sor = &sor907d,
diff --git a/drm/nouveau/dispnv50/core917d.c b/drm/nouveau/dispnv50/core917d.c
index 392338df..5886c723 100644
--- a/drm/nouveau/dispnv50/core917d.c
+++ b/drm/nouveau/dispnv50/core917d.c
@@ -28,6 +28,8 @@ core917d = {
 	.ntfy_init = core507d_ntfy_init,
 	.ntfy_wait_done = core507d_ntfy_wait_done,
 	.update = core507d_update,
+	.caps_fetch = core507d_caps_fetch,
+	.caps_parse = core907d_caps_parse,
 	.head = &head917d,
 	.dac = &dac907d,
 	.sor = &sor907d,
diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c
index f04ab219..fa23d7a2 100644
--- a/drm/nouveau/dispnv50/disp.c
+++ b/drm/nouveau/dispnv50/disp.c
@@ -1409,7 +1409,8 @@ nv50_sor_func = {
 };
 
 static int
-nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
+nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe,
+                struct nv50_core_caps *caps)
 {
 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
 	struct nouveau_drm *drm = nouveau_drm(connector->dev);
@@ -1418,23 +1419,26 @@ nv50_sor_create(struct drm_connector *connector, struct
dcb_output *dcbe)
 	struct nouveau_encoder *nv_encoder;
 	struct drm_encoder *encoder;
 	u8 ver, hdr, cnt, len;
+	u8 or = ffs(dcbe->or) - 1;
 	u32 data;
 	int type, ret;
 
+	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
+	if (!nv_encoder)
+		return -ENOMEM;
+	nv_encoder->dcb = dcbe;
+	nv_encoder->update = nv50_sor_update;
+
 	switch (dcbe->type) {
 	case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break;
-	case DCB_OUTPUT_TMDS:
 	case DCB_OUTPUT_DP:
+		nv_encoder->dp.no_interlace = caps->sor[or].dp.no_interlace;
+	case DCB_OUTPUT_TMDS:
 	default:
 		type = DRM_MODE_ENCODER_TMDS;
 		break;
 	}
 
-	nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
-	if (!nv_encoder)
-		return -ENOMEM;
-	nv_encoder->dcb = dcbe;
-	nv_encoder->update = nv50_sor_update;
 
 	encoder = to_drm_encoder(nv_encoder);
 	encoder->possible_crtcs = dcbe->heads;
@@ -2160,6 +2164,7 @@ nv50_display_create(struct drm_device *dev)
 	struct drm_connector *connector, *tmp;
 	struct nv50_disp *disp;
 	struct dcb_output *dcbe;
+	struct nv50_core_caps caps = { 0 };
 	int crtcs, ret, i;
 
 	disp = kzalloc(sizeof(*disp), GFP_KERNEL);
@@ -2215,6 +2220,16 @@ nv50_display_create(struct drm_device *dev)
 			goto out;
 	}
 
+	/* fetch caps */
+	if (disp->core->func->caps_fetch &&
disp->core->func->caps_parse) {
+		if (!disp->core->func->caps_fetch(disp) ||
+		    !disp->core->func->caps_parse(disp, &caps)) {
+			ret = -EIO;
+			NV_ERROR(drm, "Failed to fetch display capabilities.\n");
+			goto out;
+		}
+	}
+
 	/* create encoder/connector objects based on VBIOS DCB table */
 	for (i = 0, dcbe = &dcb->entry[0]; i < dcb->entries; i++, dcbe++)
{
 		connector = nouveau_connector_create(dev, dcbe->connector);
@@ -2226,7 +2241,7 @@ nv50_display_create(struct drm_device *dev)
 			case DCB_OUTPUT_TMDS:
 			case DCB_OUTPUT_LVDS:
 			case DCB_OUTPUT_DP:
-				ret = nv50_sor_create(connector, dcbe);
+				ret = nv50_sor_create(connector, dcbe, &caps);
 				break;
 			case DCB_OUTPUT_ANALOG:
 				ret = nv50_dac_create(connector, dcbe);
diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c
index 5cc94944..074e6d52 100644
--- a/drm/nouveau/nouveau_connector.c
+++ b/drm/nouveau/nouveau_connector.c
@@ -1041,6 +1041,8 @@ nouveau_connector_mode_valid(struct drm_connector
*connector,
 	case DCB_OUTPUT_TV:
 		return get_slave_funcs(encoder)->mode_valid(encoder, mode);
 	case DCB_OUTPUT_DP:
+                if (mode->flags & DRM_MODE_FLAG_INTERLACE &&
nv_encoder->dp.no_interlace)
+                   return MODE_NO_INTERLACE;
 		max_clock  = nv_encoder->dp.link_nr;
 		max_clock *= nv_encoder->dp.link_bw;
 		clock = clock * (connector->display_info.bpc * 3) / 10;
diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h
index dfe095ad..f74af5ce 100644
--- a/drm/nouveau/nouveau_encoder.h
+++ b/drm/nouveau/nouveau_encoder.h
@@ -63,6 +63,7 @@ struct nouveau_encoder {
 		struct {
 			int link_nr;
 			int link_bw;
+			bool no_interlace;
 		} dp;
 	};
 
-- 
2.17.1
Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
 drm/nouveau/dispnv50/Kbuild     |  1 +
 drm/nouveau/dispnv50/core.c     |  6 ++---
 drm/nouveau/dispnv50/core.h     |  2 ++
 drm/nouveau/dispnv50/core957d.c | 42 +++++++++++++++++++++++++++++++++
 4 files changed, 48 insertions(+), 3 deletions(-)
 create mode 100644 drm/nouveau/dispnv50/core957d.c
diff --git a/drm/nouveau/dispnv50/Kbuild b/drm/nouveau/dispnv50/Kbuild
index 849b0f45..c3dee4d0 100644
--- a/drm/nouveau/dispnv50/Kbuild
+++ b/drm/nouveau/dispnv50/Kbuild
@@ -6,6 +6,7 @@ nouveau-y += dispnv50/core507d.o
 nouveau-y += dispnv50/core827d.o
 nouveau-y += dispnv50/core907d.o
 nouveau-y += dispnv50/core917d.o
+nouveau-y += dispnv50/core957d.o
 nouveau-y += dispnv50/corec37d.o
 
 nouveau-y += dispnv50/dac507d.o
diff --git a/drm/nouveau/dispnv50/core.c b/drm/nouveau/dispnv50/core.c
index f3c49adb..ba453afb 100644
--- a/drm/nouveau/dispnv50/core.c
+++ b/drm/nouveau/dispnv50/core.c
@@ -43,9 +43,9 @@ nv50_core_new(struct nouveau_drm *drm, struct nv50_core
**pcore)
 		int (*new)(struct nouveau_drm *, s32, struct nv50_core **);
 	} cores[] = {
 		{ GV100_DISP_CORE_CHANNEL_DMA, 0, corec37d_new },
-		{ GP102_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
-		{ GP100_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
-		{ GM200_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
+		{ GP102_DISP_CORE_CHANNEL_DMA, 0, core957d_new },
+		{ GP100_DISP_CORE_CHANNEL_DMA, 0, core957d_new },
+		{ GM200_DISP_CORE_CHANNEL_DMA, 0, core957d_new },
 		{ GM107_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
 		{ GK110_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
 		{ GK104_DISP_CORE_CHANNEL_DMA, 0, core917d_new },
diff --git a/drm/nouveau/dispnv50/core.h b/drm/nouveau/dispnv50/core.h
index 62ef0d24..4738447a 100644
--- a/drm/nouveau/dispnv50/core.h
+++ b/drm/nouveau/dispnv50/core.h
@@ -57,6 +57,8 @@ bool core907d_caps_parse(struct nv50_disp *, struct
nv50_core_caps *);
 
 int core917d_new(struct nouveau_drm *, s32, struct nv50_core **);
 
+int core957d_new(struct nouveau_drm *, s32, struct nv50_core **);
+
 int corec37d_new(struct nouveau_drm *, s32, struct nv50_core **);
 extern const struct nv50_outp_func sorc37d;
 #endif
diff --git a/drm/nouveau/dispnv50/core957d.c b/drm/nouveau/dispnv50/core957d.c
new file mode 100644
index 00000000..a31b1941
--- /dev/null
+++ b/drm/nouveau/dispnv50/core957d.c
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2018 Red Hat Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
"Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "core.h"
+#include "head.h"
+
+static const struct nv50_core_func
+core957d = {
+	.init = core507d_init,
+	.ntfy_init = core507d_ntfy_init,
+	.ntfy_wait_done = core507d_ntfy_wait_done,
+	.update = core507d_update,
+	.caps_fetch = core507d_caps_fetch,
+	.caps_parse = core907d_caps_parse,
+	.head = &head917d,
+	.dac = &dac907d,
+	.sor = &sor907d,
+};
+
+int
+core957d_new(struct nouveau_drm *drm, s32 oclass, struct nv50_core **pcore)
+{
+	return core507d_new_(&core957d, drm, oclass, pcore);
+}
-- 
2.17.1
Karol Herbst
2018-Aug-03  12:19 UTC
[Nouveau] [PATCH v3 4/6] kms/nv50: read out display max clocks
Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
 drm/nouveau/dispnv50/core.h     |  9 +++++++++
 drm/nouveau/dispnv50/core907d.c |  5 +++++
 drm/nouveau/dispnv50/core957d.c | 20 +++++++++++++++++++-
 3 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/drm/nouveau/dispnv50/core.h b/drm/nouveau/dispnv50/core.h
index 4738447a..4283d269 100644
--- a/drm/nouveau/dispnv50/core.h
+++ b/drm/nouveau/dispnv50/core.h
@@ -11,8 +11,17 @@ struct nv50_core {
 struct nv50_core_caps {
 	struct {
 		struct {
+			uint16_t max_mhz;
 			bool no_interlace;
 		} dp;
+
+		struct {
+			uint16_t max_mhz;
+		} lvds;
+
+		struct {
+			uint16_t max_mhz;
+		} tmds;
 	} sor[8];
 };
 
diff --git a/drm/nouveau/dispnv50/core907d.c b/drm/nouveau/dispnv50/core907d.c
index 854aa48e..79e5df31 100644
--- a/drm/nouveau/dispnv50/core907d.c
+++ b/drm/nouveau/dispnv50/core907d.c
@@ -37,6 +37,11 @@ core907d_caps_parse(struct nv50_disp *disp, struct
nv50_core_caps *caps)
 	for (i = 0; i < 8; ++i) {
 		uint32_t data = nouveau_bo_rd32(disp->sync, 0x14 + i * 2);
 		caps->sor[i].dp.no_interlace |= !(data & (1 << 26));
+
+		data = nouveau_bo_rd32(disp->sync, 0x15 + i * 2);
+		caps->sor[i].dp.max_mhz = (data & 0xff) * 10;
+		caps->sor[i].tmds.max_mhz = ((data >> 16) & 0xff) * 10;
+		caps->sor[i].lvds.max_mhz = caps->sor[i].tmds.max_mhz;
 	}
 
 	return true;
diff --git a/drm/nouveau/dispnv50/core957d.c b/drm/nouveau/dispnv50/core957d.c
index a31b1941..bd03eb6e 100644
--- a/drm/nouveau/dispnv50/core957d.c
+++ b/drm/nouveau/dispnv50/core957d.c
@@ -22,6 +22,24 @@
 #include "core.h"
 #include "head.h"
 
+#include "nouveau_bo.h"
+
+static bool
+core957d_caps_parse(struct nv50_disp *disp, struct nv50_core_caps *caps)
+{
+	int i;
+
+	if (!core907d_caps_parse(disp, caps))
+		return false;
+
+	for (i = 0; i < 8; ++i) {
+		uint32_t data = nouveau_bo_rd32(disp->sync, 0x15 + i * 2);
+		caps->sor[i].lvds.max_mhz = (data >> 24) * 10;
+	}
+
+	return true;
+}
+
 static const struct nv50_core_func
 core957d = {
 	.init = core507d_init,
@@ -29,7 +47,7 @@ core957d = {
 	.ntfy_wait_done = core507d_ntfy_wait_done,
 	.update = core507d_update,
 	.caps_fetch = core507d_caps_fetch,
-	.caps_parse = core907d_caps_parse,
+	.caps_parse = core957d_caps_parse,
 	.head = &head917d,
 	.dac = &dac907d,
 	.sor = &sor907d,
-- 
2.17.1
Karol Herbst
2018-Aug-03  12:19 UTC
[Nouveau] [PATCH v3 5/6] kms/nv50: detect HDMI max MHz correctly
v2: clean up left over comments
    don't overwrite hdmimhz parameter
    cap to 297MHz
Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
 drm/nouveau/dispnv50/disp.c     |  5 +++++
 drm/nouveau/nouveau_connector.c | 15 ++++++++++-----
 drm/nouveau/nouveau_encoder.h   |  4 ++++
 3 files changed, 19 insertions(+), 5 deletions(-)
diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c
index fa23d7a2..103433cb 100644
--- a/drm/nouveau/dispnv50/disp.c
+++ b/drm/nouveau/dispnv50/disp.c
@@ -1433,7 +1433,12 @@ nv50_sor_create(struct drm_connector *connector, struct
dcb_output *dcbe,
 	case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break;
 	case DCB_OUTPUT_DP:
 		nv_encoder->dp.no_interlace = caps->sor[or].dp.no_interlace;
+		type = DRM_MODE_ENCODER_TMDS;
+		break;
 	case DCB_OUTPUT_TMDS:
+		nv_encoder->tmds.max_mhz = caps->sor[or].tmds.max_mhz;
+		type = DRM_MODE_ENCODER_TMDS;
+		break;
 	default:
 		type = DRM_MODE_ENCODER_TMDS;
 		break;
diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c
index 074e6d52..65fac604 100644
--- a/drm/nouveau/nouveau_connector.c
+++ b/drm/nouveau/nouveau_connector.c
@@ -980,15 +980,20 @@ static unsigned
 get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi)
 {
 	struct nouveau_connector *nv_connector = nouveau_connector(connector);
+	struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
 	struct nouveau_drm *drm = nouveau_drm(connector->dev);
 	struct dcb_output *dcb = nv_connector->detected_encoder->dcb;
 
+	if (hdmi && nouveau_hdmimhz > 0)
+		return nouveau_hdmimhz * 1000;
+
+	if (nv_encoder->tmds.max_mhz)
+		/* no HDMI 2.0 for now and not quite clear if we can use
+                 * higher HDMI clocks than 297MHz
+                 */
+		return min(297, nv_encoder->tmds.max_mhz) * 1000;
+
 	if (hdmi) {
-		if (nouveau_hdmimhz > 0)
-			return nouveau_hdmimhz * 1000;
-		/* Note: these limits are conservative, some Fermi's
-		 * can do 297 MHz. Unclear how this can be determined.
-		 */
 		if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER)
 			return 297000;
 		if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI)
diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h
index f74af5ce..fbef9dc0 100644
--- a/drm/nouveau/nouveau_encoder.h
+++ b/drm/nouveau/nouveau_encoder.h
@@ -65,6 +65,10 @@ struct nouveau_encoder {
 			int link_bw;
 			bool no_interlace;
 		} dp;
+
+		struct {
+			uint16_t max_mhz;
+		} tmds;
 	};
 
 	void (*enc_save)(struct drm_encoder *encoder);
-- 
2.17.1
Karol Herbst
2018-Aug-03  12:19 UTC
[Nouveau] [PATCH v3 6/6] kms/nv50: detect LVDS max MHz correctly
Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
 drm/nouveau/dispnv50/disp.c     | 5 ++++-
 drm/nouveau/nouveau_connector.c | 5 ++++-
 drm/nouveau/nouveau_encoder.h   | 4 ++++
 3 files changed, 12 insertions(+), 2 deletions(-)
diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c
index 103433cb..53c97c7a 100644
--- a/drm/nouveau/dispnv50/disp.c
+++ b/drm/nouveau/dispnv50/disp.c
@@ -1430,7 +1430,10 @@ nv50_sor_create(struct drm_connector *connector, struct
dcb_output *dcbe,
 	nv_encoder->update = nv50_sor_update;
 
 	switch (dcbe->type) {
-	case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break;
+	case DCB_OUTPUT_LVDS:
+		nv_encoder->lvds.max_mhz = caps->sor[or].lvds.max_mhz;
+		type = DRM_MODE_ENCODER_LVDS;
+		break;
 	case DCB_OUTPUT_DP:
 		nv_encoder->dp.no_interlace = caps->sor[or].dp.no_interlace;
 		type = DRM_MODE_ENCODER_TMDS;
diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c
index 65fac604..027636b3 100644
--- a/drm/nouveau/nouveau_connector.c
+++ b/drm/nouveau/nouveau_connector.c
@@ -1029,7 +1029,10 @@ nouveau_connector_mode_valid(struct drm_connector
*connector,
 			return MODE_PANEL;
 
 		min_clock = 0;
-		max_clock = 400000;
+		if (nv_encoder->lvds.max_mhz)
+			max_clock = nv_encoder->lvds.max_mhz * 1000;
+		else
+			max_clock = 400000;
 		break;
 	case DCB_OUTPUT_TMDS:
 		hdmi = drm_detect_hdmi_monitor(nv_connector->edid);
diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h
index fbef9dc0..6961bdfc 100644
--- a/drm/nouveau/nouveau_encoder.h
+++ b/drm/nouveau/nouveau_encoder.h
@@ -66,6 +66,10 @@ struct nouveau_encoder {
 			bool no_interlace;
 		} dp;
 
+		struct {
+			uint16_t max_mhz;
+		} lvds;
+
 		struct {
 			uint16_t max_mhz;
 		} tmds;
-- 
2.17.1
Ilia Mirkin
2018-Aug-03  14:08 UTC
[Nouveau] [PATCH v3 5/6] kms/nv50: detect HDMI max MHz correctly
On Fri, Aug 3, 2018 at 8:19 AM, Karol Herbst <kherbst at redhat.com> wrote:> v2: clean up left over comments > don't overwrite hdmimhz parameter > cap to 297MHz > > Signed-off-by: Karol Herbst <kherbst at redhat.com> > --- > drm/nouveau/dispnv50/disp.c | 5 +++++ > drm/nouveau/nouveau_connector.c | 15 ++++++++++----- > drm/nouveau/nouveau_encoder.h | 4 ++++ > 3 files changed, 19 insertions(+), 5 deletions(-) > > diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c > index fa23d7a2..103433cb 100644 > --- a/drm/nouveau/dispnv50/disp.c > +++ b/drm/nouveau/dispnv50/disp.c > @@ -1433,7 +1433,12 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe, > case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; > case DCB_OUTPUT_DP: > nv_encoder->dp.no_interlace = caps->sor[or].dp.no_interlace; > + type = DRM_MODE_ENCODER_TMDS; > + break; > case DCB_OUTPUT_TMDS: > + nv_encoder->tmds.max_mhz = caps->sor[or].tmds.max_mhz; > + type = DRM_MODE_ENCODER_TMDS; > + break; > default: > type = DRM_MODE_ENCODER_TMDS; > break; > diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c > index 074e6d52..65fac604 100644 > --- a/drm/nouveau/nouveau_connector.c > +++ b/drm/nouveau/nouveau_connector.c > @@ -980,15 +980,20 @@ static unsigned > get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi) > { > struct nouveau_connector *nv_connector = nouveau_connector(connector); > + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; > struct nouveau_drm *drm = nouveau_drm(connector->dev); > struct dcb_output *dcb = nv_connector->detected_encoder->dcb; > > + if (hdmi && nouveau_hdmimhz > 0) > + return nouveau_hdmimhz * 1000; > + > + if (nv_encoder->tmds.max_mhz) > + /* no HDMI 2.0 for now and not quite clear if we can use > + * higher HDMI clocks than 297MHz > + */ > + return min(297, nv_encoder->tmds.max_mhz) * 1000;So ... will you start reporting 297MHz single-link TMDS bandwidth over DVI now? What values show up in max_mhz in practice, across the boards you've tested (ideally a per-generation summary would be nice).> + > if (hdmi) { > - if (nouveau_hdmimhz > 0) > - return nouveau_hdmimhz * 1000; > - /* Note: these limits are conservative, some Fermi's > - * can do 297 MHz. Unclear how this can be determined. > - */ > if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER) > return 297000; > if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) > diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h > index f74af5ce..fbef9dc0 100644 > --- a/drm/nouveau/nouveau_encoder.h > +++ b/drm/nouveau/nouveau_encoder.h > @@ -65,6 +65,10 @@ struct nouveau_encoder { > int link_bw; > bool no_interlace; > } dp; > + > + struct { > + uint16_t max_mhz; > + } tmds; > }; > > void (*enc_save)(struct drm_encoder *encoder); > -- > 2.17.1 > > _______________________________________________ > Nouveau mailing list > Nouveau at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau