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 <[email protected]>
---
 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

_______________________________________________
Nouveau mailing list
[email protected]
https://lists.freedesktop.org/mailman/listinfo/nouveau

Reply via email to