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

Commit 58717281 authored by Padmanabhan Komanduru's avatar Padmanabhan Komanduru
Browse files

drm/msm/dp: fix handling of branch devices



A branch device usually notifies a change in downstream connections
using the HPD IRQ pulse. Handle this by checking for a change in
downstream sink count and appropriately handling EDID reads. It is
also possible that the branch device may not have any local EDID.
In such cases, when the downstream sink count is zero, do not read
EDID.

CRs-Fixed: 2093830
Change-Id: I230560c995d7c3b395e37aef5483e5468e1d1dec
Signed-off-by: default avatarAravind Venkateswaran <aravindh@codeaurora.org>
Signed-off-by: default avatarPadmanabhan Komanduru <pkomandu@codeaurora.org>
parent c28b7776
Loading
Loading
Loading
Loading
+51 −2
Original line number Diff line number Diff line
@@ -422,12 +422,34 @@ static const struct component_ops dp_display_comp_ops = {
	.unbind = dp_display_unbind,
};

static bool dp_display_is_ds_bridge(struct dp_panel *panel)
{
	return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] &
		DP_DWN_STRM_PORT_PRESENT);
}

static bool dp_display_is_sink_count_zero(struct dp_display_private *dp)
{
	return dp_display_is_ds_bridge(dp->panel) &&
		(dp->link->sink_count.count == 0);
}

static int dp_display_process_hpd_high(struct dp_display_private *dp)
{
	int rc = 0;
	u32 max_pclk_from_edid = 0;
	struct edid *edid;

	rc = dp->link->get_sink_count(dp->link);
	if (rc)
		goto end;

	if (dp_display_is_sink_count_zero(dp)) {
		pr_debug("no downstream devices connected\n");
		rc = -EINVAL;
		goto end;
	}

	rc = dp->panel->read_sink_caps(dp->panel, dp->dp_display.connector);
	if (rc)
		return rc;
@@ -449,6 +471,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)
	if (!wait_for_completion_timeout(&dp->notification_comp, HZ * 2))
		pr_warn("timeout\n");

end:
	return rc;
}

@@ -586,6 +609,28 @@ static int dp_display_usbpd_disconnect_cb(struct device *dev)
	return rc;
}

static int dp_display_handle_hpd_irq(struct dp_display_private *dp)
{
	if (dp->link->test_requested & 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");

		if (dp_display_is_sink_count_zero(dp)) {
			pr_debug("sink count is zero, nothing to do\n");
			return 0;
		}

		return dp_display_process_hpd_high(dp);
	}

	return 0;
}

static int dp_display_usbpd_attention_cb(struct device *dev)
{
	int rc = 0;
@@ -611,10 +656,14 @@ static int dp_display_usbpd_attention_cb(struct device *dev)
		}

		rc = dp->link->process_request(dp->link);
		/* check for any test request issued by sink */
		if (!rc) {
			dp_display_handle_hpd_irq(dp);
			dp->hpd_irq_on = false;
		if (!rc)
			goto end;
		}
		dp->hpd_irq_on = false;
	}

	if (!dp->usbpd->hpd_high) {
		dp_display_process_hpd_low(dp);
+30 −25
Original line number Diff line number Diff line
@@ -80,18 +80,13 @@ struct dp_link_request {
	u32 response;
};

struct dp_link_sink_count {
	u32 count;
	bool cp_ready;
};

struct dp_link_private {
	u32 prev_sink_count;
	struct device *dev;
	struct dp_aux *aux;
	struct dp_link dp_link;

	struct dp_link_request request;
	struct dp_link_sink_count sink_count;
	u8 link_status[DP_LINK_STATUS_SIZE];
};

@@ -992,41 +987,44 @@ static int dp_link_parse_request(struct dp_link_private *link)
 * (Byte 0x200), and whether all the sink devices connected have Content
 * Protection enabled.
 */
static void dp_link_parse_sink_count(struct dp_link_private *link)
static int dp_link_parse_sink_count(struct dp_link *dp_link)
{
	u8 bp;
	u8 data;
	int rlen;
	int const param_len = 0x1;
	struct dp_link_private *link = container_of(dp_link,
			struct dp_link_private, dp_link);

	rlen = drm_dp_dpcd_read(link->aux->drm_aux, DP_SINK_COUNT,
			&bp, param_len);
			&link->dp_link.sink_count.count, param_len);
	if (rlen < param_len) {
		pr_err("failed to read sink count\n");
		return;
		return -EINVAL;
	}

	data = bp;

	link->dp_link.sink_count.cp_ready =
		link->dp_link.sink_count.count & DP_SINK_CP_READY;
	/* BIT 7, BIT 5:0 */
	link->sink_count.count = (data & BIT(7)) << 6 | (data & 0x63);
	/* BIT 6*/
	link->sink_count.cp_ready = data & BIT(6);
	link->dp_link.sink_count.count =
		DP_GET_SINK_COUNT(link->dp_link.sink_count.count);

	pr_debug("sink_count = 0x%x, cp_ready = 0x%x\n",
		link->sink_count.count, link->sink_count.cp_ready);
		link->dp_link.sink_count.count,
		link->dp_link.sink_count.cp_ready);
	return 0;
}

static void dp_link_parse_sink_status_field(struct dp_link_private *link)
{
	int len = 0;

	dp_link_parse_sink_count(link);
	dp_link_parse_request(link);
	link->prev_sink_count = link->dp_link.sink_count.count;
	dp_link_parse_sink_count(&link->dp_link);

	len = drm_dp_dpcd_read_link_status(link->aux->drm_aux,
		link->link_status);
	if (len < DP_LINK_STATUS_SIZE)
		pr_err("DP link status read failed\n");
	dp_link_parse_request(link);
}

static bool dp_link_is_link_training_requested(struct dp_link_private *link)
@@ -1221,6 +1219,9 @@ static bool dp_link_is_ds_port_status_changed(struct dp_link_private *link)
		DP_DOWNSTREAM_PORT_STATUS_CHANGED) /* port status changed */
		return true;

	if (link->prev_sink_count != link->dp_link.sink_count.count)
		return true;

	return false;
}

@@ -1240,6 +1241,9 @@ static int dp_link_process_ds_port_status_change(struct dp_link_private *link)
	if (!dp_link_is_ds_port_status_changed(link))
		return -EINVAL;

	/* reset prev_sink_count */
	link->prev_sink_count = link->dp_link.sink_count.count;

	return 0;
}

@@ -1351,6 +1355,12 @@ static int dp_link_process_request(struct dp_link *dp_link)

	dp_link_parse_sink_status_field(link);

	ret = dp_link_process_ds_port_status_change(link);
	if (!ret) {
		dp_link->test_requested |= DS_PORT_STATUS_CHANGED;
		goto exit;
	}

	ret = dp_link_process_link_training_request(link);
	if (!ret) {
		dp_link->test_requested |= DP_TEST_LINK_TRAINING;
@@ -1369,12 +1379,6 @@ static int dp_link_process_request(struct dp_link *dp_link)
		goto exit;
	}

	ret = dp_link_process_ds_port_status_change(link);
	if (!ret) {
		dp_link->test_requested |= DS_PORT_STATUS_CHANGED;
		goto exit;
	}

	ret = dp_link_process_video_pattern_request(link);
	if (!ret) {
		dp_link->test_requested |= DP_TEST_LINK_VIDEO_PATTERN;
@@ -1556,6 +1560,7 @@ struct dp_link *dp_link_get(struct device *dev, struct dp_aux *aux)
	dp_link = &link->dp_link;

	dp_link->process_request        = dp_link_process_request;
	dp_link->get_sink_count         = dp_link_parse_sink_count;
	dp_link->get_test_bits_depth    = dp_link_get_test_bits_depth;
	dp_link->get_colorimetry_config = dp_link_get_colorimetry_config;
	dp_link->adjust_levels          = dp_link_adjust_levels;
+8 −0
Original line number Diff line number Diff line
@@ -34,6 +34,11 @@ enum dp_link_preemaphasis_level {
#define DS_PORT_STATUS_CHANGED 0x200
#define DP_TEST_BIT_DEPTH_UNKNOWN 0xFFFFFFFF

struct dp_link_sink_count {
	u32 count;
	bool cp_ready;
};

struct dp_link {
	u32 test_requested;

@@ -42,8 +47,11 @@ struct dp_link {
	u32 v_level;
	u32 p_level;

	struct dp_link_sink_count sink_count;

	u32 (*get_test_bits_depth)(struct dp_link *dp_link, u32 bpp);
	int (*process_request)(struct dp_link *dp_link);
	int (*get_sink_count)(struct dp_link *dp_link);
	int (*get_colorimetry_config)(struct dp_link *dp_link);
	int (*adjust_levels)(struct dp_link *dp_link, u8 *link_status);
	int (*send_psm_request)(struct dp_link *dp_link, bool req);
+26 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
#include "dp_panel.h"

#define DP_PANEL_DEFAULT_BPP 24
#define DP_MAX_DS_PORT_COUNT 1

enum {
	DP_LINK_RATE_MULTIPLIER = 27000000,
@@ -35,7 +36,8 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
	int rlen, rc = 0;
	struct dp_panel_private *panel;
	struct drm_dp_link *link_info;
	u8 major = 0, minor = 0;
	u8 *dpcd, major = 0, minor = 0;
	u32 dfp_count = 0;
	unsigned long caps = DP_LINK_CAP_ENHANCED_FRAMING;

	if (!dp_panel) {
@@ -44,11 +46,13 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)
		goto end;
	}

	dpcd = dp_panel->dpcd;

	panel = container_of(dp_panel, struct dp_panel_private, dp_panel);
	link_info = &dp_panel->link_info;

	rlen = drm_dp_dpcd_read(panel->aux->drm_aux, DP_DPCD_REV,
		dp_panel->dpcd, (DP_RECEIVER_CAP_SIZE + 1));
		dpcd, (DP_RECEIVER_CAP_SIZE + 1));
	if (rlen < (DP_RECEIVER_CAP_SIZE + 1)) {
		pr_err("dpcd read failed, rlen=%d\n", rlen);
		rc = -EINVAL;
@@ -70,9 +74,28 @@ static int dp_panel_read_dpcd(struct dp_panel *dp_panel)

	pr_debug("lane_count=%d\n", link_info->num_lanes);

	if (dp_panel->dpcd[DP_MAX_LANE_COUNT] & DP_ENHANCED_FRAME_CAP)
	if (drm_dp_enhanced_frame_cap(dpcd))
		link_info->capabilities |= caps;

	dfp_count = dpcd[DP_DOWN_STREAM_PORT_COUNT] &
						DP_DOWN_STREAM_PORT_COUNT;

	if ((dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_PRESENT)
		&& (dpcd[DP_DPCD_REV] > 0x10)) {
		rlen = drm_dp_dpcd_read(panel->aux->drm_aux,
			DP_DOWNSTREAM_PORT_0, dp_panel->ds_ports,
			DP_MAX_DOWNSTREAM_PORTS);
		if (rlen < DP_MAX_DOWNSTREAM_PORTS) {
			pr_err("ds port status failed, rlen=%d\n", rlen);
			rc = -EINVAL;
			goto end;
		}
	}

	if (dfp_count > DP_MAX_DS_PORT_COUNT)
		pr_debug("DS port count %d greater that max (%d) supported\n",
			dfp_count, DP_MAX_DS_PORT_COUNT);

end:
	return rc;
}
+4 −1
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@
#include "dp_aux.h"
#include "sde_edid_parser.h"

#define DP_MAX_DOWNSTREAM_PORTS 0x10

struct dp_panel_info {
	u32 h_active;
	u32 v_active;
@@ -38,8 +40,9 @@ struct dp_panel_info {
struct dp_panel {
	/* dpcd raw data */
	u8 dpcd[DP_RECEIVER_CAP_SIZE];
	struct drm_dp_link link_info;
	u8 ds_ports[DP_MAX_DOWNSTREAM_PORTS];

	struct drm_dp_link link_info;
	struct sde_edid_ctrl *edid_ctrl;
	struct drm_connector *connector;
	struct dp_panel_info pinfo;