From c76f7c2f3f63d404875de52fda4cd7df1511735f Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Mon, 21 Nov 2016 13:06:54 -0800 Subject: [PATCH] msm: mdss: dp: add support for PHY compliance tests Add support for PHY compliance tests by parsing requests from the reference sink and generating the requested PHY test patterns from DP PHY. CRs-Fixed: 1076516 Change-Id: I290ec786bbe5c45873265ea74290eefcd3d16cb1 Signed-off-by: Tatenda Chipeperekwa --- drivers/video/fbdev/msm/mdss_dp.c | 63 ++++++++++++- drivers/video/fbdev/msm/mdss_dp.h | 55 ++++++++++- drivers/video/fbdev/msm/mdss_dp_aux.c | 126 +++++++++++++++++++++---- drivers/video/fbdev/msm/mdss_dp_util.c | 84 +++++++++++++++++ drivers/video/fbdev/msm/mdss_dp_util.h | 6 ++ 5 files changed, 314 insertions(+), 20 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index da3c8ca23fb6..394d31941c8a 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1351,9 +1351,16 @@ static inline bool mdss_dp_is_link_training_requested( 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; } @@ -2500,6 +2507,57 @@ static int mdss_dp_process_link_training_request(struct mdss_dp_drv_pdata *dp) return 0; } +/** + * 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 @@ -2548,6 +2606,9 @@ static int mdss_dp_process_hpd_irq_high(struct mdss_dp_drv_pdata *dp) 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); diff --git a/drivers/video/fbdev/msm/mdss_dp.h b/drivers/video/fbdev/msm/mdss_dp.h index c4494aa9c629..399ca61e0f46 100644 --- a/drivers/video/fbdev/msm/mdss_dp.h +++ b/drivers/video/fbdev/msm/mdss_dp.h @@ -268,6 +268,7 @@ struct dpcd_test_request { u32 test_requested; u32 test_link_rate; u32 test_lane_count; + u32 phy_test_pattern_sel; u32 response; }; @@ -509,6 +510,51 @@ enum dp_lane_count { 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, @@ -561,7 +607,7 @@ static inline char *mdss_dp_get_test_response(u32 test_response) 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), }; @@ -569,7 +615,7 @@ static inline char *mdss_dp_get_test_name(u32 test_requested) { 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"; } @@ -640,5 +686,10 @@ void *mdss_dp_get_hdcp_data(struct device *dev); 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 */ diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index b216506fe2a9..82588f681585 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -960,7 +960,7 @@ static void dp_sink_capability_read(struct mdss_dp_drv_pdata *ep, 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; @@ -1031,12 +1031,12 @@ void mdss_dp_aux_send_test_response(struct mdss_dp_drv_pdata *dp) } /** - * 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) || @@ -1044,12 +1044,12 @@ static bool dp_is_link_rate_valid(u32 link_rate) } /** - * 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) || @@ -1086,7 +1086,7 @@ static int dp_parse_link_training_params(struct mdss_dp_drv_pdata *ep) 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; @@ -1108,7 +1108,7 @@ static int dp_parse_link_training_params(struct mdss_dp_drv_pdata *ep) 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; @@ -1156,6 +1156,53 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep) ep->sink_count.count, ep->sink_count.cp_ready); } +/** + * 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 @@ -1165,7 +1212,8 @@ static void dp_sink_parse_sink_count(struct mdss_dp_drv_pdata *ep) 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); } /** @@ -1229,8 +1277,19 @@ static void dp_sink_parse_test_request(struct mdss_dp_drv_pdata *ep) 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 @@ -1453,7 +1512,8 @@ char vm_voltage_swing[4][4] = { {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; @@ -1488,6 +1548,38 @@ static void dp_voltage_pre_emphasise_set(struct mdss_dp_drv_pdata *dp) } +/** + * 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; @@ -1500,7 +1592,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) 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; @@ -1508,7 +1600,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) 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; @@ -1531,7 +1623,7 @@ static int dp_start_link_train_1(struct mdss_dp_drv_pdata *ep) } dp_sink_train_set_adjust(ep); - dp_voltage_pre_emphasise_set(ep); + dp_aux_set_voltage_and_pre_emphasis_lvl(ep); } return ret; @@ -1555,13 +1647,13 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) 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; @@ -1698,7 +1790,7 @@ void mdss_dp_aux_parse_sink_status_field(struct mdss_dp_drv_pdata *ep) { 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) @@ -1706,7 +1798,7 @@ 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; diff --git a/drivers/video/fbdev/msm/mdss_dp_util.c b/drivers/video/fbdev/msm/mdss_dp_util.c index b1bfe2c548ba..3b294a11f555 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.c +++ b/drivers/video/fbdev/msm/mdss_dp_util.c @@ -892,3 +892,87 @@ void mdss_dp_audio_enable(struct dss_io_data *ctrl_io, bool enable) 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; + } +} diff --git a/drivers/video/fbdev/msm/mdss_dp_util.h b/drivers/video/fbdev/msm/mdss_dp_util.h index 5e238d5be2b4..c046deef48a3 100644 --- a/drivers/video/fbdev/msm/mdss_dp_util.h +++ b/drivers/video/fbdev/msm/mdss_dp_util.h @@ -60,6 +60,11 @@ #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) @@ -315,5 +320,6 @@ void mdss_dp_audio_set_sample_rate(struct dss_io_data *ctrl_io, 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__ */