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

Commit b5d5b854 authored by Ajay Singh Parmar's avatar Ajay Singh Parmar
Browse files

drm/msm/dp: fix suspend/resume hot plug handling



If the device is going into suspend and then resumed,
handle the power on sequence gracefully. Cable may have
been removed during suspend. On resume, let the DisplayPort
driver come up with the same settings it went to sleep.
In case the cable remains connected, it will join back the
previous session. If the cable is removed, DisplayPort
driver eventually receives the cable disconnect notification
on resume and handles it same way as normal hot plug.

CRs-Fixed: 2296893
Change-Id: Ie6c1da154cc0a2e03cf85b7383479b1dd3028873
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent 0bb81cfe
Loading
Loading
Loading
Loading
+34 −27
Original line number Diff line number Diff line
@@ -80,6 +80,7 @@ struct dp_ctrl_private {
	atomic_t aborted;

	u32 vic;
	u32 stream_count;
	struct dp_mst_channel_info mst_ch_info;
};

@@ -150,7 +151,7 @@ static void dp_ctrl_push_idle(struct dp_ctrl_private *ctrl,
	if (!wait_for_completion_timeout(&ctrl->idle_comp,
			idle_pattern_completion_timeout_ms))
		pr_warn("time out\n");

	else
		pr_debug("mainlink off done\n");
}

@@ -175,17 +176,10 @@ static void dp_ctrl_configure_source_link_params(struct dp_ctrl_private *ctrl,
	}
}

static int dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
static void dp_ctrl_wait4video_ready(struct dp_ctrl_private *ctrl)
{
	int ret = 0;

	ret = wait_for_completion_timeout(&ctrl->video_comp, HZ / 2);
	if (ret <= 0) {
		pr_err("SEND_VIDEO time out (%d)\n", ret);
		return -EINVAL;
	}

	return 0;
	if (!wait_for_completion_timeout(&ctrl->video_comp, HZ / 2))
		pr_warn("SEND_VIDEO time out\n");
}

static int dp_ctrl_update_sink_vx_px(struct dp_ctrl_private *ctrl,
@@ -566,7 +560,7 @@ static void dp_ctrl_disable_link_clock(struct dp_ctrl_private *ctrl)
	ctrl->power->clk_enable(ctrl->power, DP_LINK_PM, false);
}

static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl)
static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl, bool shallow)
{
	int rc = -EINVAL;
	u32 link_train_max_retries = 100;
@@ -584,13 +578,26 @@ static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl)
		pr_debug("bw_code=%d, lane_count=%d\n",
			link_params->bw_code, link_params->lane_count);

		dp_ctrl_enable_link_clock(ctrl);
		rc = dp_ctrl_enable_link_clock(ctrl);
		if (rc)
			break;

		dp_ctrl_configure_source_link_params(ctrl, true);

		rc = dp_ctrl_setup_main_link(ctrl);
		if (!rc)
			break;

		/*
		 * Shallow means link training failure is not important.
		 * If it fails, we still keep the link clocks on.
		 * In this mode, the system expects DP to be up
		 * even though the cable is removed. Disconnect interrupt
		 * will eventually trigger and shutdown DP.
		 */
		if (shallow)
			break;

		dp_ctrl_link_rate_down_shift(ctrl);

		dp_ctrl_configure_source_link_params(ctrl, false);
@@ -747,8 +754,10 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)

	ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_COMPLETED;

	if (ctrl->stream_count) {
		dp_ctrl_send_video(ctrl);
	ret = dp_ctrl_wait4video_ready(ctrl);
		dp_ctrl_wait4video_ready(ctrl);
	}
end:
	return ret;
}
@@ -784,7 +793,7 @@ static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl)

	ctrl->aux->init(ctrl->aux, ctrl->parser->aux_cfg);

	ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode);
	ret = ctrl->dp_ctrl.on(&ctrl->dp_ctrl, ctrl->mst_mode, false);
	if (ret)
		pr_err("failed to enable DP controller\n");

@@ -1007,18 +1016,14 @@ static int dp_ctrl_stream_on(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)

	dp_ctrl_mst_send_act(ctrl);

	rc = dp_ctrl_wait4video_ready(ctrl);
	if (rc)
		goto error;
	dp_ctrl_wait4video_ready(ctrl);

	ctrl->stream_count++;

	link_ready = ctrl->catalog->mainlink_ready(ctrl->catalog);
	pr_debug("mainlink %s\n", link_ready ? "READY" : "NOT READY");

	return rc;

error:
	dp_ctrl_disable_stream_clocks(ctrl, panel);
	return rc;
}

static void dp_ctrl_mst_stream_pre_off(struct dp_ctrl *dp_ctrl,
@@ -1082,9 +1087,10 @@ static void dp_ctrl_stream_off(struct dp_ctrl *dp_ctrl, struct dp_panel *panel)
	panel->hw_cfg(panel, false);

	dp_ctrl_disable_stream_clocks(ctrl, panel);
	ctrl->stream_count--;
}

static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode)
static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode, bool shallow)
{
	int rc = 0;
	struct dp_ctrl_private *ctrl;
@@ -1116,8 +1122,9 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode)
		ctrl->link->link_params.bw_code,
		ctrl->link->link_params.lane_count);

	rc = dp_ctrl_link_setup(ctrl);
	if (rc)
	rc = dp_ctrl_link_setup(ctrl, shallow);
	/* Ignore errors in case of shallow processing */
	if (!shallow && rc)
		goto end;

	ctrl->power_on = true;
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@
struct dp_ctrl {
	int (*init)(struct dp_ctrl *dp_ctrl, bool flip, bool reset);
	void (*deinit)(struct dp_ctrl *dp_ctrl);
	int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode);
	int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode, bool shallow);
	void (*off)(struct dp_ctrl *dp_ctrl);
	void (*abort)(struct dp_ctrl *dp_ctrl);
	void (*isr)(struct dp_ctrl *dp_ctrl);
+20 −40
Original line number Diff line number Diff line
@@ -495,14 +495,8 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp)

	dp->aux->state |= DP_STATE_NOTIFICATION_SENT;

	if (!dp->mst.mst_active) {
		if (dp->dp_display.is_sst_connected == hpd) {
			pr_debug("SKIPPED:hpd:%d\n", hpd);
			goto skip_wait;
		}

	if (!dp->mst.mst_active)
		dp->dp_display.is_sst_connected = hpd;
	}

	reinit_completion(&dp->notification_comp);
	dp_display_send_hpd_event(dp);
@@ -566,31 +560,6 @@ static void dp_display_host_init(struct dp_display_private *dp)
	dp->core_initialized = true;
}

static int dp_display_update_pclk(struct dp_display_private *dp)
{
	int rc = 0;
	u32 rate, max_pclk_khz;
	u32 const enc_factx10 = 8;
	u32 const default_bpp = 30;

	if (dp->debug->max_pclk_khz) {
		dp->dp_display.max_pclk_khz = dp->debug->max_pclk_khz;
		goto end;
	}

	rate = drm_dp_bw_code_to_link_rate(dp->link->link_params.bw_code);
	rate /= default_bpp;

	max_pclk_khz = dp->link->link_params.lane_count * rate * enc_factx10;

	dp->dp_display.max_pclk_khz = min(dp->parser->max_pclk_khz,
						max_pclk_khz);

	pr_debug("dp max_pclk_khz = %d\n", dp->dp_display.max_pclk_khz);
end:
	return rc;
}

static void dp_display_host_deinit(struct dp_display_private *dp)
{
	if (!dp->core_initialized) {
@@ -623,6 +592,8 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)

	dp->is_connected = true;

	dp->dp_display.max_pclk_khz = dp->parser->max_pclk_khz;

	dp_display_host_init(dp);

	if (dp->debug->psm_enabled) {
@@ -648,13 +619,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
	dp_display_process_mst_hpd_high(dp);

	mutex_lock(&dp->session_lock);
	rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active);
	if (rc) {
		mutex_unlock(&dp->session_lock);
		goto end;
	}

	rc = dp_display_update_pclk(dp);
	rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, false);
	if (rc) {
		mutex_unlock(&dp->session_lock);
		goto end;
@@ -958,6 +923,11 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
			dp->power_on);

	if (!dp->hpd->hpd_high) {
		if (!dp->is_connected) {
			pr_debug("already disconnected\n");
			return 0;
		}

		/* cancel any pending request */
		atomic_set(&dp->aborted, 1);
		dp->ctrl->abort(dp->ctrl);
@@ -1285,7 +1255,17 @@ static int dp_display_prepare(struct dp_display *dp_display, void *panel)

	dp_display_host_init(dp);

	rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active);
	/*
	 * Execute the dp controller power on in shallow mode here.
	 * In normal cases, controller should have been powered on
	 * by now. In some cases like suspend/resume or framework
	 * reboot, we end up here without a powered on controller.
	 * Cable may have been removed in suspended state. In that
	 * case, link training is bound to fail on system resume.
	 * So, we execute in shallow mode here to do only minimal
	 * and required things.
	 */
	rc = dp->ctrl->on(dp->ctrl, dp->mst.mst_active, true);
	if (rc)
		goto end;