drm/nouveau/disp: move DP link config into acquire
Aside from fixing MST->SST switching (KMS never turned off MST link config), this should preserve existing behaviour for the moment, but provide a path for the KMS driver to have more explicit control of the DP link, which has been requested by Lyude. More research into modeset/supervisor interactions is needed before we can have fully explicit control from the KMS driver. Signed-off-by: Ben Skeggs <bskeggs@redhat.com> Reviewed-by: Lyude Paul <lyude@redhat.com>
This commit is contained in:
parent
a9f5d77219
commit
8134437213
@ -1014,7 +1014,7 @@ nv50_msto_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st
|
|||||||
|
|
||||||
if (!mstm->links++) {
|
if (!mstm->links++) {
|
||||||
/*XXX: MST audio. */
|
/*XXX: MST audio. */
|
||||||
nvif_outp_acquire_dp(&mstm->outp->outp, false);
|
nvif_outp_acquire_dp(&mstm->outp->outp, mstm->outp->dp.dpcd, 0, 0, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mstm->outp->outp.or.link & 1)
|
if (mstm->outp->outp.or.link & 1)
|
||||||
@ -1380,26 +1380,6 @@ nv50_mstm_remove(struct nv50_mstm *mstm)
|
|||||||
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
|
drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
nv50_mstm_enable(struct nv50_mstm *mstm, int state)
|
|
||||||
{
|
|
||||||
struct nouveau_encoder *outp = mstm->outp;
|
|
||||||
struct {
|
|
||||||
struct nv50_disp_mthd_v1 base;
|
|
||||||
struct nv50_disp_sor_dp_mst_link_v0 mst;
|
|
||||||
} args = {
|
|
||||||
.base.version = 1,
|
|
||||||
.base.method = NV50_DISP_MTHD_V1_SOR_DP_MST_LINK,
|
|
||||||
.base.hasht = outp->dcb->hasht,
|
|
||||||
.base.hashm = outp->dcb->hashm,
|
|
||||||
.mst.state = state,
|
|
||||||
};
|
|
||||||
struct nouveau_drm *drm = nouveau_drm(outp->base.base.dev);
|
|
||||||
struct nvif_object *disp = &drm->display->disp.object;
|
|
||||||
|
|
||||||
return nvif_mthd(disp, 0, &args, sizeof(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
nv50_mstm_detect(struct nouveau_encoder *outp)
|
nv50_mstm_detect(struct nouveau_encoder *outp)
|
||||||
{
|
{
|
||||||
@ -1420,16 +1400,10 @@ nv50_mstm_detect(struct nouveau_encoder *outp)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
/* And start enabling */
|
/* And start enabling */
|
||||||
ret = nv50_mstm_enable(mstm, true);
|
ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, true);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, true);
|
|
||||||
if (ret) {
|
|
||||||
nv50_mstm_enable(mstm, false);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
mstm->is_mst = true;
|
mstm->is_mst = true;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
@ -1660,7 +1634,7 @@ nv50_sor_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *sta
|
|||||||
nvif_outp_acquire_lvds(&nv_encoder->outp, lvds_dual, lvds_8bpc);
|
nvif_outp_acquire_lvds(&nv_encoder->outp, lvds_dual, lvds_8bpc);
|
||||||
break;
|
break;
|
||||||
case DCB_OUTPUT_DP:
|
case DCB_OUTPUT_DP:
|
||||||
nvif_outp_acquire_dp(&nv_encoder->outp, hda);
|
nvif_outp_acquire_dp(&nv_encoder->outp, nv_encoder->dp.dpcd, 0, 0, hda, false);
|
||||||
depth = nv50_dp_bpc_to_depth(asyh->or.bpc);
|
depth = nv50_dp_bpc_to_depth(asyh->or.bpc);
|
||||||
|
|
||||||
if (nv_encoder->outp.or.link & 1)
|
if (nv_encoder->outp.or.link & 1)
|
||||||
@ -1858,7 +1832,7 @@ nv50_pior_atomic_enable(struct drm_encoder *encoder, struct drm_atomic_state *st
|
|||||||
break;
|
break;
|
||||||
case DCB_OUTPUT_DP:
|
case DCB_OUTPUT_DP:
|
||||||
ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC);
|
ctrl |= NVDEF(NV507D, PIOR_SET_CONTROL, PROTOCOL, EXT_TMDS_ENC);
|
||||||
nvif_outp_acquire_dp(&nv_encoder->outp, false);
|
nvif_outp_acquire_dp(&nv_encoder->outp, nv_encoder->dp.dpcd, 0, 0, false, false);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
BUG();
|
BUG();
|
||||||
|
@ -28,7 +28,6 @@ struct nv50_disp_scanoutpos_v0 {
|
|||||||
|
|
||||||
struct nv50_disp_mthd_v1 {
|
struct nv50_disp_mthd_v1 {
|
||||||
__u8 version;
|
__u8 version;
|
||||||
#define NV50_DISP_MTHD_V1_SOR_DP_MST_LINK 0x25
|
|
||||||
#define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26
|
#define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26
|
||||||
__u8 method;
|
__u8 method;
|
||||||
__u16 hasht;
|
__u16 hasht;
|
||||||
@ -36,12 +35,6 @@ struct nv50_disp_mthd_v1 {
|
|||||||
__u8 pad06[2];
|
__u8 pad06[2];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct nv50_disp_sor_dp_mst_link_v0 {
|
|
||||||
__u8 version;
|
|
||||||
__u8 state;
|
|
||||||
__u8 pad02[6];
|
|
||||||
};
|
|
||||||
|
|
||||||
struct nv50_disp_sor_dp_mst_vcpi_v0 {
|
struct nv50_disp_sor_dp_mst_vcpi_v0 {
|
||||||
__u8 version;
|
__u8 version;
|
||||||
__u8 pad01[1];
|
__u8 pad01[1];
|
||||||
|
@ -55,8 +55,12 @@ union nvif_outp_acquire_args {
|
|||||||
__u8 pad02[6];
|
__u8 pad02[6];
|
||||||
} lvds;
|
} lvds;
|
||||||
struct {
|
struct {
|
||||||
|
__u8 link_nr; /* 0 = highest possible. */
|
||||||
|
__u8 link_bw; /* 0 = highest possible, DP BW code otherwise. */
|
||||||
__u8 hda;
|
__u8 hda;
|
||||||
__u8 pad01[7];
|
__u8 mst;
|
||||||
|
__u8 pad04[4];
|
||||||
|
__u8 dpcd[16];
|
||||||
} dp;
|
} dp;
|
||||||
};
|
};
|
||||||
} v0;
|
} v0;
|
||||||
|
@ -21,7 +21,8 @@ int nvif_outp_acquire_rgb_crt(struct nvif_outp *);
|
|||||||
int nvif_outp_acquire_tmds(struct nvif_outp *, int head,
|
int nvif_outp_acquire_tmds(struct nvif_outp *, int head,
|
||||||
bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda);
|
bool hdmi, u8 max_ac_packet, u8 rekey, u8 scdc, bool hda);
|
||||||
int nvif_outp_acquire_lvds(struct nvif_outp *, bool dual, bool bpc8);
|
int nvif_outp_acquire_lvds(struct nvif_outp *, bool dual, bool bpc8);
|
||||||
int nvif_outp_acquire_dp(struct nvif_outp *, bool hda);
|
int nvif_outp_acquire_dp(struct nvif_outp *, u8 dpcd[16],
|
||||||
|
int link_nr, int link_bw, bool hda, bool mst);
|
||||||
void nvif_outp_release(struct nvif_outp *);
|
void nvif_outp_release(struct nvif_outp *);
|
||||||
int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size);
|
int nvif_outp_infoframe(struct nvif_outp *, u8 type, struct nvif_outp_infoframe_v0 *, u32 size);
|
||||||
int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size);
|
int nvif_outp_hda_eld(struct nvif_outp *, int head, void *data, u32 size);
|
||||||
|
@ -84,16 +84,22 @@ nvif_outp_acquire(struct nvif_outp *outp, u8 proto, struct nvif_outp_acquire_v0
|
|||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
nvif_outp_acquire_dp(struct nvif_outp *outp, bool hda)
|
nvif_outp_acquire_dp(struct nvif_outp *outp, u8 dpcd[16],
|
||||||
|
int link_nr, int link_bw, bool hda, bool mst)
|
||||||
{
|
{
|
||||||
struct nvif_outp_acquire_v0 args;
|
struct nvif_outp_acquire_v0 args;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
args.dp.link_nr = link_nr;
|
||||||
|
args.dp.link_bw = link_bw;
|
||||||
args.dp.hda = hda;
|
args.dp.hda = hda;
|
||||||
|
args.dp.mst = mst;
|
||||||
|
memcpy(args.dp.dpcd, dpcd, sizeof(args.dp.dpcd));
|
||||||
|
|
||||||
ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_DP, &args);
|
ret = nvif_outp_acquire(outp, NVIF_OUTP_ACQUIRE_V0_DP, &args);
|
||||||
NVIF_ERRON(ret, &outp->object,
|
NVIF_ERRON(ret, &outp->object,
|
||||||
"[ACQUIRE proto:DP hda:%d] or:%d link:%d", args.dp.hda, args.or, args.link);
|
"[ACQUIRE proto:DP link_nr:%d link_bw:%02x hda:%d mst:%d] or:%d link:%d",
|
||||||
|
args.dp.link_nr, args.dp.link_bw, args.dp.hda, args.dp.mst, args.or, args.link);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,7 +287,7 @@ nvkm_dp_train_links(struct nvkm_outp *outp, int rate)
|
|||||||
u8 sink[2], data;
|
u8 sink[2], data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
OUTP_DBG(outp, "training %d x %d MB/s", ior->dp.nr, ior->dp.bw * 27);
|
OUTP_DBG(outp, "training %dx%02x", ior->dp.nr, ior->dp.bw);
|
||||||
|
|
||||||
/* Intersect misc. capabilities of the OR and sink. */
|
/* Intersect misc. capabilities of the OR and sink. */
|
||||||
if (disp->engine.subdev.device->chipset < 0x110)
|
if (disp->engine.subdev.device->chipset < 0x110)
|
||||||
@ -455,6 +455,21 @@ nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps)
|
|||||||
/* Link training. */
|
/* Link training. */
|
||||||
OUTP_DBG(outp, "training");
|
OUTP_DBG(outp, "training");
|
||||||
nvkm_dp_train_init(outp);
|
nvkm_dp_train_init(outp);
|
||||||
|
|
||||||
|
/* Validate and train at configuration requested (if any) on ACQUIRE. */
|
||||||
|
if (outp->dp.lt.nr) {
|
||||||
|
for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) {
|
||||||
|
for (rate = 0; nr == outp->dp.lt.nr && rate < outp->dp.rates; rate++) {
|
||||||
|
if (outp->dp.rate[rate].rate / 27000 == outp->dp.lt.bw) {
|
||||||
|
ior->dp.bw = outp->dp.rate[rate].rate / 27000;
|
||||||
|
ior->dp.nr = nr;
|
||||||
|
ret = nvkm_dp_train_links(outp, rate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Otherwise, loop through all valid link configurations that support the data rate. */
|
||||||
for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) {
|
for (nr = outp->dp.links; ret < 0 && nr; nr >>= 1) {
|
||||||
for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) {
|
for (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) {
|
||||||
if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) {
|
if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) {
|
||||||
@ -465,6 +480,8 @@ nvkm_dp_train(struct nvkm_outp *outp, u32 dataKBps)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Finish up. */
|
||||||
nvkm_dp_train_fini(outp);
|
nvkm_dp_train_fini(outp);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
OUTP_ERR(outp, "training failed");
|
OUTP_ERR(outp, "training failed");
|
||||||
|
@ -54,6 +54,8 @@ struct nvkm_outp {
|
|||||||
struct mutex mutex;
|
struct mutex mutex;
|
||||||
struct {
|
struct {
|
||||||
atomic_t done;
|
atomic_t done;
|
||||||
|
u8 nr;
|
||||||
|
u8 bw;
|
||||||
bool mst;
|
bool mst;
|
||||||
} lt;
|
} lt;
|
||||||
} dp;
|
} dp;
|
||||||
|
@ -91,21 +91,6 @@ nv50_disp_root_mthd_(struct nvkm_object *object, u32 mthd, void *data, u32 size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (mthd * !!outp) {
|
switch (mthd * !!outp) {
|
||||||
case NV50_DISP_MTHD_V1_SOR_DP_MST_LINK: {
|
|
||||||
union {
|
|
||||||
struct nv50_disp_sor_dp_mst_link_v0 v0;
|
|
||||||
} *args = data;
|
|
||||||
int ret = -ENOSYS;
|
|
||||||
nvif_ioctl(object, "disp sor dp mst link size %d\n", size);
|
|
||||||
if (!(ret = nvif_unpack(ret, &data, &size, args->v0, 0, 0, false))) {
|
|
||||||
nvif_ioctl(object, "disp sor dp mst link vers %d state %d\n",
|
|
||||||
args->v0.version, args->v0.state);
|
|
||||||
outp->dp.lt.mst = !!args->v0.state;
|
|
||||||
return 0;
|
|
||||||
} else
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
|
case NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI: {
|
||||||
union {
|
union {
|
||||||
struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
|
struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
|
||||||
|
@ -100,6 +100,23 @@ nvkm_uoutp_mthd_release(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
nvkm_uoutp_mthd_acquire_dp(struct nvkm_outp *outp, u8 dpcd[16],
|
||||||
|
u8 link_nr, u8 link_bw, bool hda, bool mst)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, hda);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
memcpy(outp->dp.dpcd, dpcd, sizeof(outp->dp.dpcd));
|
||||||
|
outp->dp.lt.nr = link_nr;
|
||||||
|
outp->dp.lt.bw = link_bw;
|
||||||
|
outp->dp.lt.mst = mst;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp *outp, u8 head, u8 hdmi, u8 hdmi_max_ac_packet,
|
nvkm_uoutp_mthd_acquire_tmds(struct nvkm_outp *outp, u8 head, u8 hdmi, u8 hdmi_max_ac_packet,
|
||||||
u8 hdmi_rekey, u8 hdmi_scdc, u8 hdmi_hda)
|
u8 hdmi_rekey, u8 hdmi_scdc, u8 hdmi_hda)
|
||||||
@ -152,6 +169,8 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||||||
|
|
||||||
if (argc != sizeof(args->v0) || args->v0.version != 0)
|
if (argc != sizeof(args->v0) || args->v0.version != 0)
|
||||||
return -ENOSYS;
|
return -ENOSYS;
|
||||||
|
if (outp->ior)
|
||||||
|
return -EBUSY;
|
||||||
|
|
||||||
switch (args->v0.proto) {
|
switch (args->v0.proto) {
|
||||||
case NVIF_OUTP_ACQUIRE_V0_RGB_CRT:
|
case NVIF_OUTP_ACQUIRE_V0_RGB_CRT:
|
||||||
@ -165,12 +184,16 @@ nvkm_uoutp_mthd_acquire(struct nvkm_outp *outp, void *argv, u32 argc)
|
|||||||
args->v0.tmds.hdmi_scdc,
|
args->v0.tmds.hdmi_scdc,
|
||||||
args->v0.tmds.hdmi_hda);
|
args->v0.tmds.hdmi_hda);
|
||||||
break;
|
break;
|
||||||
case NVIF_OUTP_ACQUIRE_V0_DP:
|
|
||||||
ret = nvkm_outp_acquire(outp, NVKM_OUTP_USER, args->v0.dp.hda);
|
|
||||||
break;
|
|
||||||
case NVIF_OUTP_ACQUIRE_V0_LVDS:
|
case NVIF_OUTP_ACQUIRE_V0_LVDS:
|
||||||
ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8);
|
ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8);
|
||||||
break;
|
break;
|
||||||
|
case NVIF_OUTP_ACQUIRE_V0_DP:
|
||||||
|
ret = nvkm_uoutp_mthd_acquire_dp(outp, args->v0.dp.dpcd,
|
||||||
|
args->v0.dp.link_nr,
|
||||||
|
args->v0.dp.link_bw,
|
||||||
|
args->v0.dp.hda != 0,
|
||||||
|
args->v0.dp.mst != 0);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
ret = -EINVAL;
|
ret = -EINVAL;
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user