Blob Blame History Raw
From 49f2b376df7f9235ccbed1e90f0ec93040001a9b Mon Sep 17 00:00:00 2001
From: Ben Skeggs <bskeggs@redhat.com>
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 <bskeggs@redhat.com>
Acked-by: Takashi Iwai <tiwai@suse.de>

---
 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;
 }