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

Commit 3edfaf7b authored by Xiaowen Wu's avatar Xiaowen Wu
Browse files

drm/msm/dsi-staging: add split DSI support with external bridge



Add split DSI support with external bridge. Multiple endpoints
are supported to attach all the mipi_dsi bridges.

Signed-off-by: default avatarXiaowen Wu <wxiaowen@codeaurora.org>
Change-Id: Ibf231f06d7fa493b5d0612bb4a69cb04f3579b0d
parent 2c16ce8d
Loading
Loading
Loading
Loading
+187 −59
Original line number Diff line number Diff line
@@ -3479,8 +3479,15 @@ static int dsi_display_parse_dt(struct dsi_display *display)
	/* Parse TE data */
	dsi_display_parse_te_data(display);

	/* Parse external bridge from port 0, reg 0 */
	display->ext_bridge_of = of_graph_get_remote_node(of_node, 0, 0);
	/* Parse all external bridges from port 0 */
	display_for_each_ctrl(i, display) {
		display->ext_bridge[i].node_of =
			of_graph_get_remote_node(of_node, 0, i);
		if (display->ext_bridge[i].node_of)
			display->ext_bridge_cnt++;
		else
			break;
	}

	pr_debug("success\n");
error:
@@ -5093,6 +5100,102 @@ static int dsi_display_ext_get_mode_info(struct drm_connector *connector,
	return 0;
}

static struct dsi_display_ext_bridge *dsi_display_ext_get_bridge(
		struct drm_bridge *bridge)
{
	struct msm_drm_private *priv;
	struct sde_kms *sde_kms;
	struct list_head *connector_list;
	struct drm_connector *conn_iter;
	struct sde_connector *sde_conn;
	struct dsi_display *display;
	int i;

	if (!bridge || !bridge->encoder) {
		SDE_ERROR("invalid argument\n");
		return NULL;
	}

	priv = bridge->dev->dev_private;
	sde_kms = to_sde_kms(priv->kms);
	connector_list = &sde_kms->dev->mode_config.connector_list;

	list_for_each_entry(conn_iter, connector_list, head) {
		sde_conn = to_sde_connector(conn_iter);
		if (sde_conn->encoder == bridge->encoder) {
			display = sde_conn->display;
			for (i = 0; i < display->ctrl_count; i++) {
				if (display->ext_bridge[i].bridge == bridge)
					return &display->ext_bridge[i];
			}
		}
	}

	return NULL;
}

static void dsi_display_drm_ext_adjust_timing(
		const struct dsi_display *display,
		struct drm_display_mode *mode)
{
	mode->hdisplay /= display->ctrl_count;
	mode->hsync_start /= display->ctrl_count;
	mode->hsync_end /= display->ctrl_count;
	mode->htotal /= display->ctrl_count;
	mode->hskew /= display->ctrl_count;
	mode->clock /= display->ctrl_count;
}

static enum drm_mode_status dsi_display_drm_ext_bridge_mode_valid(
		struct drm_bridge *bridge,
		const struct drm_display_mode *mode)
{
	struct dsi_display_ext_bridge *ext_bridge;
	struct drm_display_mode tmp;

	ext_bridge = dsi_display_ext_get_bridge(bridge);
	if (!ext_bridge)
		return MODE_ERROR;

	tmp = *mode;
	dsi_display_drm_ext_adjust_timing(ext_bridge->display, &tmp);
	return ext_bridge->orig_funcs->mode_valid(bridge, &tmp);
}

static bool dsi_display_drm_ext_bridge_mode_fixup(
		struct drm_bridge *bridge,
		const struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode)
{
	struct dsi_display_ext_bridge *ext_bridge;
	struct drm_display_mode tmp;

	ext_bridge = dsi_display_ext_get_bridge(bridge);
	if (!ext_bridge)
		return false;

	tmp = *mode;
	dsi_display_drm_ext_adjust_timing(ext_bridge->display, &tmp);
	return ext_bridge->orig_funcs->mode_fixup(bridge, &tmp, &tmp);
}

static void dsi_display_drm_ext_bridge_mode_set(
		struct drm_bridge *bridge,
		struct drm_display_mode *mode,
		struct drm_display_mode *adjusted_mode)
{
	struct dsi_display_ext_bridge *ext_bridge;
	struct drm_display_mode tmp;

	ext_bridge = dsi_display_ext_get_bridge(bridge);
	if (!ext_bridge)
		return;

	tmp = *mode;
	dsi_display_drm_ext_adjust_timing(ext_bridge->display, &tmp);
	ext_bridge->orig_funcs->mode_set(bridge, &tmp, &tmp);
}

static int dsi_host_ext_attach(struct mipi_dsi_host *host,
			   struct mipi_dsi_device *dsi)
{
@@ -5185,31 +5288,50 @@ int dsi_display_drm_ext_bridge_init(struct dsi_display *display,
	struct drm_bridge *ext_bridge;
	struct drm_connector *ext_conn;
	struct sde_connector *sde_conn = to_sde_connector(connector);
	int rc;
	struct drm_bridge *prev_bridge = bridge;
	int rc = 0, i;

	/* check if ext_bridge is already attached */
	if (display->ext_bridge)
		return 0;
	for (i = 0; i < display->ext_bridge_cnt; i++) {
		struct dsi_display_ext_bridge *ext_bridge_info =
				&display->ext_bridge[i];

	/* check if there is no external bridge defined */
	if (!display->ext_bridge_of)
		/* return if ext bridge is already initialized */
		if (ext_bridge_info->bridge)
			return 0;

	ext_bridge = of_drm_find_bridge(display->ext_bridge_of);
		ext_bridge = of_drm_find_bridge(ext_bridge_info->node_of);
		if (IS_ERR_OR_NULL(ext_bridge)) {
			rc = PTR_ERR(ext_bridge);
			pr_err("failed to find ext bridge\n");
			goto error;
		}

	rc = drm_bridge_attach(bridge->encoder, ext_bridge, bridge);
		/* override functions for mode adjustment */
		if (display->ext_bridge_cnt > 1) {
			ext_bridge_info->bridge_funcs = *ext_bridge->funcs;
			if (ext_bridge->funcs->mode_fixup)
				ext_bridge_info->bridge_funcs.mode_fixup =
					dsi_display_drm_ext_bridge_mode_fixup;
			if (ext_bridge->funcs->mode_valid)
				ext_bridge_info->bridge_funcs.mode_valid =
					dsi_display_drm_ext_bridge_mode_valid;
			if (ext_bridge->funcs->mode_set)
				ext_bridge_info->bridge_funcs.mode_set =
					dsi_display_drm_ext_bridge_mode_set;
			ext_bridge_info->orig_funcs = ext_bridge->funcs;
			ext_bridge->funcs = &ext_bridge_info->bridge_funcs;
		}

		rc = drm_bridge_attach(encoder, ext_bridge, prev_bridge);
		if (rc) {
			pr_err("[%s] ext brige attach failed, %d\n",
				display->name, rc);
			goto error;
		}

	display->ext_bridge = ext_bridge;
		ext_bridge_info->display = display;
		ext_bridge_info->bridge = ext_bridge;
		prev_bridge = ext_bridge;

		/* ext bridge will init its own connector during attach,
		 * we need to extract it out of the connector list
@@ -5224,14 +5346,15 @@ int dsi_display_drm_ext_bridge_init(struct dsi_display *display,
		}
		spin_unlock_irq(&drm->mode_config.connector_list_lock);

	/* if there is no valid external connector created, we'll use default
	 * setting from panel defined in DT file.
		/* if there is no valid external connector created, or in split
		 * mode, default setting is used from panel defined in DT file.
		 */
		if (!display->ext_conn ||
		    !display->ext_conn->funcs ||
	    !display->ext_conn->helper_private) {
		    !display->ext_conn->helper_private ||
		    display->ext_bridge_cnt > 1) {
			display->ext_conn = NULL;
		return 0;
			continue;
		}

		/* otherwise, hook up the functions to use external connector */
@@ -5239,13 +5362,16 @@ int dsi_display_drm_ext_bridge_init(struct dsi_display *display,
			sde_conn->ops.detect = dsi_display_drm_ext_detect;

		if (display->ext_conn->helper_private->get_modes)
		sde_conn->ops.get_modes = dsi_display_drm_ext_get_modes;
			sde_conn->ops.get_modes =
				dsi_display_drm_ext_get_modes;

		if (display->ext_conn->helper_private->mode_valid)
		sde_conn->ops.mode_valid = dsi_display_drm_ext_mode_valid;
			sde_conn->ops.mode_valid =
				dsi_display_drm_ext_mode_valid;

		if (display->ext_conn->helper_private->atomic_check)
		sde_conn->ops.atomic_check = dsi_display_drm_ext_atomic_check;
			sde_conn->ops.atomic_check =
				dsi_display_drm_ext_atomic_check;

		sde_conn->ops.get_info =
				dsi_display_ext_get_info;
@@ -5254,6 +5380,8 @@ int dsi_display_drm_ext_bridge_init(struct dsi_display *display,

		/* add support to attach/detach */
		display->host.ops = &dsi_host_ext_ops;
	}

	return 0;
error:
	return rc;
+22 −4
Original line number Diff line number Diff line
@@ -123,6 +123,22 @@ struct dsi_display_clk_info {
	struct dsi_clk_link_set shadow_clks;
};

/**
 * struct dsi_display_ext_bridge - dsi display external bridge information
 * @display:           Pointer of DSI display.
 * @node_of:           Bridge node created from bridge driver.
 * @bridge:            Bridge created from bridge driver
 * @orig_funcs:        Bridge function from bridge driver (split mode only)
 * @bridge_funcs:      Overridden function from bridge driver (split mode only)
 */
struct dsi_display_ext_bridge {
	void *display;
	struct device_node *node_of;
	struct drm_bridge *bridge;
	const struct drm_bridge_funcs *orig_funcs;
	struct drm_bridge_funcs bridge_funcs;
};

/**
 * struct dsi_display - dsi display information
 * @pdev:             Pointer to platform device.
@@ -143,7 +159,8 @@ struct dsi_display_clk_info {
 * @ctrl:             Controller information for DSI display.
 * @panel:            Handle to DSI panel.
 * @panel_of:         pHandle to DSI panel.
 * @ext_bridge_of:    pHandle to external DSI bridge.
 * @ext_bridge:       External bridge information for DSI display.
 * @ext_bridge_cnt:   Number of external bridges
 * @modes:            Array of probed DSI modes
 * @type:             DSI display type.
 * @clk_master_idx:   The master controller for controlling clocks. This is an
@@ -163,7 +180,6 @@ struct dsi_display_clk_info {
 * @phy_idle_power_off:   PHY power state.
 * @host:             DRM MIPI DSI Host.
 * @bridge:           Pointer to DRM bridge object.
 * @ext_bridge:       Pointer to external bridge object attached to DSI bridge.
 * @cmd_engine_refcount:  Reference count enforcing single instance of cmd eng
 * @clk_mngr:         DSI clock manager.
 * @dsi_clk_handle:   DSI clock handle.
@@ -198,7 +214,10 @@ struct dsi_display {
	struct device_node *disp_node;
	struct device_node *panel_of;
	struct device_node *parser_node;
	struct device_node *ext_bridge_of;

	/* external bridge */
	struct dsi_display_ext_bridge ext_bridge[MAX_DSI_CTRLS_PER_DISPLAY];
	u32 ext_bridge_cnt;

	struct dsi_display_mode *modes;

@@ -228,7 +247,6 @@ struct dsi_display {

	struct mipi_dsi_host host;
	struct dsi_bridge    *bridge;
	struct drm_bridge    *ext_bridge;
	u32 cmd_engine_refcount;

	struct sde_power_handle *phandle;