tegra124: video: Add full link training for eDP

Add full link training as a fallback in case the fast link training
fails.

Signed-off-by: Simon Glass <sjg@chromium.org>
Acked-by: Anatolij Gustschin <agust@denx.de>
Signed-off-by: Tom Warren <twarren@nvidia.com>
diff --git a/drivers/video/tegra124/sor.c b/drivers/video/tegra124/sor.c
index faeda4c..aa3d80c 100644
--- a/drivers/video/tegra124/sor.c
+++ b/drivers/video/tegra124/sor.c
@@ -57,6 +57,23 @@
 	tegra_sor_writel(sor, reg, reg_val);
 }
 
+void tegra_dp_disable_tx_pu(struct tegra_dc_sor_data *sor)
+{
+	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+			      DP_PADCTL_TX_PU_MASK, DP_PADCTL_TX_PU_DISABLE);
+}
+
+void tegra_dp_set_pe_vs_pc(struct tegra_dc_sor_data *sor, u32 mask, u32 pe_reg,
+			   u32 vs_reg, u32 pc_reg, u8 pc_supported)
+{
+	tegra_sor_write_field(sor, PR(sor->portnum), mask, pe_reg);
+	tegra_sor_write_field(sor, DC(sor->portnum), mask, vs_reg);
+	if (pc_supported) {
+		tegra_sor_write_field(sor, POSTCURSOR(sor->portnum), mask,
+				      pc_reg);
+	}
+}
+
 static int tegra_dc_sor_poll_register(struct tegra_dc_sor_data *sor, u32 reg,
 				      u32 mask, u32 exp_val,
 				      int poll_interval_us, int timeout_ms)
@@ -808,12 +825,36 @@
 	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum), 0xf0, 0x0);
 }
 
+int tegra_dc_sor_set_voltage_swing(struct tegra_dc_sor_data *sor,
+				    const struct tegra_dp_link_config *link_cfg)
+{
+	u32 drive_current = 0;
+	u32 pre_emphasis = 0;
+
+	/* Set to a known-good pre-calibrated setting */
+	switch (link_cfg->link_bw) {
+	case SOR_LINK_SPEED_G1_62:
+	case SOR_LINK_SPEED_G2_7:
+		drive_current = 0x13131313;
+		pre_emphasis = 0;
+		break;
+	case SOR_LINK_SPEED_G5_4:
+		debug("T124 does not support 5.4G link clock.\n");
+	default:
+		debug("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
+		return -ENOLINK;
+	}
+
+	tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum), drive_current);
+	tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
+
+	return 0;
+}
+
 void tegra_dc_sor_power_down_unused_lanes(struct tegra_dc_sor_data *sor,
 			const struct tegra_dp_link_config *link_cfg)
 {
 	u32 pad_ctrl = 0;
-	u32 drive_current = 0;
-	u32 pre_emphasis = 0;
 	int err = 0;
 
 	switch (link_cfg->lane_count) {
@@ -848,25 +889,112 @@
 		debug("Wait for lane power down failed: %d\n", err);
 		return;
 	}
+}
 
-	/* Set to a known-good pre-calibrated setting */
-	switch (link_cfg->link_bw) {
-	case SOR_LINK_SPEED_G1_62:
-	case SOR_LINK_SPEED_G2_7:
-		drive_current = 0x13131313;
-		pre_emphasis = 0;
+int tegra_sor_precharge_lanes(struct tegra_dc_sor_data *sor,
+			      const struct tegra_dp_link_config *cfg)
+{
+	u32 val = 0;
+
+	switch (cfg->lane_count) {
+	case 4:
+		val |= (DP_PADCTL_PD_TXD_3_NO |
+			DP_PADCTL_PD_TXD_2_NO);
+		/* fall through */
+	case 2:
+		val |= DP_PADCTL_PD_TXD_1_NO;
+		/* fall through */
+	case 1:
+		val |= DP_PADCTL_PD_TXD_0_NO;
 		break;
-	case SOR_LINK_SPEED_G5_4:
-		drive_current = 0x19191919;
-		pre_emphasis = 0x09090909;
 	default:
-		printf("Invalid sor link bandwidth: %d\n", link_cfg->link_bw);
-		return;
+		debug("dp: invalid lane number %d\n", cfg->lane_count);
+		return -EINVAL;
 	}
 
-	tegra_sor_writel(sor, LANE_DRIVE_CURRENT(sor->portnum),
-			 drive_current);
-	tegra_sor_writel(sor, PR(sor->portnum), pre_emphasis);
+	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+			      (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+			      (val << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT));
+	udelay(100);
+	tegra_sor_write_field(sor, DP_PADCTL(sor->portnum),
+			      (0xf << DP_PADCTL_COMODE_TXD_0_DP_TXD_2_SHIFT),
+			      0);
+
+	return 0;
+}
+
+static void tegra_dc_sor_enable_sor(struct dc_ctlr *disp_ctrl, bool enable)
+{
+	u32 reg_val = readl(&disp_ctrl->disp.disp_win_opt);
+
+	reg_val = enable ? reg_val | SOR_ENABLE : reg_val & ~SOR_ENABLE;
+	writel(reg_val, &disp_ctrl->disp.disp_win_opt);
+}
+
+int tegra_dc_sor_detach(struct tegra_dc_sor_data *sor)
+{
+	int dc_reg_ctx[DC_REG_SAVE_SPACE];
+	const void *blob = gd->fdt_blob;
+	struct dc_ctlr *disp_ctrl;
+	unsigned long dc_int_mask;
+	int node;
+	int ret;
+
+	debug("%s\n", __func__);
+	/* Use the first display controller */
+	node = fdtdec_next_compatible(blob, 0, COMPAT_NVIDIA_TEGRA124_DC);
+	if (node < 0) {
+		ret = -ENOENT;
+		goto err;
+	}
+	disp_ctrl = (struct dc_ctlr *)fdtdec_get_addr(blob, node, "reg");
+
+	/* Sleep mode */
+	tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+			 SUPER_STATE1_ASY_ORMODE_SAFE |
+			 SUPER_STATE1_ATTACHED_YES);
+	tegra_dc_sor_super_update(sor);
+
+	tegra_dc_sor_disable_win_short_raster(disp_ctrl, dc_reg_ctx);
+
+	if (tegra_dc_sor_poll_register(sor, TEST,
+				       TEST_ACT_HEAD_OPMODE_DEFAULT_MASK,
+				       TEST_ACT_HEAD_OPMODE_SLEEP, 100,
+				       TEGRA_SOR_ATTACH_TIMEOUT_MS)) {
+		debug("dc timeout waiting for OPMOD = SLEEP\n");
+		ret = -ETIMEDOUT;
+		goto err;
+	}
+
+	tegra_sor_writel(sor, SUPER_STATE1, SUPER_STATE1_ASY_HEAD_OP_SLEEP |
+			 SUPER_STATE1_ASY_ORMODE_SAFE |
+			 SUPER_STATE1_ATTACHED_NO);
+
+	/* Mask DC interrupts during the 2 dummy frames required for detach */
+	dc_int_mask = readl(&disp_ctrl->cmd.int_mask);
+	writel(0, &disp_ctrl->cmd.int_mask);
+
+	/* Stop DC->SOR path */
+	tegra_dc_sor_enable_sor(disp_ctrl, false);
+	ret = tegra_dc_sor_general_act(disp_ctrl);
+	if (ret)
+		goto err;
+
+	/* Stop DC */
+	writel(CTRL_MODE_STOP << CTRL_MODE_SHIFT, &disp_ctrl->cmd.disp_cmd);
+	ret = tegra_dc_sor_general_act(disp_ctrl);
+	if (ret)
+		goto err;
+
+	tegra_dc_sor_restore_win_and_raster(disp_ctrl, dc_reg_ctx);
+
+	writel(dc_int_mask, &disp_ctrl->cmd.int_mask);
+
+	return 0;
+err:
+	debug("%s: ret=%d\n", __func__, ret);
+
+	return ret;
 }
 
 int tegra_dc_sor_init(struct tegra_dc_sor_data **sorp)