]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
drm/nouveau/kms/nv50-: Probe SOR and PIOR caps for DP interlacing support
authorLyude Paul <lyude@redhat.com>
Mon, 11 May 2020 22:41:24 +0000 (18:41 -0400)
committerBen Skeggs <bskeggs@redhat.com>
Fri, 22 May 2020 01:13:51 +0000 (11:13 +1000)
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@redhat.com>
Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
15 files changed:
drivers/gpu/drm/nouveau/dispnv50/core.h
drivers/gpu/drm/nouveau/dispnv50/core507d.c
drivers/gpu/drm/nouveau/dispnv50/core827d.c
drivers/gpu/drm/nouveau/dispnv50/core907d.c
drivers/gpu/drm/nouveau/dispnv50/core917d.c
drivers/gpu/drm/nouveau/dispnv50/corec37d.c
drivers/gpu/drm/nouveau/dispnv50/corec57d.c
drivers/gpu/drm/nouveau/dispnv50/disp.c
drivers/gpu/drm/nouveau/dispnv50/disp.h
drivers/gpu/drm/nouveau/dispnv50/pior507d.c
drivers/gpu/drm/nouveau/dispnv50/sor507d.c
drivers/gpu/drm/nouveau/dispnv50/sor907d.c
drivers/gpu/drm/nouveau/dispnv50/sorc37d.c
drivers/gpu/drm/nouveau/nouveau_connector.c
drivers/gpu/drm/nouveau/nouveau_encoder.h

index ff94f3f6f264ef0c01e8c5811e104546f621a8d1..99157dc94d2350f1a6be09ade0a1bcd9138ea20b 100644 (file)
@@ -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 *);
index c5152c39c684dc1e50896e88eef458d38e162f88..e341f572c2696e3672109ed1efd47cc70f34380e 100644 (file)
@@ -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,
index 6123a068f8364ac49012d4caf805fb082cd04b14..2e0c1c536afebf44a30c4ad4335cda93467d7d95 100644 (file)
@@ -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,
index ef822f8134355a9a4e4915127b93d43f2e717905..2716298326299cf07c93cf5df1a13e54aea9a2c6 100644 (file)
@@ -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,
index 392338df5bfdcaa2170196031f6fa573f6d88312..5cc072d4c30fef18b32bebe1ee8538e5fa5d2351 100644 (file)
@@ -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,
index c03cb987856bd3d2a75543ec88bb4b480674a989..e0c8811fb8e45ca05d27d4e8afa2c4fe49fc0060 100644 (file)
@@ -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,
index 147adcd609378829f25dc5a1f4449703ebe9e713..10ba9e9e4ae6b4889f65b884ea4dcffc2a01824b 100644 (file)
@@ -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,
index d5488c85eb43964a5dcef000d501dde368e5ca42..e6a93fdfc6a29866b7b5e902930b77a76b82f1b9 100644 (file)
@@ -1663,6 +1663,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) {
@@ -1689,10 +1690,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
@@ -1805,7 +1808,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;
@@ -1844,6 +1849,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;
 }
 
@@ -2401,6 +2409,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);
@@ -2462,6 +2472,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;
+       }
 
        /* Assign the correct format modifiers */
        if (disp->disp->object.oclass >= TU102_DISP)
index 1743c3a7621649d654e2fdf2ef6e646a0d4b0404..696e70a6b98b670e567058d185c5ea3c3b34b3e1 100644 (file)
@@ -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)
index d2bac6a341dcb696204016f1c17fd00dd4357d77..45d8ce7d2c28318d289ba21477c5b980582dd256 100644 (file)
@@ -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,
 };
index 5222fe6a9b21cd4ca1e7e064af14c2c3d94c8a82..9a59fa7da00dc4963fa9d0a66d31178a68cb54dd 100644 (file)
@@ -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,
 };
index b0314ec11fb3bc121e30b36e9e9491b00f00e0ee..9577ccf1c809b1d9e2095f56a3500d155b2e134e 100644 (file)
@@ -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,
 };
index dff059241c5ddda510ca5636587605e129356ac6..c86ca955fdcd4b125f0203d5530d54c229ed5710 100644 (file)
@@ -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,
 };
index 9a9a7f5003d3f370fe5f23926a836fd7ad0a6f37..6dae00da5d7ecfa66f57638c2833a03e130e2e14 100644 (file)
@@ -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->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;
index 3517f920bf8932952b7cf63e5feebabf0e1f77e6..3217f587eceb4930b5d6c042adbb73a892a4ab37 100644 (file)
@@ -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,