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