[Nouveau] [PATCH] kms/nv50: reject interlaced modes if the hardware doesn't support it
Karol Herbst
kherbst at redhat.com
Thu Jul 19 00:08:53 UTC 2018
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.
Signed-off-by: Karol Herbst <kherbst at redhat.com>
---
drm/nouveau/dispnv50/core.h | 10 ++++++++++
drm/nouveau/dispnv50/core507d.c | 25 +++++++++++++++++++++++++
drm/nouveau/dispnv50/core907d.c | 23 +++++++++++++++++++++++
drm/nouveau/dispnv50/core917d.c | 2 ++
drm/nouveau/dispnv50/disp.c | 17 +++++++++++++++--
drm/nouveau/nouveau_connector.c | 2 ++
drm/nouveau/nouveau_encoder.h | 1 +
7 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/drm/nouveau/dispnv50/core.h b/drm/nouveau/dispnv50/core.h
index 8470df9d..5ff79c89 100644
--- a/drm/nouveau/dispnv50/core.h
+++ b/drm/nouveau/dispnv50/core.h
@@ -8,6 +8,12 @@ struct nv50_core {
struct nv50_dmac chan;
};
+struct nv50_core_caps {
+ struct {
+ bool no_interlace;
+ } dp;
+};
+
int nv50_core_new(struct nouveau_drm *, struct nv50_core **);
void nv50_core_del(struct nv50_core **);
@@ -17,6 +23,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 +40,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 +51,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..2e1c2fe6 100644
--- a/drm/nouveau/dispnv50/core907d.c
+++ b/drm/nouveau/dispnv50/core907d.c
@@ -22,12 +22,35 @@
#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;
+ caps->dp.no_interlace = false;
+
+ if (core->func->ntfy_wait_done(disp->sync, 0x10,
+ disp->core->chan.base.device))
+ return false;
+
+ for (i = 0x14; i < 0x24; i += 2) {
+ uint32_t data = nouveau_bo_rd32(disp->sync, i);
+ caps->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 1f8bba8f..970dddf6 100644
--- a/drm/nouveau/dispnv50/disp.c
+++ b/drm/nouveau/dispnv50/disp.c
@@ -1384,7 +1384,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);
@@ -1410,6 +1411,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe)
return -ENOMEM;
nv_encoder->dcb = dcbe;
nv_encoder->update = nv50_sor_update;
+ nv_encoder->dp.no_interlace = caps->dp.no_interlace;
encoder = to_drm_encoder(nv_encoder);
encoder->possible_crtcs = dcbe->heads;
@@ -2132,6 +2134,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);
@@ -2189,6 +2192,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);
@@ -2200,7 +2213,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 7b557c35..b0e1f617 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 3517f920..a9e55096 100644
--- a/drm/nouveau/nouveau_encoder.h
+++ b/drm/nouveau/nouveau_encoder.h
@@ -63,6 +63,7 @@ struct nouveau_encoder {
struct nv50_mstm *mstm;
int link_nr;
int link_bw;
+ bool no_interlace;
} dp;
};
--
2.17.1
More information about the Nouveau
mailing list