Karol Herbst
2018-Jul-19 00:08 UTC
[Nouveau] [PATCH] 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. 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
Reasonably Related Threads
- [PATCH 0/6] improve feature detection
- [PATCH v3 0/6] improve feature detection
- [PATCH v4] drm/nouveau/kms/nv50-: Program notifier offset before requesting disp caps
- [PATCH v5 1/2] drm/nouveau/kms/nv50-: Program notifier offset before requesting disp caps
- [PATCH v3] drm/nouveau/kms/nv50-: Program notifier offset before requesting disp caps