diff options
| author | Narender Ankam <nankam@codeaurora.org> | 2017-08-08 17:38:32 +0530 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2017-10-04 00:02:40 -0700 |
| commit | 37b71d4aacd50107a2bb58c5b77dfd4e7bb0e466 (patch) | |
| tree | 0f229ab4809ef7a18ec2dbaacf866cbeca99f2ee | |
| parent | 04e88e68eedf67f5a6e4554f717917605121bf48 (diff) | |
msm: mdss: dp: fix no display issues during cable plugin/plugouts
If DP cable is disconnected while processing HPD or IRQ_HPD,
DP driver may continue with failsafe parameters and notify
connection event and immediately notify disconnection event
which may result in state machine corruption in userspace.
Add changes to avoid reading dpcd caps, edid, link training
or connection events if DP cable is disconnected in between.
Change-Id: I0b59ebdb636c9dc1086673253399b849734d51ee
Signed-off-by: Narender Ankam <nankam@codeaurora.org>
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp.c | 49 | ||||
| -rw-r--r-- | drivers/video/fbdev/msm/mdss_dp_aux.c | 42 |
2 files changed, 79 insertions, 12 deletions
diff --git a/drivers/video/fbdev/msm/mdss_dp.c b/drivers/video/fbdev/msm/mdss_dp.c index bc325a91a9bf..6d839a58470d 100644 --- a/drivers/video/fbdev/msm/mdss_dp.c +++ b/drivers/video/fbdev/msm/mdss_dp.c @@ -1542,6 +1542,19 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv, bool lt_needed) { int ret = 0; char ln_map[4]; + bool connected; + + mutex_lock(&dp_drv->attention_lock); + connected = dp_drv->cable_connected; + mutex_unlock(&dp_drv->attention_lock); + + /* + * If DP cable disconnected, Avoid link training or turning on DP Path + */ + if (!connected) { + pr_err("DP sink not connected\n"); + return -EINVAL; + } /* wait until link training is completed */ pr_debug("enter, lt_needed=%s\n", lt_needed ? "true" : "false"); @@ -1583,6 +1596,13 @@ static int mdss_dp_on_irq(struct mdss_dp_drv_pdata *dp_drv, bool lt_needed) dp_drv->power_on = true; ret = mdss_dp_setup_main_link(dp_drv, lt_needed); + if (ret) { + if (ret == -ENODEV || ret == -EINVAL) { + pr_err("main link setup failed\n"); + mutex_unlock(&dp_drv->train_mutex); + return ret; + } + } exit_loop: mutex_unlock(&dp_drv->train_mutex); @@ -2183,7 +2203,7 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) ret = mdss_dp_dpcd_cap_read(dp); if (ret || !mdss_dp_aux_is_link_rate_valid(dp->dpcd.max_link_rate) || !mdss_dp_aux_is_lane_count_valid(dp->dpcd.max_lane_count)) { - if (ret == EDP_AUX_ERR_TOUT) { + if ((ret == -ENODEV) || (ret == EDP_AUX_ERR_TOUT)) { pr_err("DPCD read timedout, skip connect notification\n"); goto end; } @@ -2215,6 +2235,9 @@ static int mdss_dp_process_hpd_high(struct mdss_dp_drv_pdata *dp) read_edid: ret = mdss_dp_edid_read(dp); if (ret) { + if (ret == -ENODEV) + goto end; + pr_err("edid read error, setting default resolution\n"); goto notify; } @@ -3532,9 +3555,33 @@ static void mdss_dp_reset_event_list(struct mdss_dp_drv_pdata *dp) static void mdss_dp_reset_sw_state(struct mdss_dp_drv_pdata *dp) { + int ret = 0; + pr_debug("enter\n"); mdss_dp_reset_event_list(dp); + + /* + * IRQ_HPD attention event handler first turns on DP path and then + * notifies CONNECT_IRQ_HPD and waits for userspace to trigger UNBLANK. + * In such cases, before UNBLANK call, if cable is disconnected, if + * DISCONNECT is notified immediately, userspace might not sense any + * change in connection status, leaving DP controller ON. + * + * To avoid such cases, wait for the connection event to complete before + * sending disconnection event + */ + if (atomic_read(&dp->notification_pending)) { + pr_debug("waiting for the pending notitfication\n"); + ret = wait_for_completion_timeout(&dp->notification_comp, HZ); + if (ret <= 0) { + pr_err("%s timed out\n", + mdss_dp_notification_status_to_string( + dp->hpd_notification_status)); + } + } + atomic_set(&dp->notification_pending, 0); + /* complete any waiting completions */ complete_all(&dp->notification_comp); } diff --git a/drivers/video/fbdev/msm/mdss_dp_aux.c b/drivers/video/fbdev/msm/mdss_dp_aux.c index 86946adfeeb0..407f230ca71e 100644 --- a/drivers/video/fbdev/msm/mdss_dp_aux.c +++ b/drivers/video/fbdev/msm/mdss_dp_aux.c @@ -411,7 +411,8 @@ retry: if (!connected) { pr_err("dp cable disconnected\n"); - break; + ret = -ENODEV; + goto end; } dp->aux_error_num = EDP_AUX_ERR_NONE; @@ -877,7 +878,7 @@ void dp_extract_edid_detailed_timing_description(struct edp_edid *edid, static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep) { - int cnt, ret; + int cnt, ret = 0; char data = 0; for (cnt = 5; cnt; cnt--) { @@ -886,6 +887,10 @@ static int dp_aux_chan_ready(struct mdss_dp_drv_pdata *ep) ret, mdss_dp_get_aux_error(ep->aux_error_num)); if (ret >= 0) break; + + if (ret == -ENODEV) + return ret; + msleep(100); } @@ -973,6 +978,7 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) u32 checksum = 0; bool phy_aux_update_requested = false; bool ext_block_parsing_done = false; + bool connected = false; ret = dp_aux_chan_ready(dp); if (ret) { @@ -992,6 +998,15 @@ int mdss_dp_edid_read(struct mdss_dp_drv_pdata *dp) u8 segment; u8 edid_buf[EDID_BLOCK_SIZE] = {0}; + mutex_lock(&dp->attention_lock); + connected = dp->cable_connected; + mutex_unlock(&dp->attention_lock); + + if (!connected) { + pr_err("DP sink not connected\n"); + return -ENODEV; + } + /* * Write the segment first. * Segment = 0, for blocks 0 and 1 @@ -1243,7 +1258,7 @@ int mdss_dp_aux_link_status_read(struct mdss_dp_drv_pdata *ep, int len) rlen = dp_aux_read_buf(ep, 0x202, len, 0); if (rlen < len) { pr_err("edp aux read failed\n"); - return 0; + return rlen; } rp = &ep->rxp; bp = rp->data; @@ -2459,21 +2474,24 @@ 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); - mdss_dp_aux_link_status_read(ep, 6); + ret = mdss_dp_aux_link_status_read(ep, 6); + if (ret == -ENODEV) + break; + if (mdss_dp_aux_clock_recovery_done(ep)) { ret = 0; break; } if (ep->v_level == DPCD_LINK_VOLTAGE_MAX) { - ret = -1; + ret = -EAGAIN; break; /* quit */ } if (old_v_level == ep->v_level) { tries++; if (tries >= maximum_retries) { - ret = -1; + ret = -EAGAIN; break; /* quit */ } } else { @@ -2511,7 +2529,9 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) usleep_time = ep->dpcd.training_read_interval; usleep_range(usleep_time, usleep_time); - mdss_dp_aux_link_status_read(ep, 6); + ret = mdss_dp_aux_link_status_read(ep, 6); + if (ret == -ENODEV) + break; if (mdss_dp_aux_channel_eq_done(ep)) { ret = 0; @@ -2519,7 +2539,7 @@ static int dp_start_link_train_2(struct mdss_dp_drv_pdata *ep) } if (tries > maximum_retries) { - ret = -1; + ret = -EAGAIN; break; } tries++; @@ -2584,7 +2604,7 @@ int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp) ret = dp_start_link_train_1(dp); if (ret < 0) { - if (!dp_link_rate_down_shift(dp)) { + if ((ret == -EAGAIN) && !dp_link_rate_down_shift(dp)) { pr_debug("retry with lower rate\n"); dp_clear_training_pattern(dp); return -EAGAIN; @@ -2603,7 +2623,7 @@ int mdss_dp_link_train(struct mdss_dp_drv_pdata *dp) ret = dp_start_link_train_2(dp); if (ret < 0) { - if (!dp_link_rate_down_shift(dp)) { + if ((ret == -EAGAIN) && !dp_link_rate_down_shift(dp)) { pr_debug("retry with lower rate\n"); dp_clear_training_pattern(dp); return -EAGAIN; @@ -2640,7 +2660,7 @@ int mdss_dp_dpcd_status_read(struct mdss_dp_drv_pdata *ep) ret = mdss_dp_aux_link_status_read(ep, 6); - if (ret) { + if (ret > 0) { sp = &ep->link_status; ret = sp->port_0_in_sync; /* 1 == sync */ } |
