Currently, nouveau doesn't actually bother to try probing whether or not it can actually handle interlaced modes over DisplayPort. As a result, on volta and later we'll end up trying to set an interlaced mode even when it's not supported and cause the front end for the display engine to hang. So, let's teach nouveau to reject interlaced modes on hardware that can't actually handle it. Additionally for MST, since we accomplish this by simply reusing more of the SST mode validation we also get (some) basic bw validation for modes we detect on MST connectors completely for free. Lyude Paul (5): drm/nouveau/kms/nv50-: Initialize core channel in nouveau_display_create() drm/nouveau/kms/nv50-: Probe SOR and PIOR caps for DP interlacing support drm/nouveau/kms/gv100-: Add support for interlaced modes drm/nouveau/kms/nv50-: Move 8BPC limit for MST into nv50_mstc_get_modes() drm/nouveau/kms/nv50-: Share DP SST mode_valid() handling with MST drivers/gpu/drm/nouveau/dispnv50/core.h | 7 +++ drivers/gpu/drm/nouveau/dispnv50/core507d.c | 15 ++++++ drivers/gpu/drm/nouveau/dispnv50/core827d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/core907d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/core917d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/corec37d.c | 26 +++++++++ drivers/gpu/drm/nouveau/dispnv50/corec57d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/disp.c | 58 +++++++++++++++------ drivers/gpu/drm/nouveau/dispnv50/disp.h | 1 + drivers/gpu/drm/nouveau/dispnv50/headc37d.c | 5 +- drivers/gpu/drm/nouveau/dispnv50/headc57d.c | 5 +- drivers/gpu/drm/nouveau/dispnv50/pior507d.c | 8 +++ drivers/gpu/drm/nouveau/dispnv50/sor507d.c | 7 +++ drivers/gpu/drm/nouveau/dispnv50/sor907d.c | 11 ++++ drivers/gpu/drm/nouveau/dispnv50/sorc37d.c | 9 ++++ drivers/gpu/drm/nouveau/nouveau_connector.c | 48 +++++++++++------ drivers/gpu/drm/nouveau/nouveau_connector.h | 5 ++ drivers/gpu/drm/nouveau/nouveau_dp.c | 31 +++++++++++ drivers/gpu/drm/nouveau/nouveau_encoder.h | 8 +++ 19 files changed, 213 insertions(+), 35 deletions(-) -- 2.26.2
Lyude Paul
2020-May-11 22:41 UTC
[Nouveau] [PATCH v3 1/5] drm/nouveau/kms/nv50-: Initialize core channel in nouveau_display_create()
We'll need the core channel initialized and ready by the time that we start creating modesetting objects, so that we can call the NV507D_GET_CAPABILITIES method to make the hardware expose it's modesetting capabilities for later probing. So, when loading the driver prepare the core channel from within nouveau_display_create(). Everywhere else, we initialize the core channel during resume. Signed-off-by: Lyude Paul <lyude at redhat.com> --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 6be9df1820c5..05b458b16e3f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -2369,7 +2369,8 @@ nv50_display_init(struct drm_device *dev, bool resume, bool runtime) struct drm_encoder *encoder; struct drm_plane *plane; - core->func->init(core); + if (resume || runtime) + core->func->init(core); list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { @@ -2456,6 +2457,8 @@ nv50_display_create(struct drm_device *dev) if (ret) goto out; + disp->core->func->init(disp->core); + /* create crtc objects to represent the hw heads */ if (disp->disp->object.oclass >= GV100_DISP) crtcs = nvif_rd32(&device->object, 0x610060) & 0xff; -- 2.26.2
Lyude Paul
2020-May-11 22:41 UTC
[Nouveau] [PATCH v3 2/5] drm/nouveau/kms/nv50-: Probe SOR and PIOR caps for DP interlacing support
Right now, we make the mistake of allowing interlacing on all connectors. Nvidia hardware does not always support interlacing with DP though, so we need to make sure that we don't allow interlaced modes to be set in such situations as otherwise we'll end up accidentally hanging the display HW. This fixes some hangs with Turing, which would be caused by attempting to set an interlaced mode on hardware that doesn't support it. This patch likely fixes other hardware hanging in the same way as well. Note that we say we probe PIOR caps, but they don't actually have any interlacing caps. So, the get_caps() function for PIORs just sets interlacing support to true. Changes since v1: * Actually probe caps correctly this time, both on EVO and NVDisplay. Changes since v2: * Fix probing for < GF119 * Use vfunc table, in prep for adding more caps in the future. Signed-off-by: Lyude Paul <lyude at redhat.com> --- drivers/gpu/drm/nouveau/dispnv50/core.h | 7 ++++++ drivers/gpu/drm/nouveau/dispnv50/core507d.c | 15 ++++++++++++ drivers/gpu/drm/nouveau/dispnv50/core827d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/core907d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/core917d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/corec37d.c | 26 +++++++++++++++++++++ drivers/gpu/drm/nouveau/dispnv50/corec57d.c | 1 + drivers/gpu/drm/nouveau/dispnv50/disp.c | 19 +++++++++++++-- drivers/gpu/drm/nouveau/dispnv50/disp.h | 1 + drivers/gpu/drm/nouveau/dispnv50/pior507d.c | 8 +++++++ drivers/gpu/drm/nouveau/dispnv50/sor507d.c | 7 ++++++ drivers/gpu/drm/nouveau/dispnv50/sor907d.c | 11 +++++++++ drivers/gpu/drm/nouveau/dispnv50/sorc37d.c | 9 +++++++ drivers/gpu/drm/nouveau/nouveau_connector.c | 10 +++++++- drivers/gpu/drm/nouveau/nouveau_encoder.h | 4 ++++ 15 files changed, 118 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/core.h b/drivers/gpu/drm/nouveau/dispnv50/core.h index ff94f3f6f264..99157dc94d23 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core.h +++ b/drivers/gpu/drm/nouveau/dispnv50/core.h @@ -2,6 +2,7 @@ #define __NV50_KMS_CORE_H__ #include "disp.h" #include "atom.h" +#include <nouveau_encoder.h> struct nv50_core { const struct nv50_core_func *func; @@ -15,6 +16,7 @@ void nv50_core_del(struct nv50_core **); struct nv50_core_func { void (*init)(struct nv50_core *); void (*ntfy_init)(struct nouveau_bo *, u32 offset); + int (*caps_init)(struct nouveau_drm *, struct nv50_disp *); int (*ntfy_wait_done)(struct nouveau_bo *, u32 offset, struct nvif_device *); void (*update)(struct nv50_core *, u32 *interlock, bool ntfy); @@ -27,6 +29,9 @@ struct nv50_core_func { const struct nv50_outp_func { void (*ctrl)(struct nv50_core *, int or, u32 ctrl, struct nv50_head_atom *); + /* XXX: Only used by SORs and PIORs for now */ + void (*get_caps)(struct nv50_disp *, + struct nouveau_encoder *, int or); } *dac, *pior, *sor; }; @@ -35,6 +40,7 @@ int core507d_new_(const struct nv50_core_func *, struct nouveau_drm *, s32, struct nv50_core **); void core507d_init(struct nv50_core *); void core507d_ntfy_init(struct nouveau_bo *, u32); +int core507d_caps_init(struct nouveau_drm *, struct nv50_disp *); int core507d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *); void core507d_update(struct nv50_core *, u32 *, bool); @@ -51,6 +57,7 @@ extern const struct nv50_outp_func sor907d; int core917d_new(struct nouveau_drm *, s32, struct nv50_core **); int corec37d_new(struct nouveau_drm *, s32, struct nv50_core **); +int corec37d_caps_init(struct nouveau_drm *, struct nv50_disp *); int corec37d_ntfy_wait_done(struct nouveau_bo *, u32, struct nvif_device *); void corec37d_update(struct nv50_core *, u32 *, bool); void corec37d_wndw_owner(struct nv50_core *); diff --git a/drivers/gpu/drm/nouveau/dispnv50/core507d.c b/drivers/gpu/drm/nouveau/dispnv50/core507d.c index c5152c39c684..e341f572c269 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core507d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core507d.c @@ -62,6 +62,20 @@ core507d_ntfy_init(struct nouveau_bo *bo, u32 offset) nouveau_bo_wr32(bo, offset / 4, 0x00000000); } +int +core507d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +{ + u32 *push = evo_wait(&disp->core->chan, 2); + + if (push) { + evo_mthd(push, 0x008c, 1); + evo_data(push, 0x0); + evo_kick(push, &disp->core->chan); + } + + return 0; +} + void core507d_init(struct nv50_core *core) { @@ -77,6 +91,7 @@ static const struct nv50_core_func core507d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, + .caps_init = core507d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head507d, diff --git a/drivers/gpu/drm/nouveau/dispnv50/core827d.c b/drivers/gpu/drm/nouveau/dispnv50/core827d.c index 6123a068f836..2e0c1c536afe 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core827d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core827d.c @@ -26,6 +26,7 @@ static const struct nv50_core_func core827d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, + .caps_init = core507d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head827d, diff --git a/drivers/gpu/drm/nouveau/dispnv50/core907d.c b/drivers/gpu/drm/nouveau/dispnv50/core907d.c index ef822f813435..271629832629 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core907d.c @@ -26,6 +26,7 @@ static const struct nv50_core_func core907d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, + .caps_init = core507d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head907d, diff --git a/drivers/gpu/drm/nouveau/dispnv50/core917d.c b/drivers/gpu/drm/nouveau/dispnv50/core917d.c index 392338df5bfd..5cc072d4c30f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/core917d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/core917d.c @@ -26,6 +26,7 @@ static const struct nv50_core_func core917d = { .init = core507d_init, .ntfy_init = core507d_ntfy_init, + .caps_init = core507d_caps_init, .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .head = &head917d, diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c index c03cb987856b..e0c8811fb8e4 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec37d.c @@ -22,6 +22,7 @@ #include "core.h" #include "head.h" +#include <nvif/class.h> #include <nouveau_bo.h> #include <nvif/timer.h> @@ -87,6 +88,30 @@ corec37d_ntfy_init(struct nouveau_bo *bo, u32 offset) nouveau_bo_wr32(bo, offset / 4 + 3, 0x00000000); } +int corec37d_caps_init(struct nouveau_drm *drm, struct nv50_disp *disp) +{ + int ret; + + ret = nvif_object_init(&disp->disp->object, 0, GV100_DISP_CAPS, + NULL, 0, &disp->caps); + if (ret) { + NV_ERROR(drm, + "Failed to init notifier caps region: %d\n", + ret); + return ret; + } + + ret = nvif_object_map(&disp->caps, NULL, 0); + if (ret) { + NV_ERROR(drm, + "Failed to map notifier caps region: %d\n", + ret); + return ret; + } + + return 0; +} + static void corec37d_init(struct nv50_core *core) { @@ -111,6 +136,7 @@ static const struct nv50_core_func corec37d = { .init = corec37d_init, .ntfy_init = corec37d_ntfy_init, + .caps_init = corec37d_caps_init, .ntfy_wait_done = corec37d_ntfy_wait_done, .update = corec37d_update, .wndw.owner = corec37d_wndw_owner, diff --git a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c index 147adcd60937..10ba9e9e4ae6 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/corec57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/corec57d.c @@ -46,6 +46,7 @@ static const struct nv50_core_func corec57d = { .init = corec57d_init, .ntfy_init = corec37d_ntfy_init, + .caps_init = corec37d_caps_init, .ntfy_wait_done = corec37d_ntfy_wait_done, .update = corec37d_update, .wndw.owner = corec37d_wndw_owner, diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index 05b458b16e3f..f67dffe8c005 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1659,6 +1659,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nouveau_encoder *nv_encoder; struct drm_encoder *encoder; + struct nv50_disp *disp = nv50_disp(connector->dev); int type, ret; switch (dcbe->type) { @@ -1685,10 +1686,12 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) drm_connector_attach_encoder(connector, encoder); + disp->core->func->sor->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); + if (dcbe->type == DCB_OUTPUT_DP) { - struct nv50_disp *disp = nv50_disp(encoder->dev); struct nvkm_i2c_aux *aux nvkm_i2c_aux_find(i2c, dcbe->i2c_index); + if (aux) { if (disp->disp->object.oclass < GF110_DISP) { /* HW has no support for address-only @@ -1801,7 +1804,9 @@ nv50_pior_func = { static int nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) { - struct nouveau_drm *drm = nouveau_drm(connector->dev); + struct drm_device *dev = connector->dev; + struct nouveau_drm *drm = nouveau_drm(dev); + struct nv50_disp *disp = nv50_disp(dev); struct nvkm_i2c *i2c = nvxx_i2c(&drm->client.device); struct nvkm_i2c_bus *bus = NULL; struct nvkm_i2c_aux *aux = NULL; @@ -1840,6 +1845,9 @@ nv50_pior_create(struct drm_connector *connector, struct dcb_output *dcbe) drm_encoder_helper_add(encoder, &nv50_pior_help); drm_connector_attach_encoder(connector, encoder); + + disp->core->func->pior->get_caps(disp, nv_encoder, ffs(dcbe->or) - 1); + return 0; } @@ -2397,6 +2405,8 @@ nv50_display_destroy(struct drm_device *dev) nv50_audio_component_fini(nouveau_drm(dev)); + nvif_object_unmap(&disp->caps); + nvif_object_fini(&disp->caps); nv50_core_del(&disp->core); nouveau_bo_unmap(disp->sync); @@ -2458,6 +2468,11 @@ nv50_display_create(struct drm_device *dev) goto out; disp->core->func->init(disp->core); + if (disp->core->func->caps_init) { + ret = disp->core->func->caps_init(drm, disp); + if (ret) + goto out; + } /* create crtc objects to represent the hw heads */ if (disp->disp->object.oclass >= GV100_DISP) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.h b/drivers/gpu/drm/nouveau/dispnv50/disp.h index d54fe00ac3a3..89c3b38c32a5 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.h +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.h @@ -9,6 +9,7 @@ struct nv50_msto; struct nv50_disp { struct nvif_disp *disp; struct nv50_core *core; + struct nvif_object caps; #define NV50_DISP_SYNC(c, o) ((c) * 0x040 + (o)) #define NV50_DISP_CORE_NTFY NV50_DISP_SYNC(0 , 0x00) diff --git a/drivers/gpu/drm/nouveau/dispnv50/pior507d.c b/drivers/gpu/drm/nouveau/dispnv50/pior507d.c index d2bac6a341dc..45d8ce7d2c28 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/pior507d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/pior507d.c @@ -38,7 +38,15 @@ pior507d_ctrl(struct nv50_core *core, int or, u32 ctrl, } } +static void +pior507d_get_caps(struct nv50_disp *disp, struct nouveau_encoder *outp, + int or) +{ + outp->caps.dp_interlace = true; +} + const struct nv50_outp_func pior507d = { .ctrl = pior507d_ctrl, + .get_caps = pior507d_get_caps, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/sor507d.c b/drivers/gpu/drm/nouveau/dispnv50/sor507d.c index 5222fe6a9b21..9a59fa7da00d 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/sor507d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/sor507d.c @@ -38,7 +38,14 @@ sor507d_ctrl(struct nv50_core *core, int or, u32 ctrl, } } +static void +sor507d_get_caps(struct nv50_disp *core, struct nouveau_encoder *outp, int or) +{ + outp->caps.dp_interlace = true; +} + const struct nv50_outp_func sor507d = { .ctrl = sor507d_ctrl, + .get_caps = sor507d_get_caps, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/sor907d.c b/drivers/gpu/drm/nouveau/dispnv50/sor907d.c index b0314ec11fb3..9577ccf1c809 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/sor907d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/sor907d.c @@ -21,6 +21,7 @@ */ #include "core.h" +#include <nouveau_bo.h> #include <nvif/class.h> static void @@ -35,7 +36,17 @@ sor907d_ctrl(struct nv50_core *core, int or, u32 ctrl, } } +static void +sor907d_get_caps(struct nv50_disp *disp, struct nouveau_encoder *outp, int or) +{ + const int off = or * 2; + u32 tmp = nouveau_bo_rd32(disp->sync, 0x000014 + off); + + outp->caps.dp_interlace = !!(tmp & 0x04000000); +} + const struct nv50_outp_func sor907d = { .ctrl = sor907d_ctrl, + .get_caps = sor907d_get_caps, }; diff --git a/drivers/gpu/drm/nouveau/dispnv50/sorc37d.c b/drivers/gpu/drm/nouveau/dispnv50/sorc37d.c index dff059241c5d..c86ca955fdcd 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/sorc37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/sorc37d.c @@ -33,7 +33,16 @@ sorc37d_ctrl(struct nv50_core *core, int or, u32 ctrl, } } +static void +sorc37d_get_caps(struct nv50_disp *disp, struct nouveau_encoder *outp, int or) +{ + u32 tmp = nvif_rd32(&disp->caps, 0x000144 + (or * 8)); + + outp->caps.dp_interlace = !!(tmp & 0x04000000); +} + const struct nv50_outp_func sorc37d = { .ctrl = sorc37d_ctrl, + .get_caps = sorc37d_get_caps, }; diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 9a9a7f5003d3..43bcbb6d73c4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -509,7 +509,11 @@ nouveau_connector_set_encoder(struct drm_connector *connector, nv_connector->detected_encoder = nv_encoder; if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_TESLA) { - connector->interlace_allowed = true; + if (nv_encoder->dcb->type == DCB_OUTPUT_DP) + connector->interlace_allowed + nv_encoder->caps.dp_interlace; + else + connector->interlace_allowed = true; connector->doublescan_allowed = true; } else if (nv_encoder->dcb->type == DCB_OUTPUT_LVDS || @@ -1060,6 +1064,10 @@ 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.caps.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/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 3517f920bf89..3217f587eceb 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -66,6 +66,10 @@ struct nouveau_encoder { } dp; }; + struct { + bool dp_interlace : 1; + } caps; + void (*enc_save)(struct drm_encoder *encoder); void (*enc_restore)(struct drm_encoder *encoder); void (*update)(struct nouveau_encoder *, u8 head, -- 2.26.2
Lyude Paul
2020-May-11 22:41 UTC
[Nouveau] [PATCH v3 3/5] drm/nouveau/kms/gv100-: Add support for interlaced modes
We advertise being able to set interlaced modes, so let's actually make sure to do that. Otherwise, we'll end up hanging the display engine due to trying to set a mode with timings adjusted for interlacing without telling the hardware it's actually an interlaced mode. Signed-off-by: Lyude Paul <lyude at redhat.com> --- drivers/gpu/drm/nouveau/dispnv50/headc37d.c | 5 +++-- drivers/gpu/drm/nouveau/dispnv50/headc57d.c | 5 +++-- drivers/gpu/drm/nouveau/nouveau_connector.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c index 00011ce109a6..4a9a32b89f74 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc37d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc37d.c @@ -168,14 +168,15 @@ headc37d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; struct nv50_head_mode *m = &asyh->mode; u32 *push; - if ((push = evo_wait(core, 12))) { + if ((push = evo_wait(core, 13))) { evo_mthd(push, 0x2064 + (head->base.index * 0x400), 5); evo_data(push, (m->v.active << 16) | m->h.active ); evo_data(push, (m->v.synce << 16) | m->h.synce ); evo_data(push, (m->v.blanke << 16) | m->h.blanke ); evo_data(push, (m->v.blanks << 16) | m->h.blanks ); evo_data(push, (m->v.blank2e << 16) | m->v.blank2s); - evo_mthd(push, 0x200c + (head->base.index * 0x400), 1); + evo_mthd(push, 0x2008 + (head->base.index * 0x400), 2); + evo_data(push, m->interlace); evo_data(push, m->clock * 1000); evo_mthd(push, 0x2028 + (head->base.index * 0x400), 1); evo_data(push, m->clock * 1000); diff --git a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c index 938d910a1b1e..859131a8bc3c 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/headc57d.c +++ b/drivers/gpu/drm/nouveau/dispnv50/headc57d.c @@ -173,14 +173,15 @@ headc57d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; struct nv50_head_mode *m = &asyh->mode; u32 *push; - if ((push = evo_wait(core, 12))) { + if ((push = evo_wait(core, 13))) { evo_mthd(push, 0x2064 + (head->base.index * 0x400), 5); evo_data(push, (m->v.active << 16) | m->h.active ); evo_data(push, (m->v.synce << 16) | m->h.synce ); evo_data(push, (m->v.blanke << 16) | m->h.blanke ); evo_data(push, (m->v.blanks << 16) | m->h.blanks ); evo_data(push, (m->v.blank2e << 16) | m->v.blank2s); - evo_mthd(push, 0x200c + (head->base.index * 0x400), 1); + evo_mthd(push, 0x2008 + (head->base.index * 0x400), 2); + evo_data(push, m->interlace); evo_data(push, m->clock * 1000); evo_mthd(push, 0x2028 + (head->base.index * 0x400), 1); evo_data(push, m->clock * 1000); diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 43bcbb6d73c4..6dae00da5d7e 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -1065,7 +1065,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return get_slave_funcs(encoder)->mode_valid(encoder, mode); case DCB_OUTPUT_DP: if (mode->flags & DRM_MODE_FLAG_INTERLACE && - !nv_encoder->dp.caps.interlace) + !nv_encoder->caps.dp_interlace) return MODE_NO_INTERLACE; max_clock = nv_encoder->dp.link_nr; -- 2.26.2
Lyude Paul
2020-May-11 22:41 UTC
[Nouveau] [PATCH v3 4/5] drm/nouveau/kms/nv50-: Move 8BPC limit for MST into nv50_mstc_get_modes()
This just limits the BPC for MST connectors to a maximum of 8 from nv50_mstc_get_modes(), instead of doing so during nv50_msto_atomic_check(). This doesn't introduce any functional changes yet (other then userspace now lying about the max bpc, but we can't support that yet anyway so meh). But, we'll need this in a moment so that we can share mode validation between SST and MST which will fix some real world issues. Signed-off-by: Lyude Paul <lyude at redhat.com> --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index f67dffe8c005..c49a6c47c66f 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -904,15 +904,9 @@ nv50_msto_atomic_check(struct drm_encoder *encoder, if (!state->duplicated) { const int clock = crtc_state->adjusted_mode.clock; - /* - * XXX: Since we don't use HDR in userspace quite yet, limit - * the bpc to 8 to save bandwidth on the topology. In the - * future, we'll want to properly fix this by dynamically - * selecting the highest possible bpc that would fit in the - * topology - */ - asyh->or.bpc = min(connector->display_info.bpc, 8U); - asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3, false); + asyh->or.bpc = connector->display_info.bpc; + asyh->dp.pbn = drm_dp_calc_pbn_mode(clock, asyh->or.bpc * 3, + false); } slots = drm_dp_atomic_find_vcpi_slots(state, &mstm->mgr, mstc->port, @@ -1072,8 +1066,17 @@ nv50_mstc_get_modes(struct drm_connector *connector) if (mstc->edid) ret = drm_add_edid_modes(&mstc->connector, mstc->edid); - if (!mstc->connector.display_info.bpc) - mstc->connector.display_info.bpc = 8; + /* + * XXX: Since we don't use HDR in userspace quite yet, limit the bpc + * to 8 to save bandwidth on the topology. In the future, we'll want + * to properly fix this by dynamically selecting the highest possible + * bpc that would fit in the topology + */ + if (connector->display_info.bpc) + connector->display_info.bpc + clamp(connector->display_info.bpc, 6U, 8U); + else + connector->display_info.bpc = 8; if (mstc->native) drm_mode_destroy(mstc->connector.dev, mstc->native); -- 2.26.2
Lyude Paul
2020-May-11 22:41 UTC
[Nouveau] [PATCH v3 5/5] drm/nouveau/kms/nv50-: Share DP SST mode_valid() handling with MST
Currently, the nv50_mstc_mode_valid() function is happy to take any and all modes, even the ones we can't actually support sometimes like interlaced modes. Luckily, the only difference between the mode validation that needs to be performed for MST vs. SST is that eventually we'll need to check the minimum PBN against the MSTB's full PBN capabilities (remember-we don't care about the current bw state here). Otherwise, all of the other code can be shared. So, we move all of the common mode validation in nouveau_connector_mode_valid() into a separate helper, nv50_dp_mode_valid(), and use that from both nv50_mstc_mode_valid() and nouveau_connector_mode_valid(). Note that we allow for returning the calculated clock that nv50_dp_mode_valid() came up with, since we'll eventually want to use that for PBN calculation in nv50_mstc_mode_valid(). Signed-off-by: Lyude Paul <lyude at redhat.com> --- drivers/gpu/drm/nouveau/dispnv50/disp.c | 9 +++- drivers/gpu/drm/nouveau/nouveau_connector.c | 46 ++++++++++++--------- drivers/gpu/drm/nouveau/nouveau_connector.h | 5 +++ drivers/gpu/drm/nouveau/nouveau_dp.c | 31 ++++++++++++++ drivers/gpu/drm/nouveau/nouveau_encoder.h | 4 ++ 5 files changed, 75 insertions(+), 20 deletions(-) diff --git a/drivers/gpu/drm/nouveau/dispnv50/disp.c b/drivers/gpu/drm/nouveau/dispnv50/disp.c index c49a6c47c66f..f3a77267ff09 100644 --- a/drivers/gpu/drm/nouveau/dispnv50/disp.c +++ b/drivers/gpu/drm/nouveau/dispnv50/disp.c @@ -1052,7 +1052,14 @@ static enum drm_mode_status nv50_mstc_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { - return MODE_OK; + struct nv50_mstc *mstc = nv50_mstc(connector); + struct nouveau_encoder *outp = mstc->mstm->outp; + + /* TODO: calculate the PBN from the dotclock and validate against the + * MSTB's max possible PBN + */ + + return nv50_dp_mode_valid(connector, outp, mode, NULL); } static int diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c index 6dae00da5d7e..1b383ae0248f 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.c +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c @@ -38,6 +38,7 @@ #include "nouveau_reg.h" #include "nouveau_drv.h" #include "dispnv04/hw.h" +#include "dispnv50/disp.h" #include "nouveau_acpi.h" #include "nouveau_display.h" @@ -1033,6 +1034,29 @@ get_tmds_link_bandwidth(struct drm_connector *connector) return 112000 * duallink_scale; } +enum drm_mode_status +nouveau_conn_mode_clock_valid(const struct drm_display_mode *mode, + const unsigned min_clock, + const unsigned max_clock, + unsigned int *clock_out) +{ + unsigned int clock = mode->clock; + + if ((mode->flags & DRM_MODE_FLAG_3D_MASK) =+ DRM_MODE_FLAG_3D_FRAME_PACKING) + clock *= 2; + + if (clock < min_clock) + return MODE_CLOCK_LOW; + if (clock > max_clock) + return MODE_CLOCK_HIGH; + + if (clock_out) + *clock_out = clock; + + return MODE_OK; +} + static enum drm_mode_status nouveau_connector_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) @@ -1041,7 +1065,6 @@ nouveau_connector_mode_valid(struct drm_connector *connector, struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct drm_encoder *encoder = to_drm_encoder(nv_encoder); unsigned min_clock = 25000, max_clock = min_clock; - unsigned clock = mode->clock; switch (nv_encoder->dcb->type) { case DCB_OUTPUT_LVDS: @@ -1064,29 +1087,14 @@ 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->caps.dp_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; - break; + return nv50_dp_mode_valid(connector, nv_encoder, mode, NULL); default: BUG(); return MODE_BAD; } - if ((mode->flags & DRM_MODE_FLAG_3D_MASK) == DRM_MODE_FLAG_3D_FRAME_PACKING) - clock *= 2; - - if (clock < min_clock) - return MODE_CLOCK_LOW; - - if (clock > max_clock) - return MODE_CLOCK_HIGH; - - return MODE_OK; + return nouveau_conn_mode_clock_valid(mode, min_clock, max_clock, + NULL); } static struct drm_encoder * diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h index de84fb4708c7..9e062c7adec8 100644 --- a/drivers/gpu/drm/nouveau/nouveau_connector.h +++ b/drivers/gpu/drm/nouveau/nouveau_connector.h @@ -195,6 +195,11 @@ int nouveau_conn_atomic_get_property(struct drm_connector *, const struct drm_connector_state *, struct drm_property *, u64 *); struct drm_display_mode *nouveau_conn_native_mode(struct drm_connector *); +enum drm_mode_status +nouveau_conn_mode_clock_valid(const struct drm_display_mode *, + const unsigned min_clock, + const unsigned max_clock, + unsigned *clock); #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT extern int nouveau_backlight_init(struct drm_connector *); diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c index 2674f1587457..8a0f7994e1ae 100644 --- a/drivers/gpu/drm/nouveau/nouveau_dp.c +++ b/drivers/gpu/drm/nouveau/nouveau_dp.c @@ -98,3 +98,34 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) return NOUVEAU_DP_SST; return ret; } + +/* TODO: + * - Use the minimum possible BPC here, once we add support for the max bpc + * property. + * - Validate the mode against downstream port caps (see + * drm_dp_downstream_max_clock()) + * - Validate against the DP caps advertised by the GPU (we don't check these + * yet) + */ +enum drm_mode_status +nv50_dp_mode_valid(struct drm_connector *connector, + struct nouveau_encoder *outp, + const struct drm_display_mode *mode, + unsigned *out_clock) +{ + const unsigned min_clock = 25000; + unsigned max_clock, clock; + enum drm_mode_status ret; + + if (mode->flags & DRM_MODE_FLAG_INTERLACE && !outp->caps.dp_interlace) + return MODE_NO_INTERLACE; + + max_clock = outp->dp.link_nr * outp->dp.link_bw; + clock = mode->clock * (connector->display_info.bpc * 3) / 10; + + ret = nouveau_conn_mode_clock_valid(mode, min_clock, max_clock, + &clock); + if (out_clock) + *out_clock = clock; + return ret; +} diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h index 3217f587eceb..de51733b0476 100644 --- a/drivers/gpu/drm/nouveau/nouveau_encoder.h +++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h @@ -104,6 +104,10 @@ enum nouveau_dp_status { }; int nouveau_dp_detect(struct nouveau_encoder *); +enum drm_mode_status nv50_dp_mode_valid(struct drm_connector *, + struct nouveau_encoder *, + const struct drm_display_mode *, + unsigned *clock); struct nouveau_connector * nouveau_encoder_connector_get(struct nouveau_encoder *encoder); -- 2.26.2
Ilia Mirkin
2020-May-11 23:05 UTC
[Nouveau] [PATCH v3 3/5] drm/nouveau/kms/gv100-: Add support for interlaced modes
On Mon, May 11, 2020 at 6:42 PM Lyude Paul <lyude at redhat.com> wrote:> diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c > index 43bcbb6d73c4..6dae00da5d7e 100644 > --- a/drivers/gpu/drm/nouveau/nouveau_connector.c > +++ b/drivers/gpu/drm/nouveau/nouveau_connector.c > @@ -1065,7 +1065,7 @@ nouveau_connector_mode_valid(struct drm_connector *connector, > return get_slave_funcs(encoder)->mode_valid(encoder, mode); > case DCB_OUTPUT_DP: > if (mode->flags & DRM_MODE_FLAG_INTERLACE && > - !nv_encoder->dp.caps.interlace) > + !nv_encoder->caps.dp_interlace) > return MODE_NO_INTERLACE; > > max_clock = nv_encoder->dp.link_nr;You probably meant for this hunk to go into an earlier patch. -ilia
Seemingly Similar Threads
- [PATCH v3 3/5] drm/nouveau/kms/gv100-: Add support for interlaced modes
- [PATCH v3 0/5] drm/nouveau: DP interlace fixes
- [PATCH v2 0/5] drm/nouveau: DP interlace fixes
- [PATCH 4/4] drm/nouveau/kms/nv50-: Share DP SST mode_valid() handling with MST
- [PATCH 1/4] drm/nouveau/kms/nv50-: Probe SOR caps for DP interlacing support