small update to my last version I sent out. Patches 3-6 are optional and should only improve detecting the max clocks for HDMI and DP, but they didn't underwent big testing and I am a bit concerned, that it might break detecting the DP limits on some boards. Karol Herbst (6): kms/nv50: move nv50_mstm out of the dp union in nouveau_encoder kms/nv50: reject interlaced modes if the hardware doesn't support it kms/nv50: add core957d class kms/nv50: read out display max clocks kms/nv50: detect HDMI max MHz correctly kms/nv50: detect LVDS max MHz correctly drm/nouveau/dispnv50/Kbuild | 1 + drm/nouveau/dispnv50/core.c | 6 ++-- drm/nouveau/dispnv50/core.h | 23 +++++++++++++ drm/nouveau/dispnv50/core507d.c | 25 ++++++++++++++ drm/nouveau/dispnv50/core907d.c | 27 +++++++++++++++ drm/nouveau/dispnv50/core917d.c | 2 ++ drm/nouveau/dispnv50/core957d.c | 60 +++++++++++++++++++++++++++++++++ drm/nouveau/dispnv50/disp.c | 53 ++++++++++++++++++++--------- drm/nouveau/nouveau_connector.c | 26 +++++++++----- drm/nouveau/nouveau_dp.c | 2 +- drm/nouveau/nouveau_encoder.h | 11 +++++- 11 files changed, 208 insertions(+), 28 deletions(-) create mode 100644 drm/nouveau/dispnv50/core957d.c -- 2.17.1
Karol Herbst
2018-Aug-03 12:19 UTC
[Nouveau] [PATCH v3 1/6] kms/nv50: move nv50_mstm out of the dp union in nouveau_encoder
This field isn't only used for DP ones, but for all. Prevents memory corruption when adding more structs to the union. Signed-off-by: Karol Herbst <kherbst at redhat.com> --- drm/nouveau/dispnv50/disp.c | 12 ++++++------ drm/nouveau/nouveau_connector.c | 4 ++-- drm/nouveau/nouveau_dp.c | 2 +- drm/nouveau/nouveau_encoder.h | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c index 42f47ac9..f04ab219 100644 --- a/drm/nouveau/dispnv50/disp.c +++ b/drm/nouveau/dispnv50/disp.c @@ -1398,7 +1398,7 @@ static void nv50_sor_destroy(struct drm_encoder *encoder) { struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder); - nv50_mstm_del(&nv_encoder->dp.mstm); + nv50_mstm_del(&nv_encoder->mstm); drm_encoder_cleanup(encoder); kfree(encoder); } @@ -1466,7 +1466,7 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe) ver >= 0x40 && (nvbios_rd08(bios, data + 0x08) & 0x04)) { ret = nv50_mstm_new(nv_encoder, &nv_connector->aux, 16, nv_connector->base.base.id, - &nv_encoder->dp.mstm); + &nv_encoder->mstm); if (ret) return ret; } @@ -1622,7 +1622,7 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) drm_for_each_encoder(encoder, drm->dev) { if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { - mstm = nouveau_encoder(encoder)->dp.mstm; + mstm = nouveau_encoder(encoder)->mstm; if (mstm && mstm->modified) nv50_mstm_prepare(mstm); } @@ -1636,7 +1636,7 @@ nv50_disp_atomic_commit_core(struct drm_atomic_state *state, u32 *interlock) drm_for_each_encoder(encoder, drm->dev) { if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { - mstm = nouveau_encoder(encoder)->dp.mstm; + mstm = nouveau_encoder(encoder)->mstm; if (mstm && mstm->modified) nv50_mstm_cleanup(mstm); } @@ -2103,7 +2103,7 @@ nv50_display_fini(struct drm_device *dev) list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) { if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { nv_encoder = nouveau_encoder(encoder); - nv50_mstm_fini(nv_encoder->dp.mstm); + nv50_mstm_fini(nv_encoder->mstm); } } } @@ -2121,7 +2121,7 @@ nv50_display_init(struct drm_device *dev) if (encoder->encoder_type != DRM_MODE_ENCODER_DPMST) { struct nouveau_encoder *nv_encoder nouveau_encoder(encoder); - nv50_mstm_init(nv_encoder->dp.mstm); + nv50_mstm_init(nv_encoder->mstm); } } diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c index af68eae4..5cc94944 100644 --- a/drm/nouveau/nouveau_connector.c +++ b/drm/nouveau/nouveau_connector.c @@ -1124,14 +1124,14 @@ nouveau_connector_hotplug(struct nvif_notify *notify) if (rep->mask & NVIF_NOTIFY_CONN_V0_IRQ) { NV_DEBUG(drm, "service %s\n", name); if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) - nv50_mstm_service(nv_encoder->dp.mstm); + nv50_mstm_service(nv_encoder->mstm); } else { bool plugged = (rep->mask != NVIF_NOTIFY_CONN_V0_UNPLUG); NV_DEBUG(drm, "%splugged %s\n", plugged ? "" : "un", name); if ((nv_encoder = find_encoder(connector, DCB_OUTPUT_DP))) { if (!plugged) - nv50_mstm_remove(nv_encoder->dp.mstm); + nv50_mstm_remove(nv_encoder->mstm); } drm_helper_hpd_irq_event(connector->dev); diff --git a/drm/nouveau/nouveau_dp.c b/drm/nouveau/nouveau_dp.c index 0d052e16..7f71dff0 100644 --- a/drm/nouveau/nouveau_dp.c +++ b/drm/nouveau/nouveau_dp.c @@ -92,7 +92,7 @@ nouveau_dp_detect(struct nouveau_encoder *nv_encoder) nouveau_dp_probe_oui(dev, aux, dpcd); - ret = nv50_mstm_detect(nv_encoder->dp.mstm, dpcd, nouveau_mst); + ret = nv50_mstm_detect(nv_encoder->mstm, dpcd, nouveau_mst); if (ret == 1) return NOUVEAU_DP_MST; if (ret == 0) diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h index 3517f920..dfe095ad 100644 --- a/drm/nouveau/nouveau_encoder.h +++ b/drm/nouveau/nouveau_encoder.h @@ -58,9 +58,9 @@ struct nouveau_encoder { struct nv04_output_reg restore; + struct nv50_mstm *mstm; union { struct { - struct nv50_mstm *mstm; int link_nr; int link_bw; } dp; -- 2.17.1
Karol Herbst
2018-Aug-03 12:19 UTC
[Nouveau] [PATCH v3 2/6] 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. 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
Signed-off-by: Karol Herbst <kherbst at redhat.com> --- drm/nouveau/dispnv50/Kbuild | 1 + drm/nouveau/dispnv50/core.c | 6 ++--- drm/nouveau/dispnv50/core.h | 2 ++ drm/nouveau/dispnv50/core957d.c | 42 +++++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 drm/nouveau/dispnv50/core957d.c diff --git a/drm/nouveau/dispnv50/Kbuild b/drm/nouveau/dispnv50/Kbuild index 849b0f45..c3dee4d0 100644 --- a/drm/nouveau/dispnv50/Kbuild +++ b/drm/nouveau/dispnv50/Kbuild @@ -6,6 +6,7 @@ nouveau-y += dispnv50/core507d.o nouveau-y += dispnv50/core827d.o nouveau-y += dispnv50/core907d.o nouveau-y += dispnv50/core917d.o +nouveau-y += dispnv50/core957d.o nouveau-y += dispnv50/corec37d.o nouveau-y += dispnv50/dac507d.o diff --git a/drm/nouveau/dispnv50/core.c b/drm/nouveau/dispnv50/core.c index f3c49adb..ba453afb 100644 --- a/drm/nouveau/dispnv50/core.c +++ b/drm/nouveau/dispnv50/core.c @@ -43,9 +43,9 @@ nv50_core_new(struct nouveau_drm *drm, struct nv50_core **pcore) int (*new)(struct nouveau_drm *, s32, struct nv50_core **); } cores[] = { { GV100_DISP_CORE_CHANNEL_DMA, 0, corec37d_new }, - { GP102_DISP_CORE_CHANNEL_DMA, 0, core917d_new }, - { GP100_DISP_CORE_CHANNEL_DMA, 0, core917d_new }, - { GM200_DISP_CORE_CHANNEL_DMA, 0, core917d_new }, + { GP102_DISP_CORE_CHANNEL_DMA, 0, core957d_new }, + { GP100_DISP_CORE_CHANNEL_DMA, 0, core957d_new }, + { GM200_DISP_CORE_CHANNEL_DMA, 0, core957d_new }, { GM107_DISP_CORE_CHANNEL_DMA, 0, core917d_new }, { GK110_DISP_CORE_CHANNEL_DMA, 0, core917d_new }, { GK104_DISP_CORE_CHANNEL_DMA, 0, core917d_new }, diff --git a/drm/nouveau/dispnv50/core.h b/drm/nouveau/dispnv50/core.h index 62ef0d24..4738447a 100644 --- a/drm/nouveau/dispnv50/core.h +++ b/drm/nouveau/dispnv50/core.h @@ -57,6 +57,8 @@ bool core907d_caps_parse(struct nv50_disp *, struct nv50_core_caps *); int core917d_new(struct nouveau_drm *, s32, struct nv50_core **); +int core957d_new(struct nouveau_drm *, s32, struct nv50_core **); + int corec37d_new(struct nouveau_drm *, s32, struct nv50_core **); extern const struct nv50_outp_func sorc37d; #endif diff --git a/drm/nouveau/dispnv50/core957d.c b/drm/nouveau/dispnv50/core957d.c new file mode 100644 index 00000000..a31b1941 --- /dev/null +++ b/drm/nouveau/dispnv50/core957d.c @@ -0,0 +1,42 @@ +/* + * Copyright 2018 Red Hat Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ +#include "core.h" +#include "head.h" + +static const struct nv50_core_func +core957d = { + .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 = &head917d, + .dac = &dac907d, + .sor = &sor907d, +}; + +int +core957d_new(struct nouveau_drm *drm, s32 oclass, struct nv50_core **pcore) +{ + return core507d_new_(&core957d, drm, oclass, pcore); +} -- 2.17.1
Karol Herbst
2018-Aug-03 12:19 UTC
[Nouveau] [PATCH v3 4/6] kms/nv50: read out display max clocks
Signed-off-by: Karol Herbst <kherbst at redhat.com> --- drm/nouveau/dispnv50/core.h | 9 +++++++++ drm/nouveau/dispnv50/core907d.c | 5 +++++ drm/nouveau/dispnv50/core957d.c | 20 +++++++++++++++++++- 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drm/nouveau/dispnv50/core.h b/drm/nouveau/dispnv50/core.h index 4738447a..4283d269 100644 --- a/drm/nouveau/dispnv50/core.h +++ b/drm/nouveau/dispnv50/core.h @@ -11,8 +11,17 @@ struct nv50_core { struct nv50_core_caps { struct { struct { + uint16_t max_mhz; bool no_interlace; } dp; + + struct { + uint16_t max_mhz; + } lvds; + + struct { + uint16_t max_mhz; + } tmds; } sor[8]; }; diff --git a/drm/nouveau/dispnv50/core907d.c b/drm/nouveau/dispnv50/core907d.c index 854aa48e..79e5df31 100644 --- a/drm/nouveau/dispnv50/core907d.c +++ b/drm/nouveau/dispnv50/core907d.c @@ -37,6 +37,11 @@ core907d_caps_parse(struct nv50_disp *disp, struct nv50_core_caps *caps) 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)); + + data = nouveau_bo_rd32(disp->sync, 0x15 + i * 2); + caps->sor[i].dp.max_mhz = (data & 0xff) * 10; + caps->sor[i].tmds.max_mhz = ((data >> 16) & 0xff) * 10; + caps->sor[i].lvds.max_mhz = caps->sor[i].tmds.max_mhz; } return true; diff --git a/drm/nouveau/dispnv50/core957d.c b/drm/nouveau/dispnv50/core957d.c index a31b1941..bd03eb6e 100644 --- a/drm/nouveau/dispnv50/core957d.c +++ b/drm/nouveau/dispnv50/core957d.c @@ -22,6 +22,24 @@ #include "core.h" #include "head.h" +#include "nouveau_bo.h" + +static bool +core957d_caps_parse(struct nv50_disp *disp, struct nv50_core_caps *caps) +{ + int i; + + if (!core907d_caps_parse(disp, caps)) + return false; + + for (i = 0; i < 8; ++i) { + uint32_t data = nouveau_bo_rd32(disp->sync, 0x15 + i * 2); + caps->sor[i].lvds.max_mhz = (data >> 24) * 10; + } + + return true; +} + static const struct nv50_core_func core957d = { .init = core507d_init, @@ -29,7 +47,7 @@ core957d = { .ntfy_wait_done = core507d_ntfy_wait_done, .update = core507d_update, .caps_fetch = core507d_caps_fetch, - .caps_parse = core907d_caps_parse, + .caps_parse = core957d_caps_parse, .head = &head917d, .dac = &dac907d, .sor = &sor907d, -- 2.17.1
Karol Herbst
2018-Aug-03 12:19 UTC
[Nouveau] [PATCH v3 5/6] kms/nv50: detect HDMI max MHz correctly
v2: clean up left over comments don't overwrite hdmimhz parameter cap to 297MHz Signed-off-by: Karol Herbst <kherbst at redhat.com> --- drm/nouveau/dispnv50/disp.c | 5 +++++ drm/nouveau/nouveau_connector.c | 15 ++++++++++----- drm/nouveau/nouveau_encoder.h | 4 ++++ 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c index fa23d7a2..103433cb 100644 --- a/drm/nouveau/dispnv50/disp.c +++ b/drm/nouveau/dispnv50/disp.c @@ -1433,7 +1433,12 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe, case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; case DCB_OUTPUT_DP: nv_encoder->dp.no_interlace = caps->sor[or].dp.no_interlace; + type = DRM_MODE_ENCODER_TMDS; + break; case DCB_OUTPUT_TMDS: + nv_encoder->tmds.max_mhz = caps->sor[or].tmds.max_mhz; + type = DRM_MODE_ENCODER_TMDS; + break; default: type = DRM_MODE_ENCODER_TMDS; break; diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c index 074e6d52..65fac604 100644 --- a/drm/nouveau/nouveau_connector.c +++ b/drm/nouveau/nouveau_connector.c @@ -980,15 +980,20 @@ static unsigned get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi) { struct nouveau_connector *nv_connector = nouveau_connector(connector); + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; struct nouveau_drm *drm = nouveau_drm(connector->dev); struct dcb_output *dcb = nv_connector->detected_encoder->dcb; + if (hdmi && nouveau_hdmimhz > 0) + return nouveau_hdmimhz * 1000; + + if (nv_encoder->tmds.max_mhz) + /* no HDMI 2.0 for now and not quite clear if we can use + * higher HDMI clocks than 297MHz + */ + return min(297, nv_encoder->tmds.max_mhz) * 1000; + if (hdmi) { - if (nouveau_hdmimhz > 0) - return nouveau_hdmimhz * 1000; - /* Note: these limits are conservative, some Fermi's - * can do 297 MHz. Unclear how this can be determined. - */ if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER) return 297000; if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h index f74af5ce..fbef9dc0 100644 --- a/drm/nouveau/nouveau_encoder.h +++ b/drm/nouveau/nouveau_encoder.h @@ -65,6 +65,10 @@ struct nouveau_encoder { int link_bw; bool no_interlace; } dp; + + struct { + uint16_t max_mhz; + } tmds; }; void (*enc_save)(struct drm_encoder *encoder); -- 2.17.1
Karol Herbst
2018-Aug-03 12:19 UTC
[Nouveau] [PATCH v3 6/6] kms/nv50: detect LVDS max MHz correctly
Signed-off-by: Karol Herbst <kherbst at redhat.com> --- drm/nouveau/dispnv50/disp.c | 5 ++++- drm/nouveau/nouveau_connector.c | 5 ++++- drm/nouveau/nouveau_encoder.h | 4 ++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c index 103433cb..53c97c7a 100644 --- a/drm/nouveau/dispnv50/disp.c +++ b/drm/nouveau/dispnv50/disp.c @@ -1430,7 +1430,10 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe, nv_encoder->update = nv50_sor_update; switch (dcbe->type) { - case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; + case DCB_OUTPUT_LVDS: + nv_encoder->lvds.max_mhz = caps->sor[or].lvds.max_mhz; + type = DRM_MODE_ENCODER_LVDS; + break; case DCB_OUTPUT_DP: nv_encoder->dp.no_interlace = caps->sor[or].dp.no_interlace; type = DRM_MODE_ENCODER_TMDS; diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c index 65fac604..027636b3 100644 --- a/drm/nouveau/nouveau_connector.c +++ b/drm/nouveau/nouveau_connector.c @@ -1029,7 +1029,10 @@ nouveau_connector_mode_valid(struct drm_connector *connector, return MODE_PANEL; min_clock = 0; - max_clock = 400000; + if (nv_encoder->lvds.max_mhz) + max_clock = nv_encoder->lvds.max_mhz * 1000; + else + max_clock = 400000; break; case DCB_OUTPUT_TMDS: hdmi = drm_detect_hdmi_monitor(nv_connector->edid); diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h index fbef9dc0..6961bdfc 100644 --- a/drm/nouveau/nouveau_encoder.h +++ b/drm/nouveau/nouveau_encoder.h @@ -66,6 +66,10 @@ struct nouveau_encoder { bool no_interlace; } dp; + struct { + uint16_t max_mhz; + } lvds; + struct { uint16_t max_mhz; } tmds; -- 2.17.1
Ilia Mirkin
2018-Aug-03 14:08 UTC
[Nouveau] [PATCH v3 5/6] kms/nv50: detect HDMI max MHz correctly
On Fri, Aug 3, 2018 at 8:19 AM, Karol Herbst <kherbst at redhat.com> wrote:> v2: clean up left over comments > don't overwrite hdmimhz parameter > cap to 297MHz > > Signed-off-by: Karol Herbst <kherbst at redhat.com> > --- > drm/nouveau/dispnv50/disp.c | 5 +++++ > drm/nouveau/nouveau_connector.c | 15 ++++++++++----- > drm/nouveau/nouveau_encoder.h | 4 ++++ > 3 files changed, 19 insertions(+), 5 deletions(-) > > diff --git a/drm/nouveau/dispnv50/disp.c b/drm/nouveau/dispnv50/disp.c > index fa23d7a2..103433cb 100644 > --- a/drm/nouveau/dispnv50/disp.c > +++ b/drm/nouveau/dispnv50/disp.c > @@ -1433,7 +1433,12 @@ nv50_sor_create(struct drm_connector *connector, struct dcb_output *dcbe, > case DCB_OUTPUT_LVDS: type = DRM_MODE_ENCODER_LVDS; break; > case DCB_OUTPUT_DP: > nv_encoder->dp.no_interlace = caps->sor[or].dp.no_interlace; > + type = DRM_MODE_ENCODER_TMDS; > + break; > case DCB_OUTPUT_TMDS: > + nv_encoder->tmds.max_mhz = caps->sor[or].tmds.max_mhz; > + type = DRM_MODE_ENCODER_TMDS; > + break; > default: > type = DRM_MODE_ENCODER_TMDS; > break; > diff --git a/drm/nouveau/nouveau_connector.c b/drm/nouveau/nouveau_connector.c > index 074e6d52..65fac604 100644 > --- a/drm/nouveau/nouveau_connector.c > +++ b/drm/nouveau/nouveau_connector.c > @@ -980,15 +980,20 @@ static unsigned > get_tmds_link_bandwidth(struct drm_connector *connector, bool hdmi) > { > struct nouveau_connector *nv_connector = nouveau_connector(connector); > + struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder; > struct nouveau_drm *drm = nouveau_drm(connector->dev); > struct dcb_output *dcb = nv_connector->detected_encoder->dcb; > > + if (hdmi && nouveau_hdmimhz > 0) > + return nouveau_hdmimhz * 1000; > + > + if (nv_encoder->tmds.max_mhz) > + /* no HDMI 2.0 for now and not quite clear if we can use > + * higher HDMI clocks than 297MHz > + */ > + return min(297, nv_encoder->tmds.max_mhz) * 1000;So ... will you start reporting 297MHz single-link TMDS bandwidth over DVI now? What values show up in max_mhz in practice, across the boards you've tested (ideally a per-generation summary would be nice).> + > if (hdmi) { > - if (nouveau_hdmimhz > 0) > - return nouveau_hdmimhz * 1000; > - /* Note: these limits are conservative, some Fermi's > - * can do 297 MHz. Unclear how this can be determined. > - */ > if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_KEPLER) > return 297000; > if (drm->client.device.info.family >= NV_DEVICE_INFO_V0_FERMI) > diff --git a/drm/nouveau/nouveau_encoder.h b/drm/nouveau/nouveau_encoder.h > index f74af5ce..fbef9dc0 100644 > --- a/drm/nouveau/nouveau_encoder.h > +++ b/drm/nouveau/nouveau_encoder.h > @@ -65,6 +65,10 @@ struct nouveau_encoder { > int link_bw; > bool no_interlace; > } dp; > + > + struct { > + uint16_t max_mhz; > + } tmds; > }; > > void (*enc_save)(struct drm_encoder *encoder); > -- > 2.17.1 > > _______________________________________________ > Nouveau mailing list > Nouveau at lists.freedesktop.org > https://lists.freedesktop.org/mailman/listinfo/nouveau