Commit b96a1d8c authored by Ben Skeggs's avatar Ben Skeggs Committed by Karol Herbst
Browse files

drm/nouveau/disp/dp: generate supported link rates table at detect time



Replaces the static list used during link training with a table built
dynamically from the union of source and sink capabilities.

Preparation for adding support for HBR3, LTTPR and eDP 1.4 link rates.

Signed-off-by: default avatarBen Skeggs <bskeggs@redhat.com>
Reviewed-by: default avatarLyude Paul <lyude@redhat.com>
Signed-off-by: default avatarKarol Herbst <kherbst@redhat.com>
Link: https://gitlab.freedesktop.org/drm/nouveau/-/merge_requests/17
parent be5b6985
Loading
Loading
Loading
Loading
+39 −69
Original line number Diff line number Diff line
@@ -350,63 +350,13 @@ nvkm_dp_train_init(struct nvkm_dp *dp)
	}
}

static const struct dp_rates {
	u32 rate;
	u8  bw;
	u8  nr;
} nvkm_dp_rates[] = {
	{ 2160000, 0x14, 4 },
	{ 1080000, 0x0a, 4 },
	{ 1080000, 0x14, 2 },
	{  648000, 0x06, 4 },
	{  540000, 0x0a, 2 },
	{  540000, 0x14, 1 },
	{  324000, 0x06, 2 },
	{  270000, 0x0a, 1 },
	{  162000, 0x06, 1 },
	{}
};

static int
nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
{
	struct nvkm_ior *ior = dp->outp.ior;
	const u8 sink_nr = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
	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 *failsafe = NULL, *cfg;
	int ret = -EINVAL;
	int ret = -EINVAL, nr, rate;
	u8  pwr;

	/* 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->bw <= outp_bw) {
			/* Try to respect sink limits too when selecting
			 * lowest link configuration.
			 */
			if (!failsafe ||
			    (cfg->nr <= sink_nr && cfg->bw <= sink_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) {
@@ -416,26 +366,23 @@ nvkm_dp_train(struct nvkm_dp *dp, u32 dataKBps)
		}
	}

	/* Link training. */
	OUTP_DBG(&dp->outp, "training (min: %d x %d MB/s)",
		 failsafe->nr, failsafe->bw * 27);
	nvkm_dp_train_init(dp);
	for (cfg = nvkm_dp_rates; ret < 0 && cfg <= failsafe; cfg++) {
		/* Skip configurations not supported by both OR and sink. */
		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;
		ior->dp.nr = cfg->nr;
	ior->dp.nr = 0;

	/* Link training. */
	OUTP_DBG(&dp->outp, "training");
	nvkm_dp_train_init(dp);
	for (nr = dp->links; ret < 0 && nr; nr >>= 1) {
		for (rate = 0; ret < 0 && rate < dp->rates; rate++) {
			if (dp->rate[rate].rate * nr >= dataKBps || WARN_ON(!ior->dp.nr)) {
				/* Program selected link configuration. */
				ior->dp.bw = dp->rate[rate].rate / 27000;
				ior->dp.nr = nr;
				ret = nvkm_dp_train_links(dp);
			}
		}
	}
	nvkm_dp_train_fini(dp);
	if (ret < 0)
		OUTP_ERR(&dp->outp, "training failed");
@@ -543,10 +490,33 @@ nvkm_dp_enable(struct nvkm_dp *dp, bool enable)
			dp->present = true;
		}

		if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd,
				sizeof(dp->dpcd)))
		if (!nvkm_rdaux(aux, DPCD_RC00_DPCD_REV, dp->dpcd, sizeof(dp->dpcd))) {
			const u8 rates[] = { 0x14, 0x0a, 0x06, 0 };
			const u8 *rate;
			int rate_max;

			dp->rates = 0;
			dp->links = dp->dpcd[DPCD_RC02] & DPCD_RC02_MAX_LANE_COUNT;
			dp->links = min(dp->links, dp->outp.info.dpconf.link_nr);

			rate_max = dp->dpcd[DPCD_RC01_MAX_LINK_RATE];
			rate_max = min(rate_max, dp->outp.info.dpconf.link_bw);

			if (1) {
				for (rate = rates; *rate; rate++) {
					if (*rate <= rate_max) {
						if (WARN_ON(dp->rates == ARRAY_SIZE(dp->rate)))
							break;

						dp->rate[dp->rates].rate = *rate * 27000;
						dp->rates++;
					}
				}
			}

			return true;
		}
	}

	if (dp->present) {
		OUTP_DBG(&dp->outp, "aux power -> demand");
+6 −0
Original line number Diff line number Diff line
@@ -20,6 +20,12 @@ struct nvkm_dp {
	bool present;
	u8 dpcd[16];

	struct {
		u32 rate;
	} rate[3];
	int rates;
	int links;

	struct mutex mutex;
	struct {
		atomic_t done;