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

Commit 8cf0c22b authored by Padmanabhan Komanduru's avatar Padmanabhan Komanduru
Browse files

drm/msm/dp: add support for EDID and link compliance tests



Add the support for EDID test/Link training tests and sending
the test response to the sink as appropriate.

Change-Id: I7ced0fa8145545743bd4af4b9fbe34f26a4f78ad
Signed-off-by: default avatarPadmanabhan Komanduru <pkomandu@codeaurora.org>
parent 0cc4e565
Loading
Loading
Loading
Loading
+52 −71
Original line number Diff line number Diff line
@@ -63,7 +63,6 @@ struct dp_ctrl_private {

	struct completion idle_comp;
	struct completion video_comp;
	struct completion irq_comp;

	bool psm_enabled;
	bool orientation;
@@ -1152,9 +1151,18 @@ static bool dp_ctrl_use_fixed_nvid(struct dp_ctrl_private *ctrl)
	return false;
}

static int dp_ctrl_on_irq(struct dp_ctrl_private *ctrl, bool lt_needed)
static int dp_ctrl_handle_sink_request(struct dp_ctrl *dp_ctrl,
	u32 sink_request)
{
	int ret = 0;
	struct dp_ctrl_private *ctrl;

	if (!dp_ctrl) {
		ret = -EINVAL;
		goto end;
	}

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

	do {
		if (ret == -EAGAIN)
@@ -1163,14 +1171,13 @@ static int dp_ctrl_on_irq(struct dp_ctrl_private *ctrl, bool lt_needed)
		ctrl->catalog->phy_lane_cfg(ctrl->catalog,
			ctrl->orientation, ctrl->link->link_params.lane_count);

		if (lt_needed) {
		if (sink_request &
			(DP_LINK_STATUS_UPDATED | DP_TEST_LINK_TRAINING)) {
			/*
			 * Diasable and re-enable the mainlink clock since the
			 * Disable and re-enable the mainlink clock since the
			 * link clock might have been adjusted as part of the
			 * link maintenance.
			 */
			if (!ctrl->link->phy_pattern_requested(
					ctrl->link))
			dp_ctrl_disable_mainlink_clocks(ctrl);

			ret = dp_ctrl_enable_mainlink_clocks(ctrl);
@@ -1187,28 +1194,42 @@ static int dp_ctrl_on_irq(struct dp_ctrl_private *ctrl, bool lt_needed)

		reinit_completion(&ctrl->idle_comp);

		if (ctrl->psm_enabled) {
			ret = ctrl->link->send_psm_request(ctrl->link, false);
			if (ret) {
				pr_err("failed to exit low power mode, rc=%d\n",
					ret);
				continue;
			}
		}

		ret = dp_ctrl_setup_main_link(ctrl, lt_needed);
		ret = dp_ctrl_setup_main_link(ctrl, true);
	} while (ret == -EAGAIN);

end:
	return ret;
}

static int dp_ctrl_on_hpd(struct dp_ctrl_private *ctrl)
static void dp_ctrl_reset(struct dp_ctrl *dp_ctrl)
{
	int ret = 0;
	u32 rate = ctrl->panel->link_info.rate;
	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 int dp_ctrl_on(struct dp_ctrl *dp_ctrl)
{
	int rc = 0;
	struct dp_ctrl_private *ctrl;
	u32 rate = 0;
	u32 link_train_max_retries = 100;

	if (!dp_ctrl) {
		rc = -EINVAL;
		goto end;
	}

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

	atomic_set(&ctrl->aborted, 0);
	rate = ctrl->panel->link_info.rate;

	ctrl->power->clk_enable(ctrl->power, DP_CORE_PM, true);
	ctrl->catalog->hpd_config(ctrl->catalog, true);
@@ -1224,25 +1245,22 @@ static int dp_ctrl_on_hpd(struct dp_ctrl_private *ctrl)
	ctrl->catalog->phy_lane_cfg(ctrl->catalog,
			ctrl->orientation, ctrl->link->link_params.lane_count);

	ret = dp_ctrl_enable_mainlink_clocks(ctrl);
	if (ret)
		goto exit;
	rc = dp_ctrl_enable_mainlink_clocks(ctrl);
	if (rc)
		goto end;

	reinit_completion(&ctrl->idle_comp);

	dp_ctrl_configure_source_params(ctrl);

	if (ctrl->psm_enabled)
		ret = ctrl->link->send_psm_request(ctrl->link, false);

	while (--link_train_max_retries && !atomic_read(&ctrl->aborted)) {
		ctrl->catalog->config_msa(ctrl->catalog,
			drm_dp_bw_code_to_link_rate(
			ctrl->link->link_params.bw_code),
			ctrl->pixel_rate, dp_ctrl_use_fixed_nvid(ctrl));

		ret = dp_ctrl_setup_main_link(ctrl, true);
		if (!ret)
		rc = dp_ctrl_setup_main_link(ctrl, true);
		if (!rc)
			break;

		/* try with lower link rate */
@@ -1259,48 +1277,11 @@ static int dp_ctrl_on_hpd(struct dp_ctrl_private *ctrl)

	pr_debug("End-\n");

exit:
	return ret;
}

static void dp_ctrl_off_irq(struct dp_ctrl_private *ctrl)
{
	ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);

	/* Make sure DP mainlink and audio engines are disabled */
	wmb();

	complete_all(&ctrl->irq_comp);
	pr_debug("end\n");
}

static void dp_ctrl_off_hpd(struct dp_ctrl_private *ctrl)
{
	ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
	pr_debug("DP off done\n");
}

static int dp_ctrl_on(struct dp_ctrl *dp_ctrl, bool hpd_irq)
{
	int rc = 0;
	struct dp_ctrl_private *ctrl;

	if (!dp_ctrl) {
		rc = -EINVAL;
		goto end;
	}

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

	if (hpd_irq)
		rc = dp_ctrl_on_irq(ctrl, false);
	else
		rc = dp_ctrl_on_hpd(ctrl);
end:
	return rc;
}

static void dp_ctrl_off(struct dp_ctrl *dp_ctrl, bool hpd_irq)
static void dp_ctrl_off(struct dp_ctrl *dp_ctrl)
{
	struct dp_ctrl_private *ctrl;

@@ -1309,10 +1290,9 @@ static void dp_ctrl_off(struct dp_ctrl *dp_ctrl, bool hpd_irq)

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

	if (hpd_irq)
		dp_ctrl_off_irq(ctrl);
	else
		dp_ctrl_off_hpd(ctrl);
	ctrl->catalog->mainlink_ctrl(ctrl->catalog, false);
	pr_debug("DP off done\n");

}

static void dp_ctrl_isr(struct dp_ctrl *dp_ctrl)
@@ -1354,7 +1334,6 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)

	init_completion(&ctrl->idle_comp);
	init_completion(&ctrl->video_comp);
	init_completion(&ctrl->irq_comp);

	/* in parameters */
	ctrl->parser   = in->parser;
@@ -1370,10 +1349,12 @@ struct dp_ctrl *dp_ctrl_get(struct dp_ctrl_in *in)
	dp_ctrl->init      = dp_ctrl_host_init;
	dp_ctrl->deinit    = dp_ctrl_host_deinit;
	dp_ctrl->on        = dp_ctrl_on;
	dp_ctrl->handle_sink_request = dp_ctrl_handle_sink_request;
	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;

	return dp_ctrl;
error:
+6 −2
Original line number Diff line number Diff line
@@ -23,10 +23,14 @@
#include "dp_catalog.h"

struct dp_ctrl {

	int (*init)(struct dp_ctrl *dp_ctrl, bool flip);
	void (*deinit)(struct dp_ctrl *dp_ctrl);
	int (*on)(struct dp_ctrl *dp_ctrl, bool hpd_irq);
	void (*off)(struct dp_ctrl *dp_ctrl, bool hpd_irq);
	int (*on)(struct dp_ctrl *dp_ctrl);
	void (*off)(struct dp_ctrl *dp_ctrl);
	int (*handle_sink_request)(struct dp_ctrl *dp_ctrl,
		u32 sink_request);
	void (*reset)(struct dp_ctrl *dp_ctrl);
	void (*push_idle)(struct dp_ctrl *dp_ctrl);
	void (*abort)(struct dp_ctrl *dp_ctrl);
	void (*isr)(struct dp_ctrl *dp_ctrl);
+71 −32
Original line number Diff line number Diff line
@@ -434,6 +434,38 @@ static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
		(dp->link->sink_count.count == 0);
}

static int dp_display_send_hpd_notification(struct dp_display_private *dp,
		bool hpd)
{

	if ((hpd && dp->dp_display.is_connected) ||
			(!hpd && !dp->dp_display.is_connected)) {
		pr_info("HPD already %s\n", (hpd ? "on" : "off"));
		return 0;
	}

	dp->dp_display.is_connected = hpd;
	reinit_completion(&dp->notification_comp);
	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);

	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) {
		pr_warn("timeout\n");
		return -EINVAL;
	}

	return 0;
}

static void dp_display_handle_test_edid(struct dp_display_private *dp)
{
	if (dp->link->sink_request & DP_TEST_LINK_EDID_READ) {
		drm_dp_dpcd_write(dp->aux->drm_aux, DP_TEST_EDID_CHECKSUM,
			&dp->panel->edid_ctrl->edid->checksum, 1);
		drm_dp_dpcd_write(dp->aux->drm_aux, DP_TEST_RESPONSE,
			&dp->link->test_response, 1);
	}
}

static int dp_display_process_hpd_high(struct dp_display_private *dp)
{
	int rc = 0;
@@ -456,18 +488,14 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)

	dp->audio_supported = drm_detect_monitor_audio(edid);

	dp_display_handle_test_edid(dp);

	max_pclk_from_edid = dp->panel->get_max_pclk(dp->panel);

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

	dp->dp_display.is_connected = true;

	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);

	reinit_completion(&dp->notification_comp);
	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2))
		pr_warn("timeout\n");
	dp_display_send_hpd_notification(dp, true);

end:
	return rc;
@@ -519,12 +547,7 @@ static void dp_display_process_hpd_low(struct dp_display_private *dp)
	if (dp->audio_supported)
		dp->audio->off(dp->audio);

	dp->dp_display.is_connected = false;
	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);

	reinit_completion(&dp->notification_comp);
	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2))
		pr_warn("timeout\n");
	dp_display_send_hpd_notification(dp, false);
}

static int dp_display_usbpd_configure_cb(struct device *dev)
@@ -564,7 +587,7 @@ static void dp_display_clean(struct dp_display_private *dp)
	}

	dp->ctrl->push_idle(dp->ctrl);
	dp->ctrl->off(dp->ctrl, false);
	dp->ctrl->off(dp->ctrl);
}

static int dp_display_usbpd_disconnect_cb(struct device *dev)
@@ -591,32 +614,34 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
	if (dp->audio_supported)
		dp->audio->off(dp->audio);

	dp->dp_display.is_connected = false;
	drm_helper_hpd_irq_event(dp->dp_display.connector->dev);

	reinit_completion(&dp->notification_comp);
	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2)) {
		pr_warn("timeout\n");
	rc = dp_display_send_hpd_notification(dp, false);

		if (dp->power_on)
	if ((rc < 0) && dp->power_on)
		dp_display_clean(dp);
	}

	dp_display_host_deinit(dp);
end:
	return rc;
}

static void dp_display_link_maintenance(struct dp_display_private *dp)
{
	if (dp->power_on) {
		pr_debug("Set DP ctrl to push idle for link maintenance\n");
		dp->ctrl->push_idle(dp->ctrl);
		dp->ctrl->reset(dp->ctrl);
	} else {
		pr_err("DP not powered on, skip link maintenance\n");
		return;
	}

	dp->ctrl->handle_sink_request(dp->ctrl, dp->link->sink_request);
}

static int dp_display_handle_hpd_irq(struct dp_display_private *dp)
{
	if (dp->link->sink_request & DS_PORT_STATUS_CHANGED) {
		dp->dp_display.is_connected = false;
		drm_helper_hpd_irq_event(dp->dp_display.connector->dev);

		reinit_completion(&dp->notification_comp);
		if (!wait_for_completion_timeout(
			&dp->notification_comp, HZ * 2))
			pr_warn("timeout\n");
		dp_display_send_hpd_notification(dp, false);

		if (dp_display_is_sink_count_zero(dp)) {
			pr_debug("sink count is zero, nothing to do\n");
@@ -626,6 +651,15 @@ static int dp_display_handle_hpd_irq(struct dp_display_private *dp)
		return dp_display_process_hpd_high(dp);
	}

	if (dp->link->sink_request & DP_LINK_STATUS_UPDATED)
		dp_display_link_maintenance(dp);

	if (dp->link->sink_request & DP_TEST_LINK_TRAINING) {
		drm_dp_dpcd_write(dp->aux->drm_aux, DP_TEST_RESPONSE,
			&dp->link->test_response, 1);
		dp_display_link_maintenance(dp);
	}

	return 0;
}

@@ -804,7 +838,12 @@ static int dp_display_enable(struct dp_display *dp_display)

	dp = container_of(dp_display, struct dp_display_private, dp_display);

	rc = dp->ctrl->on(dp->ctrl, dp->hpd_irq_on);
	if (dp->power_on) {
		pr_debug("Link already setup, return\n");
		return 0;
	}

	rc = dp->ctrl->on(dp->ctrl);
	if (!rc)
		dp->power_on = true;
error:
@@ -887,7 +926,7 @@ static int dp_display_disable(struct dp_display *dp_display)
	if (!dp->power_on || !dp->core_initialized)
		goto error;

	dp->ctrl->off(dp->ctrl, dp->hpd_irq_on);
	dp->ctrl->off(dp->ctrl);

	dp->power_on = false;