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

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

drm/msm/dp: complete link training before hot plug notification



DisplayPort source device needs to complete the link training
with the sink device before sending video data. Currently this
is being done on a frame commit thread. Once the hot plug
notification to user-space is sent, the commit thread is blocked
until the link training is completed. As link training can take
some time, it may result in primary display artifacts.

Optimize this by moving the link training before sending the
hot plug notification to user-space. This unblocks the frame
commit thread. As the link training is done as soon as the
cable is connected, time to display the video on sink is reduced.
Also, shut down all the clocks and release all the hardware resources
like GPIOs, regulators, pin-controls etc on not just physical cable
disconnect but on IRQ HPD disconnect as well to save power.

To achieve this, create a new power module, link, which can handle
the link training separately. Associate corresponding clocks to the
new power module. Once the cable is connected, enable necessary
power modules like core and newly created link and start the
link training. If it fails, no hot plug to user-space is sent. This
avoids unnecessary resource allocation by user-space even though
corresponding driver interface can't be used. If it succeeds, the
frame commit thread can return quickly by enabling the corresponding
video stream.

Some other minor enhancements involving this implementation is to
avoid sending hot plug if user-space can't process it like sending
same status consecutively which can result in unnecessary wait in
driver for user-space to respond.
Return with error code in case the link training has failed so that
the session can be torn down quickly.

CRs-Fixed: 2294347
Change-Id: I718624f9f5e4ca6b7f59cd8681ae9a24d0d8a52c
Signed-off-by: default avatarAjay Singh Parmar <aparmar@codeaurora.org>
parent 61240ba4
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -639,7 +639,6 @@
			 <&clock_gcc GCC_USB3_PRIM_PHY_PIPE_CLK>,
			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_CLK>,
			 <&clock_dispcc DISP_CC_MDSS_DP_LINK_INTF_CLK>,
			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
			 <&clock_dispcc DISP_CC_MDSS_DP_CRYPTO_CLK>,
			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK_SRC>,
			 <&mdss_dp_pll DP_VCO_DIVIDED_CLK_SRC_MUX>,
@@ -648,9 +647,8 @@
			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL_CLK>,
			 <&clock_dispcc DISP_CC_MDSS_DP_PIXEL1_CLK>;
		clock-names = "core_aux_clk", "core_usb_ref_clk_src",
			"core_usb_ref_clk",
			"core_usb_pipe_clk", "ctrl_link_clk",
			"ctrl_link_iface_clk", "ctrl_pixel_clk",
			"core_usb_ref_clk", "core_usb_pipe_clk",
			"link_clk", "link_iface_clk",
			"crypto_clk", "pixel_clk_rcg", "pixel_parent",
			"pixel1_clk_rcg", "pixel1_parent",
			"strm0_pixel_clk", "strm1_pixel_clk";
+9 −0
Original line number Diff line number Diff line
@@ -41,6 +41,7 @@ struct dp_aux_private {
	bool read;
	bool no_send_addr;
	bool no_send_stop;
	bool enabled;

	u32 offset;
	u32 segment;
@@ -623,12 +624,16 @@ static void dp_aux_init(struct dp_aux *dp_aux, struct dp_aux_cfg *aux_cfg)

	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);

	if (aux->enabled)
		return;

	dp_aux_reset_phy_config_indices(aux_cfg);
	aux->catalog->setup(aux->catalog, aux_cfg);
	aux->catalog->reset(aux->catalog);
	aux->catalog->enable(aux->catalog, true);
	atomic_set(&aux->aborted, 0);
	aux->retry_cnt = 0;
	aux->enabled = true;
}

static void dp_aux_deinit(struct dp_aux *dp_aux)
@@ -642,8 +647,12 @@ static void dp_aux_deinit(struct dp_aux *dp_aux)

	aux = container_of(dp_aux, struct dp_aux_private, dp_aux);

	if (!aux->enabled)
		return;

	atomic_set(&aux->aborted, 1);
	aux->catalog->enable(aux->catalog, false);
	aux->enabled = false;
}

static int dp_aux_register(struct dp_aux *dp_aux)
+82 −109
Original line number Diff line number Diff line
@@ -122,19 +122,12 @@ static void dp_ctrl_state_ctrl(struct dp_ctrl_private *ctrl, u32 state)
	ctrl->catalog->state_ctrl(ctrl->catalog, state);
}

static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl, enum dp_stream_id strm)
static void dp_ctrl_push_idle(struct dp_ctrl_private *ctrl,
				enum dp_stream_id strm)
{
	int const idle_pattern_completion_timeout_ms = 3 * HZ / 100;
	struct dp_ctrl_private *ctrl;
	int const idle_pattern_completion_timeout_ms = HZ / 10;
	u32 state = 0x0;

	if (!dp_ctrl) {
		pr_err("Invalid input data\n");
		return;
	}

	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);

	if (!ctrl->power_on) {
		pr_err("CTRL off, return\n");
		return;
@@ -158,7 +151,7 @@ static void dp_ctrl_push_idle(struct dp_ctrl *dp_ctrl, enum dp_stream_id strm)

	if (!wait_for_completion_timeout(&ctrl->idle_comp,
			idle_pattern_completion_timeout_ms))
		pr_warn("PUSH_IDLE time out\n");
		pr_warn("time out\n");

	pr_debug("mainlink off done\n");
}
@@ -301,7 +294,12 @@ static int dp_ctrl_link_train_1(struct dp_ctrl_private *ctrl)

	tries = 0;
	old_v_level = ctrl->link->phy_params.v_level;
	while (!atomic_read(&ctrl->aborted)) {
	while (1) {
		if (atomic_read(&ctrl->aborted)) {
			ret = -EINVAL;
			break;
		}

		drm_dp_link_train_clock_recovery_delay(ctrl->panel->dpcd);

		ret = dp_ctrl_read_link_status(ctrl, link_status);
@@ -417,6 +415,11 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
	}

	do  {
		if (atomic_read(&ctrl->aborted)) {
			ret = -EINVAL;
			break;
		}

		drm_dp_link_train_channel_eq_delay(ctrl->panel->dpcd);

		ret = dp_ctrl_read_link_status(ctrl, link_status);
@@ -439,7 +442,7 @@ static int dp_ctrl_link_training_2(struct dp_ctrl_private *ctrl)
			ret = -EINVAL;
			break;
		}
	} while (!atomic_read(&ctrl->aborted));
	} while (1);
end:
	ctrl->aux->state &= ~DP_STATE_TRAIN_2_STARTED;

@@ -467,8 +470,10 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
	link_info.capabilities = ctrl->panel->link_info.capabilities;

	ret = drm_dp_link_configure(ctrl->aux->drm_aux, &link_info);
	if (ret)
	if (ret) {
		pr_err_ratelimited("link_configure failed, rc=%d\n", ret);
		goto end;
	}

	ret = drm_dp_dpcd_write(ctrl->aux->drm_aux,
		DP_MAIN_LINK_CHANNEL_CODING_SET, &encoding, 1);
@@ -504,7 +509,7 @@ static int dp_ctrl_link_train(struct dp_ctrl_private *ctrl)
	return ret;
}

static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool train)
static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl)
{
	int ret = 0;

@@ -513,9 +518,6 @@ static int dp_ctrl_setup_main_link(struct dp_ctrl_private *ctrl, bool train)
	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN)
		goto end;

	if (!train)
		goto end;

	/*
	 * As part of previous calls, DP controller state might have
	 * transitioned to PUSH_IDLE. In order to start transmitting a link
@@ -548,14 +550,17 @@ static void dp_ctrl_set_clock_rate(struct dp_ctrl_private *ctrl,
		pr_err("%s clock could not be set with rate %d\n", name, rate);
}

static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
static int dp_ctrl_enable_link_clock(struct dp_ctrl_private *ctrl)
{
	int ret = 0;
	u32 rate = drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code);
	enum dp_pm_type type = DP_LINK_PM;

	dp_ctrl_set_clock_rate(ctrl, "ctrl_link_clk", DP_CTRL_PM,
		drm_dp_bw_code_to_link_rate(ctrl->link->link_params.bw_code));
	pr_debug("rate=%d\n", rate);

	ret = ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, true);
	dp_ctrl_set_clock_rate(ctrl, "link_clk", type, rate);

	ret = ctrl->power->clk_enable(ctrl->power, type, true);
	if (ret) {
		pr_err("Unabled to start link clocks\n");
		ret = -EINVAL;
@@ -564,9 +569,46 @@ static int dp_ctrl_enable_mainlink_clocks(struct dp_ctrl_private *ctrl)
	return ret;
}

static int dp_ctrl_disable_mainlink_clocks(struct dp_ctrl_private *ctrl)
static void dp_ctrl_disable_link_clock(struct dp_ctrl_private *ctrl)
{
	return ctrl->power->clk_enable(ctrl->power, DP_CTRL_PM, false);
	ctrl->power->clk_enable(ctrl->power, DP_LINK_PM, false);
}

static int dp_ctrl_link_setup(struct dp_ctrl_private *ctrl)
{
	int rc = -EINVAL;
	u32 link_train_max_retries = 100;
	struct dp_catalog_ctrl *catalog;
	struct dp_link_params *link_params;

	catalog = ctrl->catalog;
	link_params = &ctrl->link->link_params;

	catalog->hpd_config(catalog, true);
	catalog->phy_lane_cfg(catalog, ctrl->orientation,
				link_params->lane_count);

	while (--link_train_max_retries || !atomic_read(&ctrl->aborted)) {
		pr_debug("bw_code=%d, lane_count=%d\n",
			link_params->bw_code, link_params->lane_count);

		dp_ctrl_enable_link_clock(ctrl);
		dp_ctrl_configure_source_link_params(ctrl, true);

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

		dp_ctrl_link_rate_down_shift(ctrl);

		dp_ctrl_configure_source_link_params(ctrl, false);
		dp_ctrl_disable_link_clock(ctrl);

		/* hw recommended delays before retrying link training */
		msleep(20);
	}

	return rc;
}

static int dp_ctrl_enable_stream_clocks(struct dp_ctrl_private *ctrl,
@@ -645,6 +687,7 @@ static int dp_ctrl_host_init(struct dp_ctrl *dp_ctrl, bool flip, bool reset)
		catalog->phy_reset(ctrl->catalog);
	}
	catalog->enable_irq(ctrl->catalog, true);
	atomic_set(&ctrl->aborted, 0);

	return 0;
}
@@ -686,51 +729,24 @@ static int dp_ctrl_link_maintenance(struct dp_ctrl *dp_ctrl)

	if (!ctrl->power_on || atomic_read(&ctrl->aborted)) {
		pr_err("CTRL off, return\n");
		return -EINVAL;
		ret = -EINVAL;
		goto end;
	}

	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_COMPLETED;
	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_FAILED;
	ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_STARTED;

	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);

	do {
		if (ret == -EAGAIN) {
			/* try with lower link rate */
			dp_ctrl_link_rate_down_shift(ctrl);

			dp_ctrl_configure_source_link_params(ctrl, false);
		}

		ctrl->catalog->phy_lane_cfg(ctrl->catalog,
			ctrl->orientation, ctrl->link->link_params.lane_count);

		/*
		 * Disable and re-enable the mainlink clock since the
		 * link clock might have been adjusted as part of the
		 * link maintenance.
		 */
		dp_ctrl_disable_mainlink_clocks(ctrl);

		ret = dp_ctrl_enable_mainlink_clocks(ctrl);
		if (ret)
			continue;

		dp_ctrl_configure_source_link_params(ctrl, true);

		reinit_completion(&ctrl->idle_comp);

		ret = dp_ctrl_setup_main_link(ctrl, true);
	} while (ret == -EAGAIN);
	ctrl->catalog->reset(ctrl->catalog);
	dp_ctrl_disable_link_clock(ctrl);
	ret = dp_ctrl_link_setup(ctrl);

	ctrl->aux->state &= ~DP_STATE_LINK_MAINTENANCE_STARTED;

	if (ret)
		ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_FAILED;
	else
		ctrl->aux->state |= DP_STATE_LINK_MAINTENANCE_COMPLETED;

end:
	return ret;
}

@@ -753,13 +769,13 @@ static void dp_ctrl_process_phy_test_request(struct dp_ctrl *dp_ctrl)

	pr_debug("start\n");

	ctrl->dp_ctrl.push_idle(&ctrl->dp_ctrl, DP_STREAM_0);
	/*
	 * The global reset will need DP link ralated clocks to be
	 * running. Add the global reset just before disabling the
	 * link clocks and core clocks.
	 */
	ctrl->dp_ctrl.reset(&ctrl->dp_ctrl);
	ctrl->catalog->reset(ctrl->catalog);
	ctrl->dp_ctrl.stream_pre_off(&ctrl->dp_ctrl, ctrl->panel);
	ctrl->dp_ctrl.stream_off(&ctrl->dp_ctrl, ctrl->panel);
	ctrl->dp_ctrl.off(&ctrl->dp_ctrl);

@@ -826,19 +842,6 @@ static void dp_ctrl_send_phy_test_pattern(struct dp_ctrl_private *ctrl)
			dp_link_get_phy_test_pattern(pattern_requested));
}

static void dp_ctrl_reset(struct dp_ctrl *dp_ctrl)
{
	struct dp_ctrl_private *ctrl;

	if (!dp_ctrl) {
		pr_err("invalid params\n");
		return;
	}

	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);
	ctrl->catalog->reset(ctrl->catalog);
}

static void dp_ctrl_send_video(struct dp_ctrl_private *ctrl)
{
	ctrl->catalog->state_ctrl(ctrl->catalog, ST_SEND_VIDEO);
@@ -1052,12 +1055,16 @@ static void dp_ctrl_mst_stream_pre_off(struct dp_ctrl *dp_ctrl,
static void dp_ctrl_stream_pre_off(struct dp_ctrl *dp_ctrl,
		struct dp_panel *panel)
{
	struct dp_ctrl_private *ctrl;

	if (!dp_ctrl || !panel) {
		pr_err("invalid input\n");
		return;
	}

	dp_ctrl_push_idle(dp_ctrl, panel->stream_id);
	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);

	dp_ctrl_push_idle(ctrl, panel->stream_id);

	dp_ctrl_mst_stream_pre_off(dp_ctrl, panel);
}
@@ -1081,7 +1088,6 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode)
	int rc = 0;
	struct dp_ctrl_private *ctrl;
	u32 rate = 0;
	u32 link_train_max_retries = 100;

	if (!dp_ctrl) {
		rc = -EINVAL;
@@ -1090,13 +1096,9 @@ static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool mst_mode)

	ctrl = container_of(dp_ctrl, struct dp_ctrl_private, dp_ctrl);

	atomic_set(&ctrl->aborted, 0);

	ctrl->mst_mode = mst_mode;
	rate = ctrl->panel->link_info.rate;

	ctrl->catalog->hpd_config(ctrl->catalog, true);

	if (ctrl->link->sink_request & DP_TEST_LINK_PHY_TEST_PATTERN) {
		pr_debug("using phy test link parameters\n");
	} else {
@@ -1110,38 +1112,11 @@ 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);

	ctrl->catalog->phy_lane_cfg(ctrl->catalog,
			ctrl->orientation, ctrl->link->link_params.lane_count);

	rc = dp_ctrl_enable_mainlink_clocks(ctrl);
	rc = dp_ctrl_link_setup(ctrl);
	if (rc)
		goto end;

	reinit_completion(&ctrl->idle_comp);

	dp_ctrl_configure_source_link_params(ctrl, true);

	while (--link_train_max_retries && !atomic_read(&ctrl->aborted)) {
		rc = dp_ctrl_setup_main_link(ctrl, true);
		if (!rc)
			break;

		/* try with lower link rate */
		dp_ctrl_link_rate_down_shift(ctrl);

		dp_ctrl_configure_source_link_params(ctrl, false);

		dp_ctrl_disable_mainlink_clocks(ctrl);
		/* hw recommended delay before re-enabling clocks */
		msleep(20);

		dp_ctrl_enable_mainlink_clocks(ctrl);
	}

	ctrl->power_on = true;

	pr_debug("End-\n");

end:
	return rc;
}
@@ -1161,7 +1136,7 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
	/* Make sure DP is disabled before clk disable */
	wmb();

	dp_ctrl_disable_mainlink_clocks(ctrl);
	dp_ctrl_disable_link_clock(ctrl);

	ctrl->mst_mode = false;
	ctrl->power_on = false;
@@ -1249,10 +1224,8 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
	dp_ctrl->deinit    = dp_ctrl_host_deinit;
	dp_ctrl->on        = dp_ctrl_on;
	dp_ctrl->off       = dp_ctrl_off;
	dp_ctrl->push_idle = dp_ctrl_push_idle;
	dp_ctrl->abort     = dp_ctrl_abort;
	dp_ctrl->isr       = dp_ctrl_isr;
	dp_ctrl->reset	   = dp_ctrl_reset;
	dp_ctrl->link_maintenance = dp_ctrl_link_maintenance;
	dp_ctrl->process_phy_test_request = dp_ctrl_process_phy_test_request;
	dp_ctrl->stream_on = dp_ctrl_stream_on;
+0 −2
Original line number Diff line number Diff line
@@ -27,8 +27,6 @@ struct dp_ctrl {
	void (*deinit)(struct dp_ctrl *dp_ctrl);
	int (*on)(struct dp_ctrl *dp_ctrl, bool mst_mode);
	void (*off)(struct dp_ctrl *dp_ctrl);
	void (*reset)(struct dp_ctrl *dp_ctrl);
	void (*push_idle)(struct dp_ctrl *dp_ctrl, enum dp_stream_id strm);
	void (*abort)(struct dp_ctrl *dp_ctrl);
	void (*isr)(struct dp_ctrl *dp_ctrl);
	bool (*handle_sink_request)(struct dp_ctrl *dp_ctrl);
+1 −16
Original line number Diff line number Diff line
@@ -35,8 +35,6 @@ struct dp_debug_private {
	u8 *dpcd;
	u32 dpcd_size;

	int vdo;

	char exe_mode[SZ_32];
	char reg_dump[SZ_32];

@@ -47,7 +45,6 @@ struct dp_debug_private {
	struct dp_catalog *catalog;
	struct drm_connector **connector;
	struct device *dev;
	struct work_struct sim_work;
	struct dp_debug dp_debug;
	struct dp_parser *parser;
};
@@ -1068,9 +1065,7 @@ static ssize_t dp_debug_write_attention(struct file *file,
	if (kstrtoint(buf, 10, &vdo) != 0)
		goto end;

	debug->vdo = vdo;

	schedule_work(&debug->sim_work);
	debug->hpd->simulate_attention(debug->hpd, vdo);
end:
	return len;
}
@@ -1399,14 +1394,6 @@ static int dp_debug_init(struct dp_debug *dp_debug)
	return rc;
}

static void dp_debug_sim_work(struct work_struct *work)
{
	struct dp_debug_private *debug =
		container_of(work, typeof(*debug), sim_work);

	debug->hpd->simulate_attention(debug->hpd, debug->vdo);
}

u8 *dp_debug_get_edid(struct dp_debug *dp_debug)
{
	struct dp_debug_private *debug;
@@ -1441,8 +1428,6 @@ struct dp_debug *dp_debug_get(struct device *dev, struct dp_panel *panel,
		goto error;
	}

	INIT_WORK(&debug->sim_work, dp_debug_sim_work);

	debug->dp_debug.debug_en = false;
	debug->hpd = hpd;
	debug->link = link;
Loading