if (!mstm->links++) {
/*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)
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
nv50_mstm_detect(struct nouveau_encoder *outp)
{
return ret;
/* And start enabling */
- ret = nv50_mstm_enable(mstm, true);
- if (ret)
- return ret;
-
ret = drm_dp_mst_topology_mgr_set_mst(&mstm->mgr, true);
- if (ret) {
- nv50_mstm_enable(mstm, false);
+ if (ret)
return ret;
- }
mstm->is_mst = true;
return 1;
nvif_outp_acquire_lvds(&nv_encoder->outp, lvds_dual, lvds_8bpc);
break;
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);
if (nv_encoder->outp.or.link & 1)
break;
case DCB_OUTPUT_DP:
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;
default:
BUG();
struct nv50_disp_mthd_v1 {
__u8 version;
-#define NV50_DISP_MTHD_V1_SOR_DP_MST_LINK 0x25
#define NV50_DISP_MTHD_V1_SOR_DP_MST_VCPI 0x26
__u8 method;
__u16 hasht;
__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 {
__u8 version;
__u8 pad01[1];
__u8 pad02[6];
} lvds;
struct {
+ __u8 link_nr; /* 0 = highest possible. */
+ __u8 link_bw; /* 0 = highest possible, DP BW code otherwise. */
__u8 hda;
- __u8 pad01[7];
+ __u8 mst;
+ __u8 pad04[4];
+ __u8 dpcd[16];
} dp;
};
} v0;
int nvif_outp_acquire_tmds(struct nvif_outp *, int head,
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_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 *);
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_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;
int ret;
+ args.dp.link_nr = link_nr;
+ args.dp.link_bw = link_bw;
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);
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;
}
u8 sink[2], data;
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. */
if (disp->engine.subdev.device->chipset < 0x110)
/* Link training. */
OUTP_DBG(outp, "training");
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 (rate = 0; ret < 0 && rate < outp->dp.rates; rate++) {
if (outp->dp.rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) {
}
}
}
+
+ /* Finish up. */
nvkm_dp_train_fini(outp);
if (ret < 0)
OUTP_ERR(outp, "training failed");
struct mutex mutex;
struct {
atomic_t done;
+ u8 nr;
+ u8 bw;
bool mst;
} lt;
} dp;
}
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: {
union {
struct nv50_disp_sor_dp_mst_vcpi_v0 v0;
}
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
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)
{
if (argc != sizeof(args->v0) || args->v0.version != 0)
return -ENOSYS;
+ if (outp->ior)
+ return -EBUSY;
switch (args->v0.proto) {
case NVIF_OUTP_ACQUIRE_V0_RGB_CRT:
args->v0.tmds.hdmi_scdc,
args->v0.tmds.hdmi_hda);
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:
ret = nvkm_uoutp_mthd_acquire_lvds(outp, args->v0.lvds.dual, args->v0.lvds.bpc8);
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:
ret = -EINVAL;
break;