From c76f7c2f3f63d404875de52fda4cd7df1511735f Mon Sep 17 00:00:00 2001 From: Tatenda Chipeperekwa Date: Mon, 21 Nov 2016 13:06:54 -0800 Subject: [PATCH 1/3] 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__ */ From c23be78397bd1a6efa8ee1bdc5abd435e365c019 Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Thu, 15 Dec 2016 17:36:54 -0800 Subject: [PATCH 2/3] msm: mdss: dp: fix calculation of link rate If the calculated link rate based on sink's capabilities exceeds the maximum supported link rate, do not error out. Instead, cap the link rate at the maximum supported rate. This fixes instability issues seen when connecting to sinks at 4K resolution. Change-Id: I214bb19385f855af61da628fdf1cf7efc5dd08d6 Signed-off-by: Aravind Venkateswaran --- drivers/video/fbdev/msm/mdss_dp_aux.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 82588f681585..b8be110f04f0 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -537,8 +537,9 @@ char mdss_dp_gen_link_clk(struct mdss_panel_info *pinfo, char lane_cnt) else if (min_link_rate <= DP_LINK_RATE_540) calc_link_rate = DP_LINK_RATE_540; else { - pr_err("link_rate = %d is unsupported\n", min_link_rate); - calc_link_rate = 0; + /* Cap the link rate to the max supported rate */ + pr_debug("link_rate = %d is unsupported\n", min_link_rate); + calc_link_rate = DP_LINK_RATE_540; } return calc_link_rate; From 5f41a670d14345da8ba300ab6b8029bdd06344f2 Mon Sep 17 00:00:00 2001 From: Aravind Venkateswaran Date: Thu, 15 Dec 2016 17:41:04 -0800 Subject: [PATCH 3/3] msm: mdss: dp: fix handling of link training mutex Remove the additional unbalanced unlock being called for the link training mutex. This fixes random crashes seen while running Display Port connection/disconnection tests. Change-Id: I2fce80cec72e3bd8b1561fd46fa1a1520cddd294 Signed-off-by: Aravind Venkateswaran --- drivers/video/fbdev/msm/mdss_dp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index 394d31941c8a..7076b36bb6b6 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1211,16 +1211,20 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) ret = mdss_dp_get_lane_mapping(dp_drv, dp_drv->orientation, &ln_map); - if (ret) + if (ret) { + mutex_unlock(&dp_drv->train_mutex); goto exit; + } mdss_dp_phy_share_lane_config(&dp_drv->phy_io, dp_drv->orientation, dp_drv->dpcd.max_lane_count); ret = mdss_dp_enable_mainlink_clocks(dp_drv); - if (ret) + if (ret) { + mutex_unlock(&dp_drv->train_mutex); goto exit; + } mdss_dp_mainlink_reset(&dp_drv->ctrl_io); @@ -1238,7 +1242,6 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv) pr_debug("end\n"); exit: - mutex_unlock(&dp_drv->train_mutex); return ret; }