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

Commit b8e98a9c authored by Rohith Palakurthi's avatar Rohith Palakurthi
Browse files

Merge remote-tracking branch 'quic/dev/msm-4.14-display' into msm-4.14



* quic/dev/msm-4.14-display:
  drm/msm/sde: fix memory leak during hardware catalog init
  msm/sde/rotator: add check for vbif dt properties
  drm/msm/dp: add support for HDR over MST
  drm/msm/dp: add encoder and connector reservation dp-mst

Change-Id: I4327bc749cc011935c63e55965d916236a7d9283
Signed-off-by: default avatarRohith Palakurthi <prohit@codeaurora.org>
parents 03cafa97 4f3db128
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -107,6 +107,7 @@ Optional properties:
				controller. These pin configurations are installed in the pinctrl
				device node. Refer to pinctrl-bindings.txt
- qcom,max-lclk-frequency-khz:	An integer specifying the max. link clock in KHz supported by Display Port.
- qcom,mst-fixed-topology-ports: u32 values of which MST output port to reserve, start from one

[Optional child nodes]: These nodes are for devices which are
dependent on msm_ext_disp. If msm_ext_disp is disabled then
+66 −26
Original line number Diff line number Diff line
@@ -379,7 +379,7 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
	struct dp_catalog_private *catalog;
	struct drm_msm_ext_hdr_metadata *hdr;
	struct dp_io_data *io_data;
	u32 header, parity, data;
	u32 header, parity, data, mst_offset = 0;
	u8 buf[SZ_128], off = 0;

	if (!panel) {
@@ -387,6 +387,14 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
		return;
	}

	if (panel->stream_id >= DP_STREAM_MAX) {
		pr_err("invalid stream_id:%d\n", panel->stream_id);
		return;
	}

	if (panel->stream_id == DP_STREAM_1)
		mst_offset = MMSS_DP1_VSCEXT_0 - MMSS_DP_VSCEXT_0;

	catalog = dp_catalog_get_priv(panel);
	hdr = &panel->hdr_data.hdr_meta;
	io_data = catalog->io.dp_link;
@@ -396,7 +404,8 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
	parity = dp_header_get_parity(header);
	data   = ((header << HEADER_BYTE_1_BIT)
			| (parity << PARITY_BYTE_1_BIT));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_0, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_0 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -405,22 +414,26 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
	parity = dp_header_get_parity(header);
	data   = ((header << HEADER_BYTE_2_BIT)
			| (parity << PARITY_BYTE_2_BIT));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_1, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_1 + mst_offset,
			data);

	/* HEADER BYTE 3 */
	header = panel->hdr_data.vscext_header_byte3;
	parity = dp_header_get_parity(header);
	data   = ((header << HEADER_BYTE_3_BIT)
			| (parity << PARITY_BYTE_3_BIT));
	data |= dp_read(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_1);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_1, data);
	data |= dp_read(catalog->exe_mode, io_data,
			MMSS_DP_VSCEXT_1 + mst_offset);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_1 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	data = panel->hdr_data.version;
	data |= panel->hdr_data.length << 8;
	data |= hdr->eotf << 16;
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_2, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_2 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -428,7 +441,8 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
		(DP_GET_MSB(hdr->display_primaries_x[0]) << 8) |
		(DP_GET_LSB(hdr->display_primaries_y[0]) << 16) |
		(DP_GET_MSB(hdr->display_primaries_y[0]) << 24));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_3, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_3 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -436,7 +450,8 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
		(DP_GET_MSB(hdr->display_primaries_x[1]) << 8) |
		(DP_GET_LSB(hdr->display_primaries_y[1]) << 16) |
		(DP_GET_MSB(hdr->display_primaries_y[1]) << 24));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_4, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_4 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -444,7 +459,8 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
		(DP_GET_MSB(hdr->display_primaries_x[2]) << 8) |
		(DP_GET_LSB(hdr->display_primaries_y[2]) << 16) |
		(DP_GET_MSB(hdr->display_primaries_y[2]) << 24));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_5, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_5 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -452,7 +468,8 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
		(DP_GET_MSB(hdr->white_point_x) << 8) |
		(DP_GET_LSB(hdr->white_point_y) << 16) |
		(DP_GET_MSB(hdr->white_point_y) << 24));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_6, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_6 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -460,7 +477,8 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
		(DP_GET_MSB(hdr->max_luminance) << 8) |
		(DP_GET_LSB(hdr->min_luminance) << 16) |
		(DP_GET_MSB(hdr->min_luminance) << 24));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_7, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_7 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -468,12 +486,14 @@ static void dp_catalog_panel_setup_infoframe_sdp(struct dp_catalog_panel *panel)
		(DP_GET_MSB(hdr->max_content_light_level) << 8) |
		(DP_GET_LSB(hdr->max_average_light_level) << 16) |
		(DP_GET_MSB(hdr->max_average_light_level) << 24));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_8, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_8 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	data = 0;
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_9, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_VSCEXT_9 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -485,7 +505,7 @@ static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
{
	struct dp_catalog_private *catalog;
	struct dp_io_data *io_data;
	u32 header, parity, data;
	u32 header, parity, data, mst_offset = 0;
	u8 bpc, off = 0;
	u8 buf[SZ_128];

@@ -494,6 +514,14 @@ static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
		return;
	}

	if (panel->stream_id >= DP_STREAM_MAX) {
		pr_err("invalid stream_id:%d\n", panel->stream_id);
		return;
	}

	if (panel->stream_id == DP_STREAM_1)
		mst_offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;

	catalog = dp_catalog_get_priv(panel);
	io_data = catalog->io.dp_link;

@@ -502,7 +530,8 @@ static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
	parity = dp_header_get_parity(header);
	data   = ((header << HEADER_BYTE_1_BIT)
			| (parity << PARITY_BYTE_1_BIT));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_0, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_0 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -511,32 +540,39 @@ static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
	parity = dp_header_get_parity(header);
	data   = ((header << HEADER_BYTE_2_BIT)
			| (parity << PARITY_BYTE_2_BIT));
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_1, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_1 + mst_offset,
			data);

	/* HEADER BYTE 3 */
	header = panel->hdr_data.vsc_header_byte3;
	parity = dp_header_get_parity(header);
	data   = ((header << HEADER_BYTE_3_BIT)
			| (parity << PARITY_BYTE_3_BIT));
	data |= dp_read(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_1);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_1, data);
	data |= dp_read(catalog->exe_mode, io_data,
			MMSS_DP_GENERIC0_1 + mst_offset);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_1 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	data = 0;
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_2, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_2 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_3, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_3 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_4, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_4 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_5, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_5 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

@@ -559,20 +595,24 @@ static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
		((panel->hdr_data.dynamic_range & 0x1) << 15) |
		((panel->hdr_data.content_type & 0x7) << 16);

	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_6, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_6 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	data = 0;
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_7, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_7 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_8, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_8 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_9, data);
	dp_write(catalog->exe_mode, io_data, MMSS_DP_GENERIC0_9 + mst_offset,
			data);
	memcpy(buf + off, &data, sizeof(data));
	off += sizeof(data);

+75 −0
Original line number Diff line number Diff line
@@ -2364,6 +2364,77 @@ static int dp_display_update_pps(struct dp_display *dp_display,
	return 0;
}

static int dp_display_mst_connector_update_link_info(
			struct dp_display *dp_display,
			struct drm_connector *connector)
{
	int rc = 0;
	struct sde_connector *sde_conn;
	struct dp_panel *dp_panel;
	struct dp_display_private *dp;

	if (!dp_display || !connector) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

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

	if (!dp->mst.drm_registered) {
		pr_debug("drm mst not registered\n");
		return -EPERM;
	}

	sde_conn = to_sde_connector(connector);
	if (!sde_conn->drv_panel) {
		pr_err("invalid panel for connector:%d\n", connector->base.id);
		return -EINVAL;
	}

	dp_panel = sde_conn->drv_panel;

	memcpy(dp_panel->dpcd, dp->panel->dpcd,
			DP_RECEIVER_CAP_SIZE + 1);
	memcpy(dp_panel->dsc_dpcd, dp->panel->dsc_dpcd,
			DP_RECEIVER_DSC_CAP_SIZE + 1);
	memcpy(&dp_panel->link_info, &dp->panel->link_info,
			sizeof(dp_panel->link_info));

	DP_MST_DEBUG("dp mst connector:%d link info updated\n");

	return rc;
}

static int dp_display_mst_get_fixed_topology_port(
			struct dp_display *dp_display,
			u32 strm_id, u32 *port_num)
{
	struct dp_display_private *dp;
	u32 port;

	if (!dp_display) {
		pr_err("invalid input\n");
		return -EINVAL;
	}

	if (strm_id >= DP_STREAM_MAX) {
		pr_err("invalid stream id:%d\n", strm_id);
		return -EINVAL;
	}

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

	port = dp->parser->mst_fixed_port[strm_id];

	if (!port || port > 255)
		return -ENOENT;

	if (port_num)
		*port_num = port;

	return 0;
}

static int dp_display_get_mst_caps(struct dp_display *dp_display,
			struct dp_mst_caps *mst_caps)
{
@@ -2447,12 +2518,16 @@ static int dp_display_probe(struct platform_device *pdev)
					dp_display_mst_connector_uninstall;
	g_dp_display->mst_connector_update_edid =
					dp_display_mst_connector_update_edid;
	g_dp_display->mst_connector_update_link_info =
				dp_display_mst_connector_update_link_info;
	g_dp_display->get_mst_caps = dp_display_get_mst_caps;
	g_dp_display->set_stream_info = dp_display_set_stream_info;
	g_dp_display->update_pps = dp_display_update_pps;
	g_dp_display->convert_to_dp_mode = dp_display_convert_to_dp_mode;
	g_dp_display->mst_get_connector_info =
					dp_display_mst_get_connector_info;
	g_dp_display->mst_get_fixed_topology_port =
					dp_display_mst_get_fixed_topology_port;

	rc = component_add(&pdev->dev, &dp_display_comp_ops);
	if (rc) {
+4 −0
Original line number Diff line number Diff line
@@ -108,9 +108,13 @@ struct dp_display {
	int (*mst_connector_update_edid)(struct dp_display *dp_display,
			struct drm_connector *connector,
			struct edid *edid);
	int (*mst_connector_update_link_info)(struct dp_display *dp_display,
			struct drm_connector *connector);
	int (*mst_get_connector_info)(struct dp_display *dp_display,
			struct drm_connector *connector,
			struct dp_mst_connector *mst_conn);
	int (*mst_get_fixed_topology_port)(struct dp_display *dp_display,
			u32 strm_id, u32 *port_num);
	int (*get_mst_caps)(struct dp_display *dp_display,
			struct dp_mst_caps *mst_caps);
	int (*set_stream_info)(struct dp_display *dp_display, void *panel,
+325 −2
Original line number Diff line number Diff line
@@ -32,7 +32,6 @@
#define DP_MST_DEBUG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__)
#define DP_MST_INFO_LOG(fmt, ...) pr_debug(fmt, ##__VA_ARGS__)

#define MAX_DP_MST_STREAMS		2
#define MAX_DP_MST_DRM_ENCODERS		2
#define MAX_DP_MST_DRM_BRIDGES		2
#define HPD_STRING_SIZE			30
@@ -111,6 +110,10 @@ struct dp_mst_bridge {
	int pbn;
	int num_slots;
	int start_slot;

	u32 fixed_port_num;
	bool fixed_port_added;
	struct drm_connector *fixed_connector;
};

struct dp_mst_private {
@@ -911,6 +914,10 @@ static void dp_mst_bridge_mode_set(struct drm_bridge *drm_bridge,

/* DP MST Bridge APIs */

static struct drm_connector *
dp_mst_drm_fixed_connector_init(struct dp_display *dp_display,
				struct drm_encoder *encoder);

static const struct drm_bridge_funcs dp_mst_bridge_ops = {
	.attach       = dp_mst_bridge_attach,
	.mode_fixup   = dp_mst_bridge_mode_fixup,
@@ -978,6 +985,23 @@ int dp_mst_drm_bridge_init(void *data, struct drm_encoder *encoder)

	DP_MST_DEBUG("mst drm bridge init. bridge id:%d\n", i);

	/*
	 * If fixed topology port is defined, connector will be created
	 * immediately.
	 */
	rc = display->mst_get_fixed_topology_port(display, bridge->id,
			&bridge->fixed_port_num);
	if (!rc) {
		bridge->fixed_connector =
			dp_mst_drm_fixed_connector_init(display,
				bridge->encoder);
		if (bridge->fixed_connector == NULL) {
			pr_err("failed to create fixed connector\n");
			rc = -ENOMEM;
			goto end;
		}
	}

	return 0;

end:
@@ -1170,7 +1194,8 @@ dp_mst_atomic_best_encoder(struct drm_connector *connector,
	}

	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (!mst->mst_bridge[i].encoder_active_sts) {
		if (!mst->mst_bridge[i].encoder_active_sts &&
			!mst->mst_bridge[i].fixed_connector) {
			mst->mst_bridge[i].encoder_active_sts = true;
			mst->mst_bridge[i].connector = connector;
			mst->mst_bridge[i].dp_panel = conn->drv_panel;
@@ -1377,6 +1402,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,

	if (!connector) {
		pr_err("mst sde_connector_init failed\n");
		drm_modeset_unlock_all(dev);
		return connector;
	}

@@ -1384,6 +1410,7 @@ dp_mst_add_connector(struct drm_dp_mst_topology_mgr *mgr,
	if (rc) {
		pr_err("mst connector install failed\n");
		sde_connector_destroy(connector);
		drm_modeset_unlock_all(dev);
		return NULL;
	}

@@ -1433,6 +1460,291 @@ static void dp_mst_destroy_connector(struct drm_dp_mst_topology_mgr *mgr,
	drm_connector_unreference(connector);
}

static enum drm_connector_status
dp_mst_fixed_connector_detect(struct drm_connector *connector, bool force,
			void *display)
{
	struct dp_display *dp_display = display;
	struct dp_mst_private *mst = dp_display->dp_mst_prv_info;
	int i;

	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (mst->mst_bridge[i].fixed_connector != connector)
			continue;

		if (!mst->mst_bridge[i].fixed_port_added)
			break;

		return dp_mst_connector_detect(connector, force, display);
	}

	return connector_status_disconnected;
}

static struct drm_encoder *
dp_mst_fixed_atomic_best_encoder(struct drm_connector *connector,
			void *display, struct drm_connector_state *state)
{
	struct dp_display *dp_display = display;
	struct dp_mst_private *mst = dp_display->dp_mst_prv_info;
	struct sde_connector *conn = to_sde_connector(connector);
	struct drm_encoder *enc = NULL;
	u32 i;

	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (mst->mst_bridge[i].connector == connector) {
			enc = mst->mst_bridge[i].encoder;
			goto end;
		}
	}

	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (mst->mst_bridge[i].fixed_connector == connector) {
			mst->mst_bridge[i].encoder_active_sts = true;
			mst->mst_bridge[i].connector = connector;
			mst->mst_bridge[i].dp_panel = conn->drv_panel;
			enc = mst->mst_bridge[i].encoder;
			break;
		}
	}

end:
	if (enc)
		DP_MST_DEBUG("mst connector:%d atomic best encoder:%d\n",
			connector->base.id, i);
	else
		DP_MST_DEBUG("mst connector:%d atomic best encoder failed\n",
				connector->base.id);

	return enc;
}

static u32 dp_mst_find_fixed_port_num(struct drm_dp_mst_branch *mstb,
		struct drm_dp_mst_port *target)
{
	struct drm_dp_mst_port *port;
	u32 port_num = 0;

	/*
	 * search through reversed order of adding sequence, so the port number
	 * will be unique once topology is fixed
	 */
	list_for_each_entry_reverse(port, &mstb->ports, next) {
		if (port->mstb)
			port_num += dp_mst_find_fixed_port_num(port->mstb,
						target);
		else if (!port->input) {
			++port_num;
			if (port == target)
				break;
		}
	}

	return port_num;
}

static struct drm_connector *
dp_mst_find_fixed_connector(struct dp_mst_private *dp_mst,
		struct drm_dp_mst_port *port)
{
	struct dp_display *dp_display = dp_mst->dp_display;
	struct drm_connector *connector = NULL;
	struct sde_connector *c_conn;
	u32 port_num;
	int i;

	mutex_lock(&port->mgr->lock);
	port_num = dp_mst_find_fixed_port_num(port->mgr->mst_primary, port);
	mutex_unlock(&port->mgr->lock);

	if (!port_num)
		return NULL;

	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (dp_mst->mst_bridge[i].fixed_port_num == port_num) {
			connector = dp_mst->mst_bridge[i].fixed_connector;
			c_conn = to_sde_connector(connector);
			c_conn->mst_port = port;
			dp_display->mst_connector_update_link_info(dp_display,
					connector);
			dp_mst->mst_bridge[i].fixed_port_added = true;
			DP_MST_DEBUG("found fixed connector %d\n",
					DRMID(connector));
			break;
		}
	}

	return connector;
}

static int
dp_mst_find_first_available_encoder_idx(struct dp_mst_private *dp_mst)
{
	int enc_idx = MAX_DP_MST_DRM_BRIDGES;
	int i;

	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (!dp_mst->mst_bridge[i].fixed_connector) {
			enc_idx = i;
			break;
		}
	}

	return enc_idx;
}

static struct drm_connector *
dp_mst_add_fixed_connector(struct drm_dp_mst_topology_mgr *mgr,
		struct drm_dp_mst_port *port, const char *pathprop)
{
	struct dp_mst_private *dp_mst;
	struct drm_device *dev;
	struct dp_display *dp_display;
	struct drm_connector *connector;
	int i, enc_idx;

	DP_MST_DEBUG("enter\n");

	dp_mst = container_of(mgr, struct dp_mst_private, mst_mgr);

	dp_display = dp_mst->dp_display;
	dev = dp_display->drm_dev;

	if (port->input || port->mstb)
		enc_idx = MAX_DP_MST_DRM_BRIDGES;
	else {
		/* if port is already reserved, return immediately */
		connector = dp_mst_find_fixed_connector(dp_mst, port);
		if (connector != NULL)
			return connector;

		/* first available bridge index for non-reserved port */
		enc_idx = dp_mst_find_first_available_encoder_idx(dp_mst);
	}

	/* add normal connector */
	connector = dp_mst_add_connector(mgr, port, pathprop);
	if (!connector) {
		DP_MST_DEBUG("failed to add connector\n");
		return NULL;
	}

	drm_modeset_lock_all(dev);

	/* clear encoder list */
	for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++)
		connector->encoder_ids[i] = 0;

	/* re-attach encoders from first available encoders */
	for (i = enc_idx; i < MAX_DP_MST_DRM_BRIDGES; i++)
		drm_mode_connector_attach_encoder(connector,
				dp_mst->mst_bridge[i].encoder);

	drm_modeset_unlock_all(dev);

	DP_MST_DEBUG("add mst connector:%d\n", connector->base.id);

	return connector;
}

static void dp_mst_register_fixed_connector(struct drm_connector *connector)
{
	struct sde_connector *c_conn = to_sde_connector(connector);
	struct dp_display *dp_display = c_conn->display;
	struct dp_mst_private *dp_mst = dp_display->dp_mst_prv_info;
	int i;

	DP_MST_DEBUG("enter\n");

	/* skip connector registered for fixed topology ports */
	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (dp_mst->mst_bridge[i].fixed_connector == connector) {
			DP_MST_DEBUG("found fixed connector %d\n",
					DRMID(connector));
			return;
		}
	}

	dp_mst_register_connector(connector);
}

static void dp_mst_destroy_fixed_connector(struct drm_dp_mst_topology_mgr *mgr,
					   struct drm_connector *connector)
{
	struct dp_mst_private *dp_mst;
	int i;

	DP_MST_DEBUG("enter\n");

	dp_mst = container_of(mgr, struct dp_mst_private, mst_mgr);

	/* skip connector destroy for fixed topology ports */
	for (i = 0; i < MAX_DP_MST_DRM_BRIDGES; i++) {
		if (dp_mst->mst_bridge[i].fixed_connector == connector) {
			dp_mst->mst_bridge[i].fixed_port_added = false;
			DP_MST_DEBUG("destroy fixed connector %d\n",
					DRMID(connector));
			return;
		}
	}

	dp_mst_destroy_connector(mgr, connector);
}

static struct drm_connector *
dp_mst_drm_fixed_connector_init(struct dp_display *dp_display,
			struct drm_encoder *encoder)
{
	static const struct sde_connector_ops dp_mst_connector_ops = {
		.post_init  = NULL,
		.detect     = dp_mst_fixed_connector_detect,
		.get_modes  = dp_mst_connector_get_modes,
		.mode_valid = dp_mst_connector_mode_valid,
		.get_info   = dp_mst_connector_get_info,
		.get_mode_info  = dp_mst_connector_get_mode_info,
		.atomic_best_encoder = dp_mst_fixed_atomic_best_encoder,
		.atomic_check = dp_mst_connector_atomic_check,
		.config_hdr = dp_mst_connector_config_hdr,
		.pre_destroy = dp_mst_connector_pre_destroy,
	};
	struct drm_device *dev;
	struct drm_connector *connector;
	int rc;

	DP_MST_DEBUG("enter\n");

	dev = dp_display->drm_dev;

	connector = sde_connector_init(dev,
				encoder,
				NULL,
				dp_display,
				&dp_mst_connector_ops,
				DRM_CONNECTOR_POLL_HPD,
				DRM_MODE_CONNECTOR_DisplayPort);

	if (!connector) {
		pr_err("mst sde_connector_init failed\n");
		return NULL;
	}

	rc = dp_display->mst_connector_install(dp_display, connector);
	if (rc) {
		pr_err("mst connector install failed\n");
		sde_connector_destroy(connector);
		return NULL;
	}

	drm_object_attach_property(&connector->base,
			dev->mode_config.path_property, 0);
	drm_object_attach_property(&connector->base,
			dev->mode_config.tile_property, 0);

	DP_MST_DEBUG("add mst fixed connector:%d\n", connector->base.id);

	return connector;
}

static void dp_mst_hotplug(struct drm_dp_mst_topology_mgr *mgr)
{
	struct dp_mst_private *mst = container_of(mgr, struct dp_mst_private,
@@ -1578,6 +1890,13 @@ static const struct drm_dp_mst_topology_cbs dp_mst_drm_cbs = {
	.hotplug = dp_mst_hotplug,
};

static const struct drm_dp_mst_topology_cbs dp_mst_fixed_drm_cbs = {
	.add_connector = dp_mst_add_fixed_connector,
	.register_connector = dp_mst_register_fixed_connector,
	.destroy_connector = dp_mst_destroy_fixed_connector,
	.hotplug = dp_mst_hotplug,
};

static void dp_mst_sim_init(struct dp_mst_private *mst)
{
	INIT_WORK(&mst->simulator.probe_work, dp_mst_sim_link_probe_work);
@@ -1642,6 +1961,10 @@ int dp_mst_init(struct dp_display *dp_display)
	}
	memset(&dp_mst_enc_cache, 0, sizeof(dp_mst_enc_cache));

	/* choose fixed callback function if fixed topology is found */
	if (!dp_display->mst_get_fixed_topology_port(dp_display, 0, NULL))
		dp_mst.mst_mgr.cbs = &dp_mst_fixed_drm_cbs;

	DP_MST_INFO_LOG("dp drm mst topology manager init completed\n");

	return ret;
Loading