Commit cef622d7 authored by Joonas Lahtinen's avatar Joonas Lahtinen
Browse files

Merge tag 'topic/phy-compliance-2020-04-08' of...

Merge tag 'topic/phy-compliance-2020-04-08' of git://anongit.freedesktop.org/drm/drm-misc into drm-intel-next-queued

Topic pull request for topic/phy-compliance:
- Standardize DP_PHY_TEST_PATTERN name.
- Add support for setting/getting test pattern from sink.
- Implement DP PHY compliance to i915.

From: Maarten Lankhorst <maarten.lankhorst@linux.intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/efb3d0d9-2cf7-046b-3a9b-2548d086258e@linux.intel.com


Signed-off-by: default avatarJoonas Lahtinen <joonas.lahtinen@linux.intel.com>
parents 2b703bbd 8cdf7271
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2530,7 +2530,7 @@ static void dp_test_send_phy_test_pattern(struct dc_link *link)
	/* get phy test pattern and pattern parameters from DP receiver */
	core_link_read_dpcd(
			link,
			DP_TEST_PHY_PATTERN,
			DP_PHY_TEST_PATTERN,
			&dpcd_test_pattern.raw,
			sizeof(dpcd_test_pattern));
	core_link_read_dpcd(
+94 −0
Original line number Diff line number Diff line
@@ -1535,3 +1535,97 @@ int drm_dp_dsc_sink_supported_input_bpcs(const u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_S
	return num_bpc;
}
EXPORT_SYMBOL(drm_dp_dsc_sink_supported_input_bpcs);

/**
 * drm_dp_get_phy_test_pattern() - get the requested pattern from the sink.
 * @aux: DisplayPort AUX channel
 * @data: DP phy compliance test parameters.
 *
 * Returns 0 on success or a negative error code on failure.
 */
int drm_dp_get_phy_test_pattern(struct drm_dp_aux *aux,
				struct drm_dp_phy_test_params *data)
{
	int err;
	u8 rate, lanes;

	err = drm_dp_dpcd_readb(aux, DP_TEST_LINK_RATE, &rate);
	if (err < 0)
		return err;
	data->link_rate = drm_dp_bw_code_to_link_rate(rate);

	err = drm_dp_dpcd_readb(aux, DP_TEST_LANE_COUNT, &lanes);
	if (err < 0)
		return err;
	data->num_lanes = lanes & DP_MAX_LANE_COUNT_MASK;

	if (lanes & DP_ENHANCED_FRAME_CAP)
		data->enhanced_frame_cap = true;

	err = drm_dp_dpcd_readb(aux, DP_PHY_TEST_PATTERN, &data->phy_pattern);
	if (err < 0)
		return err;

	switch (data->phy_pattern) {
	case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
		err = drm_dp_dpcd_read(aux, DP_TEST_80BIT_CUSTOM_PATTERN_7_0,
				       &data->custom80, sizeof(data->custom80));
		if (err < 0)
			return err;

		break;
	case DP_PHY_TEST_PATTERN_CP2520:
		err = drm_dp_dpcd_read(aux, DP_TEST_HBR2_SCRAMBLER_RESET,
				       &data->hbr2_reset,
				       sizeof(data->hbr2_reset));
		if (err < 0)
			return err;
	}

	return 0;
}
EXPORT_SYMBOL(drm_dp_get_phy_test_pattern);

/**
 * drm_dp_set_phy_test_pattern() - set the pattern to the sink.
 * @aux: DisplayPort AUX channel
 * @data: DP phy compliance test parameters.
 *
 * Returns 0 on success or a negative error code on failure.
 */
int drm_dp_set_phy_test_pattern(struct drm_dp_aux *aux,
				struct drm_dp_phy_test_params *data, u8 dp_rev)
{
	int err, i;
	u8 link_config[2];
	u8 test_pattern;

	link_config[0] = drm_dp_link_rate_to_bw_code(data->link_rate);
	link_config[1] = data->num_lanes;
	if (data->enhanced_frame_cap)
		link_config[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
	err = drm_dp_dpcd_write(aux, DP_LINK_BW_SET, link_config, 2);
	if (err < 0)
		return err;

	test_pattern = data->phy_pattern;
	if (dp_rev < 0x12) {
		test_pattern = (test_pattern << 2) &
			       DP_LINK_QUAL_PATTERN_11_MASK;
		err = drm_dp_dpcd_writeb(aux, DP_TRAINING_PATTERN_SET,
					 test_pattern);
		if (err < 0)
			return err;
	} else {
		for (i = 0; i < data->num_lanes; i++) {
			err = drm_dp_dpcd_writeb(aux,
						 DP_LINK_QUAL_LANE0_SET + i,
						 test_pattern);
			if (err < 0)
				return err;
		}
	}

	return 0;
}
EXPORT_SYMBOL(drm_dp_set_phy_test_pattern);
+11 −1
Original line number Diff line number Diff line
@@ -1326,6 +1326,16 @@ static int i915_displayport_test_data_show(struct seq_file *m, void *data)
					   intel_dp->compliance.test_data.vdisplay);
				seq_printf(m, "bpc: %u\n",
					   intel_dp->compliance.test_data.bpc);
			} else if (intel_dp->compliance.test_type ==
				   DP_TEST_LINK_PHY_TEST_PATTERN) {
				seq_printf(m, "pattern: %d\n",
					   intel_dp->compliance.test_data.phytest.phy_pattern);
				seq_printf(m, "Number of lanes: %d\n",
					   intel_dp->compliance.test_data.phytest.num_lanes);
				seq_printf(m, "Link Rate: %d\n",
					   intel_dp->compliance.test_data.phytest.link_rate);
				seq_printf(m, "level: %02x\n",
					   intel_dp->train_set[0]);
			}
		} else
			seq_puts(m, "0");
@@ -1358,7 +1368,7 @@ static int i915_displayport_test_type_show(struct seq_file *m, void *data)

		if (encoder && connector->status == connector_status_connected) {
			intel_dp = enc_to_intel_dp(encoder);
			seq_printf(m, "%02lx", intel_dp->compliance.test_type);
			seq_printf(m, "%02lx\n", intel_dp->compliance.test_type);
		} else
			seq_puts(m, "0");
	}
+1 −0
Original line number Diff line number Diff line
@@ -1258,6 +1258,7 @@ struct intel_dp_compliance_data {
	u8 video_pattern;
	u16 hdisplay, vdisplay;
	u8 bpc;
	struct drm_dp_phy_test_params phytest;
};

struct intel_dp_compliance {
+171 −0
Original line number Diff line number Diff line
@@ -5401,9 +5401,180 @@ static u8 intel_dp_autotest_edid(struct intel_dp *intel_dp)
	return test_result;
}

static u8 intel_dp_prepare_phytest(struct intel_dp *intel_dp)
{
	struct drm_dp_phy_test_params *data =
		&intel_dp->compliance.test_data.phytest;

	if (drm_dp_get_phy_test_pattern(&intel_dp->aux, data)) {
		DRM_DEBUG_KMS("DP Phy Test pattern AUX read failure\n");
		return DP_TEST_NAK;
	}

	/*
	 * link_mst is set to false to avoid executing mst related code
	 * during compliance testing.
	 */
	intel_dp->link_mst = false;

	return DP_TEST_ACK;
}

static void intel_dp_phy_pattern_update(struct intel_dp *intel_dp)
{
	struct drm_i915_private *dev_priv =
			to_i915(dp_to_dig_port(intel_dp)->base.base.dev);
	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
	struct drm_dp_phy_test_params *data =
			&intel_dp->compliance.test_data.phytest;
	struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc);
	enum pipe pipe = crtc->pipe;
	u32 pattern_val;

	switch (data->phy_pattern) {
	case DP_PHY_TEST_PATTERN_NONE:
		DRM_DEBUG_KMS("Disable Phy Test Pattern\n");
		intel_de_write(dev_priv, DDI_DP_COMP_CTL(pipe), 0x0);
		break;
	case DP_PHY_TEST_PATTERN_D10_2:
		DRM_DEBUG_KMS("Set D10.2 Phy Test Pattern\n");
		intel_de_write(dev_priv, DDI_DP_COMP_CTL(pipe),
			       DDI_DP_COMP_CTL_ENABLE | DDI_DP_COMP_CTL_D10_2);
		break;
	case DP_PHY_TEST_PATTERN_ERROR_COUNT:
		DRM_DEBUG_KMS("Set Error Count Phy Test Pattern\n");
		intel_de_write(dev_priv, DDI_DP_COMP_CTL(pipe),
			       DDI_DP_COMP_CTL_ENABLE |
			       DDI_DP_COMP_CTL_SCRAMBLED_0);
		break;
	case DP_PHY_TEST_PATTERN_PRBS7:
		DRM_DEBUG_KMS("Set PRBS7 Phy Test Pattern\n");
		intel_de_write(dev_priv, DDI_DP_COMP_CTL(pipe),
			       DDI_DP_COMP_CTL_ENABLE | DDI_DP_COMP_CTL_PRBS7);
		break;
	case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
		/*
		 * FIXME: Ideally pattern should come from DPCD 0x250. As
		 * current firmware of DPR-100 could not set it, so hardcoding
		 * now for complaince test.
		 */
		DRM_DEBUG_KMS("Set 80Bit Custom Phy Test Pattern 0x3e0f83e0 0x0f83e0f8 0x0000f83e\n");
		pattern_val = 0x3e0f83e0;
		intel_de_write(dev_priv, DDI_DP_COMP_PAT(pipe, 0), pattern_val);
		pattern_val = 0x0f83e0f8;
		intel_de_write(dev_priv, DDI_DP_COMP_PAT(pipe, 1), pattern_val);
		pattern_val = 0x0000f83e;
		intel_de_write(dev_priv, DDI_DP_COMP_PAT(pipe, 2), pattern_val);
		intel_de_write(dev_priv, DDI_DP_COMP_CTL(pipe),
			       DDI_DP_COMP_CTL_ENABLE |
			       DDI_DP_COMP_CTL_CUSTOM80);
		break;
	case DP_PHY_TEST_PATTERN_CP2520:
		/*
		 * FIXME: Ideally pattern should come from DPCD 0x24A. As
		 * current firmware of DPR-100 could not set it, so hardcoding
		 * now for complaince test.
		 */
		DRM_DEBUG_KMS("Set HBR2 compliance Phy Test Pattern\n");
		pattern_val = 0xFB;
		intel_de_write(dev_priv, DDI_DP_COMP_CTL(pipe),
			       DDI_DP_COMP_CTL_ENABLE | DDI_DP_COMP_CTL_HBR2 |
			       pattern_val);
		break;
	default:
		WARN(1, "Invalid Phy Test Pattern\n");
	}
}

static void
intel_dp_autotest_phy_ddi_disable(struct intel_dp *intel_dp)
{
	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
	struct drm_device *dev = intel_dig_port->base.base.dev;
	struct drm_i915_private *dev_priv = to_i915(dev);
	struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc);
	enum pipe pipe = crtc->pipe;
	u32 trans_ddi_func_ctl_value, trans_conf_value, dp_tp_ctl_value;

	trans_ddi_func_ctl_value = intel_de_read(dev_priv,
						 TRANS_DDI_FUNC_CTL(pipe));
	trans_conf_value = intel_de_read(dev_priv, PIPECONF(pipe));
	dp_tp_ctl_value = intel_de_read(dev_priv, TGL_DP_TP_CTL(pipe));

	trans_ddi_func_ctl_value &= ~(TRANS_DDI_FUNC_ENABLE |
				      TGL_TRANS_DDI_PORT_MASK);
	trans_conf_value &= ~PIPECONF_ENABLE;
	dp_tp_ctl_value &= ~DP_TP_CTL_ENABLE;

	intel_de_write(dev_priv, PIPECONF(pipe), trans_conf_value);
	intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(pipe),
		       trans_ddi_func_ctl_value);
	intel_de_write(dev_priv, TGL_DP_TP_CTL(pipe), dp_tp_ctl_value);
}

static void
intel_dp_autotest_phy_ddi_enable(struct intel_dp *intel_dp, uint8_t lane_cnt)
{
	struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp);
	struct drm_device *dev = intel_dig_port->base.base.dev;
	struct drm_i915_private *dev_priv = to_i915(dev);
	enum port port = intel_dig_port->base.port;
	struct intel_crtc *crtc = to_intel_crtc(intel_dig_port->base.base.crtc);
	enum pipe pipe = crtc->pipe;
	u32 trans_ddi_func_ctl_value, trans_conf_value, dp_tp_ctl_value;

	trans_ddi_func_ctl_value = intel_de_read(dev_priv,
						 TRANS_DDI_FUNC_CTL(pipe));
	trans_conf_value = intel_de_read(dev_priv, PIPECONF(pipe));
	dp_tp_ctl_value = intel_de_read(dev_priv, TGL_DP_TP_CTL(pipe));

	trans_ddi_func_ctl_value |= TRANS_DDI_FUNC_ENABLE |
				    TGL_TRANS_DDI_SELECT_PORT(port);
	trans_conf_value |= PIPECONF_ENABLE;
	dp_tp_ctl_value |= DP_TP_CTL_ENABLE;

	intel_de_write(dev_priv, PIPECONF(pipe), trans_conf_value);
	intel_de_write(dev_priv, TGL_DP_TP_CTL(pipe), dp_tp_ctl_value);
	intel_de_write(dev_priv, TRANS_DDI_FUNC_CTL(pipe),
		       trans_ddi_func_ctl_value);
}

void intel_dp_process_phy_request(struct intel_dp *intel_dp)
{
	struct drm_dp_phy_test_params *data =
		&intel_dp->compliance.test_data.phytest;
	u8 link_status[DP_LINK_STATUS_SIZE];

	if (!intel_dp_get_link_status(intel_dp, link_status)) {
		DRM_DEBUG_KMS("failed to get link status\n");
		return;
	}

	/* retrieve vswing & pre-emphasis setting */
	intel_dp_get_adjust_train(intel_dp, link_status);

	intel_dp_autotest_phy_ddi_disable(intel_dp);

	intel_dp_set_signal_levels(intel_dp);

	intel_dp_phy_pattern_update(intel_dp);

	intel_dp_autotest_phy_ddi_enable(intel_dp, data->num_lanes);

	drm_dp_set_phy_test_pattern(&intel_dp->aux, data,
				    link_status[DP_DPCD_REV]);
}

static u8 intel_dp_autotest_phy_pattern(struct intel_dp *intel_dp)
{
	u8 test_result = DP_TEST_NAK;

	test_result = intel_dp_prepare_phytest(intel_dp);
	if (test_result != DP_TEST_ACK)
		DRM_ERROR("Phy test preparation failed\n");

	intel_dp_process_phy_request(intel_dp);

	return test_result;
}

Loading