return (dp->test_data.test_requested == TEST_LINK_TRAINING);
}
+static inline bool mdss_dp_is_phy_test_pattern_requested(
+ struct mdss_dp_drv_pdata *dp)
+{
+ return (dp->test_data.test_requested == PHY_TEST_PATTERN);
+}
+
static inline bool mdss_dp_soft_hpd_reset(struct mdss_dp_drv_pdata *dp)
{
- return mdss_dp_is_link_training_requested(dp) &&
+ return (mdss_dp_is_link_training_requested(dp) ||
+ mdss_dp_is_phy_test_pattern_requested(dp)) &&
dp->alt_mode.dp_status.hpd_irq;
}
}
/**
+ * mdss_dp_process_phy_test_pattern_request() - process new phy test requests
+ * @dp: Display Port Driver data
+ *
+ * This function will handle new phy test pattern requests that are initiated
+ * by the sink. The function will return 0 if a phy test pattern has been
+ * processed, otherwise it will return -EINVAL.
+ */
+static int mdss_dp_process_phy_test_pattern_request(
+ struct mdss_dp_drv_pdata *dp)
+{
+ u32 test_link_rate = 0, test_lane_count = 0;
+
+ if (!mdss_dp_is_phy_test_pattern_requested(dp))
+ return -EINVAL;
+
+ mdss_dp_send_test_response(dp);
+
+ test_link_rate = dp->test_data.test_link_rate;
+ test_lane_count = dp->test_data.test_lane_count;
+
+ pr_info("%s link rate = 0x%x, lane count = 0x%x\n",
+ mdss_dp_get_test_name(TEST_LINK_TRAINING),
+ test_link_rate, test_lane_count);
+
+ /**
+ * Retrain the mainlink if there is a change in link rate or lane
+ * count.
+ */
+ if (mdss_dp_aux_is_link_rate_valid(test_link_rate) &&
+ mdss_dp_aux_is_lane_count_valid(test_lane_count) &&
+ ((dp->dpcd.max_lane_count != test_lane_count) ||
+ (dp->link_rate != test_link_rate))) {
+
+ pr_info("updated link rate or lane count, retraining.\n");
+
+ dp->dpcd.max_lane_count = dp->test_data.test_lane_count;
+ dp->link_rate = dp->test_data.test_link_rate;
+
+ mdss_dp_link_retraining(dp);
+ }
+
+ mdss_dp_config_ctrl(dp);
+
+ mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(dp);
+
+ mdss_dp_phy_send_test_pattern(dp);
+
+ return 0;
+}
+
+/**
* mdss_dp_process_downstream_port_status_change() - process port status changes
* @dp: Display Port Driver data
*
if (!ret)
goto exit;
+ ret = mdss_dp_process_phy_test_pattern_request(dp);
+ if (!ret)
+ goto exit;
pr_debug("done\n");
exit:
mdss_dp_reset_test_data(dp);
u32 test_requested;
u32 test_link_rate;
u32 test_lane_count;
+ u32 phy_test_pattern_sel;
u32 response;
};
DP_LANE_COUNT_4 = 4,
};
+enum phy_test_pattern {
+ PHY_TEST_PATTERN_NONE,
+ PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING,
+ PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT,
+ PHY_TEST_PATTERN_PRBS7,
+ PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN,
+ PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN,
+};
+
+static inline char *mdss_dp_get_phy_test_pattern(u32 phy_test_pattern_sel)
+{
+ switch (phy_test_pattern_sel) {
+ case PHY_TEST_PATTERN_NONE:
+ return DP_ENUM_STR(PHY_TEST_PATTERN_NONE);
+ case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
+ return DP_ENUM_STR(PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING);
+ case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+ return DP_ENUM_STR(PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT);
+ case PHY_TEST_PATTERN_PRBS7:
+ return DP_ENUM_STR(PHY_TEST_PATTERN_PRBS7);
+ case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
+ return DP_ENUM_STR(PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN);
+ case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
+ return DP_ENUM_STR(PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN);
+ default:
+ return "unknown";
+ }
+}
+
+static inline bool mdss_dp_is_phy_test_pattern_supported(
+ u32 phy_test_pattern_sel)
+{
+ switch (phy_test_pattern_sel) {
+ case PHY_TEST_PATTERN_NONE:
+ case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
+ case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+ case PHY_TEST_PATTERN_PRBS7:
+ case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
+ case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
+ return true;
+ default:
+ return false;
+ }
+}
+
enum dp_aux_error {
EDP_AUX_ERR_NONE = 0,
EDP_AUX_ERR_ADDR = -1,
enum test_type {
UNKNOWN_TEST = 0,
TEST_LINK_TRAINING = BIT(0),
- TEST_PATTERN = BIT(1),
+ PHY_TEST_PATTERN = BIT(3),
TEST_EDID_READ = BIT(2),
};
{
switch (test_requested) {
case TEST_LINK_TRAINING: return DP_ENUM_STR(TEST_LINK_TRAINING);
- case TEST_PATTERN: return DP_ENUM_STR(TEST_PATTERN);
+ case PHY_TEST_PATTERN: return DP_ENUM_STR(PHY_TEST_PATTERN);
case TEST_EDID_READ: return DP_ENUM_STR(TEST_EDID_READ);
default: return "unknown";
}
int mdss_dp_hdcp2p2_init(struct mdss_dp_drv_pdata *dp_drv);
bool mdss_dp_aux_clock_recovery_done(struct mdss_dp_drv_pdata *ep);
bool mdss_dp_aux_channel_eq_done(struct mdss_dp_drv_pdata *ep);
+bool mdss_dp_aux_is_link_rate_valid(u32 link_rate);
+bool mdss_dp_aux_is_lane_count_valid(u32 lane_count);
+int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len);
+void mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(
+ struct mdss_dp_drv_pdata *dp);
#endif /* MDSS_DP_H */
dp_sink_parse_sink_count(ep);
}
-static int dp_link_status_read(struct mdss_dp_drv_pdata *ep, int len)
+int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len)
{
char *bp;
char data;
}
/**
- * dp_is_link_rate_valid() - validates the link rate
+ * mdss_dp_aux_is_link_rate_valid() - validates the link rate
* @lane_rate: link rate requested by the sink
*
* Returns true if the requested link rate is supported.
*/
-static bool dp_is_link_rate_valid(u32 link_rate)
+bool mdss_dp_aux_is_link_rate_valid(u32 link_rate)
{
return (link_rate == DP_LINK_RATE_162) ||
(link_rate == DP_LINK_RATE_270) ||
}
/**
- * dp_is_lane_count_valid() - validates the lane count
+ * mdss_dp_aux_is_lane_count_valid() - validates the lane count
* @lane_count: lane count requested by the sink
*
* Returns true if the requested lane count is supported.
*/
-static bool dp_is_lane_count_valid(u32 lane_count)
+bool mdss_dp_aux_is_lane_count_valid(u32 lane_count)
{
return (lane_count == DP_LANE_COUNT_1) ||
(lane_count == DP_LANE_COUNT_2) ||
bp = rp->data;
data = *bp++;
- if (!dp_is_link_rate_valid(data)) {
+ if (!mdss_dp_aux_is_link_rate_valid(data)) {
pr_err("invalid link rate = 0x%x\n", data);
ret = -EINVAL;
goto exit;
data = *bp++;
data &= 0x1F;
- if (!dp_is_lane_count_valid(data)) {
+ if (!mdss_dp_aux_is_lane_count_valid(data)) {
pr_err("invalid lane count = 0x%x\n", data);
ret = -EINVAL;
goto exit;
}
/**
+ * dp_parse_phy_test_params() - parses the phy test parameters
+ * @ep: Display Port Driver data
+ *
+ * Parses the DPCD (Byte 0x248) for the DP PHY test pattern that is being
+ * requested.
+ */
+static int dp_parse_phy_test_params(struct mdss_dp_drv_pdata *ep)
+{
+ char *bp;
+ char data;
+ struct edp_buf *rp;
+ int rlen;
+ int const param_len = 0x1;
+ int const phy_test_pattern_addr = 0x248;
+ int const dpcd_version_1_2 = 0x12;
+ int ret = 0;
+
+ rlen = dp_aux_read_buf(ep, phy_test_pattern_addr, param_len, 0);
+ if (rlen < param_len) {
+ pr_err("failed to read phy test pattern\n");
+ ret = -EINVAL;
+ goto end;
+ }
+
+ rp = &ep->rxp;
+ bp = rp->data;
+ data = *bp++;
+
+ if (ep->dpcd.major == dpcd_version_1_2)
+ data = data & 0x7;
+ else
+ data = data & 0x3;
+
+ ep->test_data.phy_test_pattern_sel = data;
+
+ pr_debug("phy_test_pattern_sel = %s\n",
+ mdss_dp_get_phy_test_pattern(data));
+
+ if (!mdss_dp_is_phy_test_pattern_supported(data))
+ ret = -EINVAL;
+
+end:
+ return ret;
+}
+
+
+/**
* dp_is_test_supported() - checks if test requested by sink is supported
* @test_requested: test requested by the sink
*
static bool dp_is_test_supported(u32 test_requested)
{
return (test_requested == TEST_LINK_TRAINING) ||
- (test_requested == TEST_EDID_READ);
+ (test_requested == TEST_EDID_READ) ||
+ (test_requested == PHY_TEST_PATTERN);
}
/**
pr_debug("%s requested\n", mdss_dp_get_test_name(data));
ep->test_data.test_requested = data;
- if (ep->test_data.test_requested == TEST_LINK_TRAINING)
+ switch (ep->test_data.test_requested) {
+ case PHY_TEST_PATTERN:
+ ret = dp_parse_phy_test_params(ep);
+ if (ret)
+ break;
+ case TEST_LINK_TRAINING:
ret = dp_parse_link_training_params(ep);
+ break;
+ default:
+ pr_debug("test 0x%x not supported\n",
+ ep->test_data.test_requested);
+ return;
+ }
/**
* Send a TEST_ACK if all test parameters are valid, otherwise send
{0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};
-static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *dp)
+static void dp_aux_set_voltage_and_pre_emphasis_lvl(
+ struct mdss_dp_drv_pdata *dp)
{
u32 value0 = 0;
u32 value1 = 0;
}
+/**
+ * mdss_dp_aux_update_voltage_and_pre_emphasis_lvl() - updates DP PHY settings
+ * @ep: Display Port Driver data
+ *
+ * Updates the DP PHY with the requested voltage swing and pre-emphasis
+ * levels if they are different from the current settings.
+ */
+void mdss_dp_aux_update_voltage_and_pre_emphasis_lvl(
+ struct mdss_dp_drv_pdata *dp)
+{
+ int const num_bytes = 6;
+ struct dpcd_link_status *status = &dp->link_status;
+
+ /* Read link status for updated voltage and pre-emphasis levels. */
+ mdss_dp_aux_link_status_read(dp, num_bytes);
+
+ pr_info("Current: v_level = %d, p_level = %d\n",
+ dp->v_level, dp->p_level);
+ pr_info("Requested: v_level = %d, p_level = %d\n",
+ status->req_voltage_swing[0],
+ status->req_pre_emphasis[0]);
+
+ if ((status->req_voltage_swing[0] != dp->v_level) ||
+ (status->req_pre_emphasis[0] != dp->p_level)) {
+ dp->v_level = status->req_voltage_swing[0];
+ dp->p_level = status->req_pre_emphasis[0];
+
+ dp_aux_set_voltage_and_pre_emphasis_lvl(dp);
+ }
+
+ pr_debug("end\n");
+}
static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep)
{
int tries, old_v_level;
dp_host_train_set(ep, 0x01); /* train_1 */
dp_cap_lane_rate_set(ep);
dp_train_pattern_set_write(ep, 0x21); /* train_1 */
- dp_voltage_pre_emphasise_set(ep);
+ dp_aux_set_voltage_and_pre_emphasis_lvl(ep);
tries = 0;
old_v_level = ep->v_level;
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
- dp_link_status_read(ep, 6);
+ mdss_dp_aux_link_status_read(ep, 6);
if (mdss_dp_aux_clock_recovery_done(ep)) {
ret = 0;
break;
}
dp_sink_train_set_adjust(ep);
- dp_voltage_pre_emphasise_set(ep);
+ dp_aux_set_voltage_and_pre_emphasis_lvl(ep);
}
return ret;
dp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
do {
- dp_voltage_pre_emphasise_set(ep);
+ dp_aux_set_voltage_and_pre_emphasis_lvl(ep);
dp_host_train_set(ep, pattern);
usleep_time = ep->dpcd.training_read_interval;
usleep_range(usleep_time, usleep_time);
- dp_link_status_read(ep, 6);
+ mdss_dp_aux_link_status_read(ep, 6);
if (mdss_dp_aux_channel_eq_done(ep)) {
ret = 0;
{
dp_sink_parse_sink_count(ep);
dp_sink_parse_test_request(ep);
- dp_link_status_read(ep, 6);
+ mdss_dp_aux_link_status_read(ep, 6);
}
int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep)
struct dpcd_link_status *sp;
int ret = 0; /* not sync */
- ret = dp_link_status_read(ep, 6);
+ ret = mdss_dp_aux_link_status_read(ep, 6);
if (ret) {
sp = &ep->link_status;
writel_relaxed(audio_ctrl, ctrl_io->base + MMSS_DP_AUDIO_CFG);
}
+
+/**
+ * mdss_dp_phy_send_test_pattern() - sends the requested PHY test pattern
+ * @ep: Display Port Driver data
+ *
+ * Updates the DP controller state and sends the requested PHY test pattern
+ * to the sink.
+ */
+void mdss_dp_phy_send_test_pattern(struct mdss_dp_drv_pdata *dp)
+{
+ struct dss_io_data *io = &dp->ctrl_io;
+ u32 phy_test_pattern_sel = dp->test_data.phy_test_pattern_sel;
+ u32 value = 0x0;
+
+ if (!mdss_dp_is_phy_test_pattern_supported(phy_test_pattern_sel)) {
+ pr_err("test pattern 0x%x not supported\n",
+ phy_test_pattern_sel);
+ return;
+ }
+
+ /* Disable mainlink */
+ writel_relaxed(0x0, io->base + DP_MAINLINK_CTRL);
+
+ /* Reset mainlink */
+ mdss_dp_mainlink_reset(io);
+
+ /* Enable mainlink */
+ writel_relaxed(0x0, io->base + DP_MAINLINK_CTRL);
+
+ /* Initialize DP state control */
+ mdss_dp_state_ctrl(io, 0x00);
+
+ pr_debug("phy_test_pattern_sel = %s\n",
+ mdss_dp_get_phy_test_pattern(phy_test_pattern_sel));
+
+ switch (phy_test_pattern_sel) {
+ case PHY_TEST_PATTERN_D10_2_NO_SCRAMBLING:
+ mdss_dp_state_ctrl(io, BIT(0));
+ break;
+ case PHY_TEST_PATTERN_SYMBOL_ERR_MEASUREMENT_CNT:
+ value = readl_relaxed(io->base +
+ DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
+ value &= ~(1 << 16);
+ writel_relaxed(value, io->base +
+ DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
+ value |= 0xFC;
+ writel_relaxed(value, io->base +
+ DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
+ writel_relaxed(0x2, io->base + DP_MAINLINK_LEVELS);
+ mdss_dp_state_ctrl(io, BIT(4));
+ break;
+ case PHY_TEST_PATTERN_PRBS7:
+ mdss_dp_state_ctrl(io, BIT(5));
+ break;
+ case PHY_TEST_PATTERN_80_BIT_CUSTOM_PATTERN:
+ mdss_dp_state_ctrl(io, BIT(6));
+ /* 00111110000011111000001111100000 */
+ writel_relaxed(0x3E0F83E0, io->base +
+ DP_TEST_80BIT_CUSTOM_PATTERN_REG0);
+ /* 00001111100000111110000011111000 */
+ writel_relaxed(0x0F83E0F8, io->base +
+ DP_TEST_80BIT_CUSTOM_PATTERN_REG1);
+ /* 1111100000111110 */
+ writel_relaxed(0x0000F83E, io->base +
+ DP_TEST_80BIT_CUSTOM_PATTERN_REG2);
+ break;
+ case PHY_TEST_PATTERN_HBR2_CTS_EYE_PATTERN:
+ value = readl_relaxed(io->base +
+ DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
+ value |= BIT(16);
+ writel_relaxed(value, io->base +
+ DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
+ value |= 0xFC;
+ writel_relaxed(value, io->base +
+ DP_HBR2_COMPLIANCE_SCRAMBLER_RESET);
+ writel_relaxed(0x2, io->base + DP_MAINLINK_LEVELS);
+ mdss_dp_state_ctrl(io, BIT(4));
+ break;
+ default:
+ pr_debug("No valid test pattern requested: 0x%x\n",
+ phy_test_pattern_sel);
+ return;
+ }
+}
#define DP_MAINLINK_LEVELS (0x00000444)
#define DP_TU (0x0000044C)
+#define DP_HBR2_COMPLIANCE_SCRAMBLER_RESET (0x00000454)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG0 (0x000004C0)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG1 (0x000004C4)
+#define DP_TEST_80BIT_CUSTOM_PATTERN_REG2 (0x000004C8)
+
#define MMSS_DP_AUDIO_TIMING_GEN (0x00000480)
#define MMSS_DP_AUDIO_TIMING_RBR_32 (0x00000484)
#define MMSS_DP_AUDIO_TIMING_HBR_32 (0x00000488)
void mdss_dp_set_safe_to_exit_level(struct dss_io_data *ctrl_io,
uint32_t lane_cnt);
int mdss_dp_aux_read_rx_status(struct mdss_dp_drv_pdata *dp, u8 *rx_status);
+void mdss_dp_phy_send_test_pattern(struct mdss_dp_drv_pdata *dp);
#endif /* __DP_UTIL_H__ */