mirror of https://gitee.com/openkylin/linux.git
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@redhat.com> Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
This commit is contained in:
parent
fa1232ea84
commit
4a2cb4181b
|
@ -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 *);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Reference in New Issue