[Nouveau] [PATCH v3 2/6] kms/nv50: reject interlaced modes if the hardware doesn't support it
Karol Herbst
kherbst at redhat.com
Fri Aug 3 12:19:35 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.
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
More information about the Nouveau
mailing list