summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
diff options
context:
space:
mode:
authorBen Skeggs <bskeggs@redhat.com>2017-05-19 23:59:35 +1000
committerBen Skeggs <bskeggs@redhat.com>2017-06-16 14:04:51 +1000
commit49f2b376df7f9235ccbed1e90f0ec93040001a9b (patch)
treeb6783446eb970cea9ddc3c38ec18fb1ab18de786 /drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
parentfafa8b5c9f7426cef6b9b52b8582a867edbc7a06 (diff)
drm/nouveau/disp/dp: determine a failsafe link training rate
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>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c')
-rw-r--r--drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c54
1 files changed, 35 insertions, 19 deletions
diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c b/drivers/gpu/drm/nouveau/nvkm/engine/disp/dp.c
index 4941643dd8fa..090567d94876 100644
--- 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 *outp, u32 datakbps)
}
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;
}