drm/nouveau/disp/dp: make use of existing output data for link training

Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
diff --git a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
index 3814c3d..efccbab 100644
--- a/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
+++ b/drivers/gpu/drm/nouveau/core/engine/disp/dport.c
@@ -33,24 +33,13 @@
 #include <core/class.h>
 
 #include "dport.h"
-
-#define DBG(fmt, args...) nv_debug(dp->disp, "DP:%04x:%04x: " fmt,             \
-				   dp->outp->hasht, dp->outp->hashm, ##args)
-#define ERR(fmt, args...) nv_error(dp->disp, "DP:%04x:%04x: " fmt,             \
-				   dp->outp->hasht, dp->outp->hashm, ##args)
+#include "outpdp.h"
 
 /******************************************************************************
  * link training
  *****************************************************************************/
 struct dp_state {
-	const struct nouveau_dp_func *func;
-	struct nouveau_disp *disp;
-	struct dcb_output *outp;
-	struct nvbios_dpout info;
-	u8 version;
-	struct nouveau_i2c_port *aux;
-	int head;
-	u8  dpcd[16];
+	struct nvkm_output_dp *outp;
 	int link_nr;
 	u32 link_bw;
 	u8  stat[6];
@@ -63,14 +52,16 @@
 static int
 dp_set_link_config(struct dp_state *dp)
 {
-	struct nouveau_disp *disp = dp->disp;
+	struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+	struct nvkm_output_dp *outp = dp->outp;
+	struct nouveau_disp *disp = nouveau_disp(outp);
 	struct nouveau_bios *bios = nouveau_bios(disp);
 	struct nvbios_init init = {
-		.subdev = nv_subdev(dp->disp),
+		.subdev = nv_subdev(disp),
 		.bios = bios,
 		.offset = 0x0000,
-		.outp = dp->outp,
-		.crtc = dp->head,
+		.outp = &outp->base.info,
+		.crtc = -1,
 		.execute = 1,
 	};
 	u32 lnkcmp;
@@ -80,8 +71,8 @@
 	DBG("%d lanes at %d KB/s\n", dp->link_nr, dp->link_bw);
 
 	/* set desired link configuration on the source */
-	if ((lnkcmp = dp->info.lnkcmp)) {
-		if (dp->version < 0x30) {
+	if ((lnkcmp = dp->outp->info.lnkcmp)) {
+		if (outp->version < 0x30) {
 			while ((dp->link_bw / 10) < nv_ro16(bios, lnkcmp))
 				lnkcmp += 4;
 			init.offset = nv_ro16(bios, lnkcmp + 2);
@@ -94,43 +85,45 @@
 		nvbios_exec(&init);
 	}
 
-	ret = dp->func->lnk_ctl(dp->disp, dp->outp, dp->head,
-				dp->link_nr, dp->link_bw / 27000,
-				dp->dpcd[DPCD_RC02] &
-					 DPCD_RC02_ENHANCED_FRAME_CAP);
+	ret = impl->lnk_ctl(outp, dp->link_nr, dp->link_bw / 27000,
+			    outp->dpcd[DPCD_RC02] &
+				       DPCD_RC02_ENHANCED_FRAME_CAP);
 	if (ret) {
-		ERR("lnk_ctl failed with %d\n", ret);
+		if (ret < 0)
+			ERR("lnk_ctl failed with %d\n", ret);
 		return ret;
 	}
 
 	/* set desired link configuration on the sink */
 	sink[0] = dp->link_bw / 27000;
 	sink[1] = dp->link_nr;
-	if (dp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
+	if (outp->dpcd[DPCD_RC02] & DPCD_RC02_ENHANCED_FRAME_CAP)
 		sink[1] |= DPCD_LC01_ENHANCED_FRAME_EN;
 
-	return nv_wraux(dp->aux, DPCD_LC00, sink, 2);
+	return nv_wraux(outp->base.edid, DPCD_LC00, sink, 2);
 }
 
 static void
 dp_set_training_pattern(struct dp_state *dp, u8 pattern)
 {
+	struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+	struct nvkm_output_dp *outp = dp->outp;
 	u8 sink_tp;
 
 	DBG("training pattern %d\n", pattern);
-	dp->func->pattern(dp->disp, dp->outp, dp->head, pattern);
+	impl->pattern(outp, pattern);
 
-	nv_rdaux(dp->aux, DPCD_LC02, &sink_tp, 1);
+	nv_rdaux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
 	sink_tp &= ~DPCD_LC02_TRAINING_PATTERN_SET;
 	sink_tp |= pattern;
-	nv_wraux(dp->aux, DPCD_LC02, &sink_tp, 1);
+	nv_wraux(outp->base.edid, DPCD_LC02, &sink_tp, 1);
 }
 
 static int
 dp_link_train_commit(struct dp_state *dp, bool pc)
 {
-	const struct nouveau_dp_func *func = dp->func;
-	struct nouveau_disp *disp = dp->disp;
+	struct nvkm_output_dp_impl *impl = (void *)nv_oclass(dp->outp);
+	struct nvkm_output_dp *outp = dp->outp;
 	int ret, i;
 
 	for (i = 0; i < dp->link_nr; i++) {
@@ -146,15 +139,15 @@
 		dp->pc2conf[i >> 1] |= 4 << ((i & 1) * 4);
 
 		DBG("config lane %d %02x\n", i, dp->conf[i]);
-		func->drv_ctl(disp, dp->outp, dp->head, i, lvsw, lpre);
+		impl->drv_ctl(outp, i, lvsw, lpre, 0);
 	}
 
-	ret = nv_wraux(dp->aux, DPCD_LC03(0), dp->conf, 4);
+	ret = nv_wraux(outp->base.edid, DPCD_LC03(0), dp->conf, 4);
 	if (ret)
 		return ret;
 
 	if (pc) {
-		ret = nv_wraux(dp->aux, DPCD_LC0F, dp->pc2conf, 2);
+		ret = nv_wraux(outp->base.edid, DPCD_LC0F, dp->pc2conf, 2);
 		if (ret)
 			return ret;
 	}
@@ -165,19 +158,20 @@
 static int
 dp_link_train_update(struct dp_state *dp, bool pc, u32 delay)
 {
+	struct nvkm_output_dp *outp = dp->outp;
 	int ret;
 
-	if (dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
-		mdelay(dp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
+	if (outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL])
+		mdelay(outp->dpcd[DPCD_RC0E_AUX_RD_INTERVAL] * 4);
 	else
 		udelay(delay);
 
-	ret = nv_rdaux(dp->aux, DPCD_LS02, dp->stat, 6);
+	ret = nv_rdaux(outp->base.edid, DPCD_LS02, dp->stat, 6);
 	if (ret)
 		return ret;
 
 	if (pc) {
-		ret = nv_rdaux(dp->aux, DPCD_LS0C, &dp->pc2stat, 1);
+		ret = nv_rdaux(outp->base.edid, DPCD_LS0C, &dp->pc2stat, 1);
 		if (ret)
 			dp->pc2stat = 0x00;
 		DBG("status %6ph pc2 %02x\n", dp->stat, dp->pc2stat);
@@ -225,10 +219,11 @@
 static int
 dp_link_train_eq(struct dp_state *dp)
 {
+	struct nvkm_output_dp *outp = dp->outp;
 	bool eq_done = false, cr_done = true;
 	int tries = 0, i;
 
-	if (dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
+	if (outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED)
 		dp_set_training_pattern(dp, 3);
 	else
 		dp_set_training_pattern(dp, 2);
@@ -257,39 +252,45 @@
 static void
 dp_link_train_init(struct dp_state *dp, bool spread)
 {
+	struct nvkm_output_dp *outp = dp->outp;
+	struct nouveau_disp *disp = nouveau_disp(outp);
+	struct nouveau_bios *bios = nouveau_bios(disp);
 	struct nvbios_init init = {
-		.subdev = nv_subdev(dp->disp),
-		.bios = nouveau_bios(dp->disp),
-		.outp = dp->outp,
-		.crtc = dp->head,
+		.subdev = nv_subdev(disp),
+		.bios = bios,
+		.outp = &outp->base.info,
+		.crtc = -1,
 		.execute = 1,
 	};
 
 	/* set desired spread */
 	if (spread)
-		init.offset = dp->info.script[2];
+		init.offset = outp->info.script[2];
 	else
-		init.offset = dp->info.script[3];
+		init.offset = outp->info.script[3];
 	nvbios_exec(&init);
 
 	/* pre-train script */
-	init.offset = dp->info.script[0];
+	init.offset = outp->info.script[0];
 	nvbios_exec(&init);
 }
 
 static void
 dp_link_train_fini(struct dp_state *dp)
 {
+	struct nvkm_output_dp *outp = dp->outp;
+	struct nouveau_disp *disp = nouveau_disp(outp);
+	struct nouveau_bios *bios = nouveau_bios(disp);
 	struct nvbios_init init = {
-		.subdev = nv_subdev(dp->disp),
-		.bios = nouveau_bios(dp->disp),
-		.outp = dp->outp,
-		.crtc = dp->head,
+		.subdev = nv_subdev(disp),
+		.bios = bios,
+		.outp = &outp->base.info,
+		.crtc = -1,
 		.execute = 1,
 	};
 
 	/* post-train script */
-	init.offset = dp->info.script[1],
+	init.offset = outp->info.script[1],
 	nvbios_exec(&init);
 }
 
@@ -311,65 +312,25 @@
 };
 
 int
-nouveau_dp_train(struct nouveau_disp *disp, const struct nouveau_dp_func *func,
-		 struct dcb_output *outp, int head, u32 datarate)
+nouveau_dp_train(struct nvkm_output_dp *outp, u32 datarate)
 {
-	struct nouveau_bios *bios = nouveau_bios(disp);
-	struct nouveau_i2c *i2c = nouveau_i2c(disp);
+	struct nouveau_disp *disp = nouveau_disp(outp);
 	const struct dp_rates *cfg = nouveau_dp_rates;
 	struct dp_state _dp = {
-		.disp = disp,
-		.func = func,
 		.outp = outp,
-		.head = head,
 	}, *dp = &_dp;
-	u8  hdr, cnt, len;
-	u32 data;
 	int ret;
 
-	/* find the bios displayport data relevant to this output */
-	data = nvbios_dpout_match(bios, outp->hasht, outp->hashm, &dp->version,
-				 &hdr, &cnt, &len, &dp->info);
-	if (!data) {
-		ERR("bios data not found\n");
-		return -EINVAL;
-	}
-
-	/* acquire the aux channel and fetch some info about the display */
-	if (outp->location)
-		dp->aux = i2c->find_type(i2c, NV_I2C_TYPE_EXTAUX(outp->extdev));
-	else
-		dp->aux = i2c->find(i2c, NV_I2C_TYPE_DCBI2C(outp->i2c_index));
-	if (!dp->aux) {
-		ERR("no aux channel?!\n");
-		return -ENODEV;
-	}
-
-	ret = nv_rdaux(dp->aux, 0x00000, dp->dpcd, sizeof(dp->dpcd));
-	if (ret) {
-		/* it's possible the display has been unplugged before we
-		 * get here.  we still need to execute the full set of
-		 * vbios scripts, and program the OR at a high enough
-		 * frequency to satisfy the target mode.  failure to do
-		 * so results at best in an UPDATE hanging, and at worst
-		 * with PDISP running away to join the circus.
-		 */
-		dp->dpcd[1] = dp->outp->dpconf.link_bw;
-		dp->dpcd[2] = dp->outp->dpconf.link_nr;
-		dp->dpcd[3] = 0x00;
-		ERR("failed to read DPCD\n");
-	}
-
 	/* bring capabilities within encoder limits */
 	if (nv_mclass(disp) < NVD0_DISP_CLASS)
-		dp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
-	if ((dp->dpcd[2] & 0x1f) > dp->outp->dpconf.link_nr) {
-		dp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
-		dp->dpcd[2] |= dp->outp->dpconf.link_nr;
+		outp->dpcd[2] &= ~DPCD_RC02_TPS3_SUPPORTED;
+	if ((outp->dpcd[2] & 0x1f) > outp->base.info.dpconf.link_nr) {
+		outp->dpcd[2] &= ~DPCD_RC02_MAX_LANE_COUNT;
+		outp->dpcd[2] |= outp->base.info.dpconf.link_nr;
 	}
-	if (dp->dpcd[1] > dp->outp->dpconf.link_bw)
-		dp->dpcd[1] = dp->outp->dpconf.link_bw;
-	dp->pc2 = dp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
+	if (outp->dpcd[1] > outp->base.info.dpconf.link_bw)
+		outp->dpcd[1] = outp->base.info.dpconf.link_bw;
+	dp->pc2 = outp->dpcd[2] & DPCD_RC02_TPS3_SUPPORTED;
 
 	/* restrict link config to the lowest required rate, if requested */
 	if (datarate) {
@@ -380,12 +341,12 @@
 	cfg--;
 
 	/* enable down-spreading and execute pre-train script from vbios */
-	dp_link_train_init(dp, dp->dpcd[3] & 0x01);
+	dp_link_train_init(dp, outp->dpcd[3] & 0x01);
 
 	while (ret = -EIO, (++cfg)->rate) {
 		/* select next configuration supported by encoder and sink */
-		while (cfg->nr > (dp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
-		       cfg->bw > (dp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
+		while (cfg->nr > (outp->dpcd[2] & DPCD_RC02_MAX_LANE_COUNT) ||
+		       cfg->bw > (outp->dpcd[DPCD_RC01_MAX_LINK_RATE]))
 			cfg++;
 		dp->link_bw = cfg->bw * 27000;
 		dp->link_nr = cfg->nr;