From 49f2b376df7f9235ccbed1e90f0ec93040001a9b Mon Sep 17 00:00:00 2001 From: Ben Skeggs Date: Fri, 19 May 2017 23:59:35 +1000 Subject: [PATCH] drm/nouveau/disp/dp: determine a failsafe link training rate Git-commit: 49f2b376df7f9235ccbed1e90f0ec93040001a9b Patch-mainline: v4.13-rc1 References: bsc#1095094 The aim here is to protect the OR against locking up when something unexpected happens (such as the display disappearing during modeset, or the DD misbehaving). Signed-off-by: Ben Skeggs Acked-by: Takashi Iwai --- drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c | 54 ++++++++++++++++---------- 1 file changed, 35 insertions(+), 19 deletions(-) --- a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c @@ -327,7 +327,7 @@ static const struct dp_rates { }; static int -nvkm_dp_train(struct nvkm_dp *dp) +nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps) { struct nv50_disp *disp = nv50_disp(dp->outp.disp); struct nvkm_ior *ior = dp->outp.ior; @@ -335,13 +335,34 @@ nvkm_dp_train(struct nvkm_dp *dp) const u8 sink_bw = dp->dpcd[DPCD_RC01_MAX_LINK_RATE]; const u8 outp_nr = dp->outp.info.dpconf.link_nr; const u8 outp_bw = dp->outp.info.dpconf.link_bw; - const struct dp_rates *cfg; + const struct dp_rates *failsafe = NULL, *cfg; + int ret = -EINVAL; u8 pwr; - int ret; if (!dp->outp.info.location && disp->func->sor.magic) disp->func->sor.magic(&dp->outp); + /* Find the lowest configuration of the OR that can support + * the required link rate. + * + * We will refuse to program the OR to lower rates, even if + * link training fails at higher rates (or even if the sink + * can't support the rate at all, though the DD is supposed + * to prevent such situations from happening). + * + * Attempting to do so can cause the entire display to hang, + * and it's better to have a failed modeset than that. + */ + for (cfg = nvkm_dp_rates; cfg->rate; cfg++) { + if (cfg->nr <= outp_nr && cfg->nr <= outp_bw) + failsafe = cfg; + if (failsafe && cfg[1].rate < dataKBps) + break; + } + + if (WARN_ON(!failsafe)) + return ret; + /* Ensure sink is not in a low-power state. */ if (!nvkm_rdaux(dp->aux, DPCD_SC00, &pwr, 1)) { if ((pwr & DPCD_SC00_SET_POWER) != DPCD_SC00_SET_POWER_D0) { @@ -352,13 +373,17 @@ nvkm_dp_train(struct nvkm_dp *dp) } /* Link training. */ + OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)", + failsafe->nr, failsafe->bw * 27); nvkm_dp_train_init(dp); - for (ret = -EINVAL, cfg = nvkm_dp_rates; ret < 0 && cfg->rate; cfg++) { + for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) { /* Skip configurations not supported by both OR and sink. */ - if (cfg[1].rate && - (cfg->nr > outp_nr || cfg->bw > outp_bw || - cfg->nr > sink_nr || cfg->bw > sink_bw)) - continue; + if ((cfg->nr > outp_nr || cfg->bw > outp_bw || + cfg->nr > sink_nr || cfg->bw > sink_bw)) { + if (cfg != failsafe) + continue; + OUTP_ERR(&dp->outp, "link rate unsupported by sink"); + } ior->dp.mst = dp->lt.mst; ior->dp.ef = dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP; ior->dp.bw = cfg->bw; @@ -422,17 +447,8 @@ nvkm_output_dp_train(struct nvkm_outp *o } done: - if (retrain || !atomic_read(&dp->lt.done)) { - /* no sink, but still need to configure source */ - if (dp->dpcd[DPCD_RC00_DPCD_REV] == 0x00) { - dp->dpcd[DPCD_RC01_MAX_LINK_RATE] = - dp->outp.info.dpconf.link_bw; - dp->dpcd[DPCD_RC02] = - dp->outp.info.dpconf.link_nr; - } - ret = nvkm_dp_train(dp); - } - + if (retrain || !atomic_read(&dp->lt.done)) + ret = nvkm_dp_train(dp, dataKBps); mutex_unlock(&dp->mutex); return ret; }