Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 37b71d4a authored by Narender Ankam's avatar Narender Ankam Committed by Gerrit - the friendly Code Review server
Browse files

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: default avatarNarender Ankam <nankam@codeaurora.org>
parent 04e88e68
Loading
Loading
Loading
Loading
+48 −1
Original line number Diff line number Diff line
@@ -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);
}

+31 −11
Original line number Diff line number Diff line
@@ -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 */
	}