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

Commit 21edecd3 authored by Lei Chen's avatar Lei Chen Committed by Gerrit - the friendly Code Review server
Browse files

disp: msm: Add support for seamless panel operating mode switch



DSI display may support video mode and command mode both and it may
support transition between these two modes.
This change adds seamless transition between these two modes for DSI
display by avoiding crtc enable/disable and panel power on/off
during modeset.

Change-Id: Id7ddaef7d1f0f7cc7d52283755bad53a246adec6
Signed-off-by: default avatarLei Chen <chenlei@codeaurora.org>
parent e6933ff2
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -74,6 +74,8 @@ enum dsi_op_mode {
 * @DSI_MODE_FLAG_DMS: Seamless transition is dynamic mode switch
 * @DSI_MODE_FLAG_VRR: Seamless transition is DynamicFPS.
 *                     New timing values are sent from DAL.
 * @DSI_MODE_FLAG_POMS:
 *         Seamless transition is dynamic panel operating mode switch
 */
enum dsi_mode_flags {
	DSI_MODE_FLAG_SEAMLESS			= BIT(0),
@@ -81,6 +83,7 @@ enum dsi_mode_flags {
	DSI_MODE_FLAG_VBLANK_PRE_MODESET	= BIT(2),
	DSI_MODE_FLAG_DMS			= BIT(3),
	DSI_MODE_FLAG_VRR			= BIT(4),
	DSI_MODE_FLAG_POMS			= BIT(5),
};

/**
@@ -555,12 +558,14 @@ struct dsi_display_mode_priv_info {
 * @timing:         Timing parameters for the panel.
 * @pixel_clk_khz:  Pixel clock in Khz.
 * @dsi_mode_flags: Flags to signal other drm components via private flags
 * @panel_mode:      Panel mode
 * @priv_info:      Mode private info
 */
struct dsi_display_mode {
	struct dsi_mode_info timing;
	u32 pixel_clk_khz;
	u32 dsi_mode_flags;
	enum dsi_op_mode panel_mode;
	struct dsi_display_mode_priv_info *priv_info;
};

+68 −33
Original line number Diff line number Diff line
@@ -3971,6 +3971,10 @@ static int dsi_display_set_mode_sub(struct dsi_display *display,
		return -EINVAL;
	}

	if (mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) {
		display->config.panel_mode = mode->panel_mode;
		display->panel->panel_mode = mode->panel_mode;
	}
	rc = dsi_panel_get_host_cfg_for_mode(display->panel,
					     mode,
					     &display->config);
@@ -5040,6 +5044,7 @@ static int dsi_display_ext_get_info(struct drm_connector *connector,

	info->capabilities |= (MSM_DISPLAY_CAP_VID_MODE |
			MSM_DISPLAY_CAP_EDID | MSM_DISPLAY_CAP_HOT_PLUG);
	info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE;

	mutex_unlock(&display->display_lock);
	return 0;
@@ -5403,10 +5408,16 @@ int dsi_display_get_info(struct drm_connector *connector,

	switch (display->panel->panel_mode) {
	case DSI_OP_VIDEO_MODE:
		info->curr_panel_mode = MSM_DISPLAY_VIDEO_MODE;
		info->capabilities |= MSM_DISPLAY_CAP_VID_MODE;
		if (display->panel->panel_mode_switch_enabled)
			info->capabilities |= MSM_DISPLAY_CAP_CMD_MODE;
		break;
	case DSI_OP_CMD_MODE:
		info->curr_panel_mode = MSM_DISPLAY_CMD_MODE;
		info->capabilities |= MSM_DISPLAY_CAP_CMD_MODE;
		if (display->panel->panel_mode_switch_enabled)
			info->capabilities |= MSM_DISPLAY_CAP_VID_MODE;
		info->is_te_using_watchdog_timer =
			display->panel->te_using_watchdog_timer |
			display->sw_te_using_wd;
@@ -5704,7 +5715,8 @@ int dsi_display_find_mode(struct dsi_display *display,

		if (cmp->timing.v_active == m->timing.v_active &&
			cmp->timing.h_active == m->timing.h_active &&
			cmp->timing.refresh_rate == m->timing.refresh_rate) {
			cmp->timing.refresh_rate == m->timing.refresh_rate &&
			cmp->panel_mode == m->panel_mode) {
			*out_mode = m;
			rc = 0;
			break;
@@ -6314,7 +6326,8 @@ int dsi_display_prepare(struct dsi_display *display)
		goto error;
	}

	if (!display->is_cont_splash_enabled) {
	if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) &&
		(!display->is_cont_splash_enabled)) {
		/*
		 * For continuous splash usecase we skip panel
		 * pre prepare since the regulator vote is already
@@ -6403,6 +6416,7 @@ int dsi_display_prepare(struct dsi_display *display)
			goto error_ctrl_link_off;
		}

		if (!(mode->dsi_mode_flags & DSI_MODE_FLAG_POMS)) {
			rc = dsi_panel_prepare(display->panel);
			if (rc) {
				pr_err("[%s] panel prepare failed, rc=%d\n",
@@ -6410,6 +6424,7 @@ int dsi_display_prepare(struct dsi_display *display)
				goto error_ctrl_link_off;
			}
		}
	}
	goto error;

error_ctrl_link_off:
@@ -6728,7 +6743,8 @@ int dsi_display_enable(struct dsi_display *display)
				   display->name, rc);
			goto error;
		}
	} else {
	} else if (!(display->panel->cur_mode->dsi_mode_flags &
			DSI_MODE_FLAG_POMS)){
		rc = dsi_panel_enable(display->panel);
		if (rc) {
			pr_err("[%s] failed to enable DSI panel, rc=%d\n",
@@ -6757,6 +6773,7 @@ int dsi_display_enable(struct dsi_display *display)
	}

	if (display->config.panel_mode == DSI_OP_VIDEO_MODE) {
		pr_debug("%s:enable video timing eng\n", __func__);
		rc = dsi_display_vid_engine_enable(display);
		if (rc) {
			pr_err("[%s]failed to enable DSI video engine, rc=%d\n",
@@ -6764,6 +6781,7 @@ int dsi_display_enable(struct dsi_display *display)
			goto error_disable_panel;
		}
	} else if (display->config.panel_mode == DSI_OP_CMD_MODE) {
		pr_debug("%s:enable command timing eng\n", __func__);
		rc = dsi_display_cmd_engine_enable(display);
		if (rc) {
			pr_err("[%s]failed to enable DSI cmd engine, rc=%d\n",
@@ -6797,10 +6815,18 @@ int dsi_display_post_enable(struct dsi_display *display)

	mutex_lock(&display->display_lock);

	if (display->panel->cur_mode->dsi_mode_flags & DSI_MODE_FLAG_POMS) {
		if (display->config.panel_mode == DSI_OP_CMD_MODE)
			dsi_panel_mode_switch_to_cmd(display->panel);

		if (display->config.panel_mode == DSI_OP_VIDEO_MODE)
			dsi_panel_mode_switch_to_vid(display->panel);
	} else {
		rc = dsi_panel_post_enable(display->panel);
		if (rc)
			pr_err("[%s] panel post-enable failed, rc=%d\n",
				display->name, rc);
	}

	/* remove the clk vote for CMD mode panels */
	if (display->config.panel_mode == DSI_OP_CMD_MODE)
@@ -6826,12 +6852,18 @@ int dsi_display_pre_disable(struct dsi_display *display)
	if (display->config.panel_mode == DSI_OP_CMD_MODE)
		dsi_display_clk_ctrl(display->dsi_clk_handle,
			DSI_ALL_CLKS, DSI_CLK_ON);
	if (display->poms_pending) {
		if (display->config.panel_mode == DSI_OP_CMD_MODE)
			dsi_panel_pre_mode_switch_to_video(display->panel);

		if (display->config.panel_mode == DSI_OP_VIDEO_MODE)
			dsi_panel_pre_mode_switch_to_cmd(display->panel);
	} else {
		rc = dsi_panel_pre_disable(display->panel);
		if (rc)
			pr_err("[%s] panel pre-disable failed, rc=%d\n",
				display->name, rc);

	}
	mutex_unlock(&display->display_lock);
	return rc;
}
@@ -6868,11 +6900,12 @@ int dsi_display_disable(struct dsi_display *display)
		rc = -EINVAL;
	}

	if (!display->poms_pending) {
		rc = dsi_panel_disable(display->panel);
		if (rc)
			pr_err("[%s] failed to disable DSI panel, rc=%d\n",
				display->name, rc);

	}
	mutex_unlock(&display->display_lock);
	SDE_EVT32(SDE_EVTLOG_FUNC_EXIT);
	return rc;
@@ -6911,12 +6944,12 @@ int dsi_display_unprepare(struct dsi_display *display)
	if (rc)
		pr_err("[%s] display wake up failed, rc=%d\n",
		       display->name, rc);

	if (!display->poms_pending) {
		rc = dsi_panel_unprepare(display->panel);
		if (rc)
			pr_err("[%s] panel unprepare failed, rc=%d\n",
			       display->name, rc);

	}
	rc = dsi_display_ctrl_host_disable(display);
	if (rc)
		pr_err("[%s] failed to disable DSI host, rc=%d\n",
@@ -6949,10 +6982,12 @@ int dsi_display_unprepare(struct dsi_display *display)
	/* destrory dsi isr set up */
	dsi_display_ctrl_isr_configure(display, false);

	if (!display->poms_pending) {
		rc = dsi_panel_post_unprepare(display->panel);
		if (rc)
			pr_err("[%s] panel post-unprepare failed, rc=%d\n",
			       display->name, rc);
	}

	mutex_unlock(&display->display_lock);

+2 −0
Original line number Diff line number Diff line
@@ -166,6 +166,7 @@ struct dsi_display_ext_bridge {
 * @cmdline_topology: Display topology shared from kernel command line.
 * @cmdline_timing:   Display timing shared from kernel command line.
 * @is_tpg_enabled:   TPG state.
 * @poms_pending;      Flag indicating the pending panel operating mode switch.
 * @ulps_enabled:     ulps state.
 * @clamp_enabled:    clamp state.
 * @phy_idle_power_off:   PHY power state.
@@ -226,6 +227,7 @@ struct dsi_display {
	int cmdline_topology;
	int cmdline_timing;
	bool is_tpg_enabled;
	bool poms_pending;
	bool ulps_enabled;
	bool clamp_enabled;
	bool phy_idle_power_off;
+34 −2
Original line number Diff line number Diff line
@@ -71,11 +71,18 @@ static void convert_to_dsi_mode(const struct drm_display_mode *drm_mode,
		dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_DMS;
	if (msm_is_mode_seamless_vrr(drm_mode))
		dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_VRR;
	if (msm_is_mode_seamless_poms(drm_mode))
		dsi_mode->dsi_mode_flags |= DSI_MODE_FLAG_POMS;

	dsi_mode->timing.h_sync_polarity =
			!!(drm_mode->flags & DRM_MODE_FLAG_PHSYNC);
	dsi_mode->timing.v_sync_polarity =
			!!(drm_mode->flags & DRM_MODE_FLAG_PVSYNC);

	if (drm_mode->flags & DRM_MODE_FLAG_VID_MODE_PANEL)
		dsi_mode->panel_mode = DSI_OP_VIDEO_MODE;
	if (drm_mode->flags & DRM_MODE_FLAG_CMD_MODE_PANEL)
		dsi_mode->panel_mode = DSI_OP_CMD_MODE;
}

void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
@@ -113,12 +120,19 @@ void dsi_convert_to_drm_mode(const struct dsi_display_mode *dsi_mode,
		drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_DMS;
	if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_VRR)
		drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_VRR;
	if (dsi_mode->dsi_mode_flags & DSI_MODE_FLAG_POMS)
		drm_mode->private_flags |= MSM_MODE_FLAG_SEAMLESS_POMS;

	if (dsi_mode->timing.h_sync_polarity)
		drm_mode->flags |= DRM_MODE_FLAG_PHSYNC;
	if (dsi_mode->timing.v_sync_polarity)
		drm_mode->flags |= DRM_MODE_FLAG_PVSYNC;

	if (dsi_mode->panel_mode == DSI_OP_VIDEO_MODE)
		drm_mode->flags |= DRM_MODE_FLAG_VID_MODE_PANEL;
	if (dsi_mode->panel_mode == DSI_OP_CMD_MODE)
		drm_mode->flags |= DRM_MODE_FLAG_CMD_MODE_PANEL;

	drm_mode_set_name(drm_mode);
}

@@ -233,8 +247,18 @@ static void dsi_bridge_disable(struct drm_bridge *bridge)
	}
	display = c_bridge->display;

	if (display && display->drm_conn)
	if (display && display->drm_conn) {
		if (bridge->encoder->crtc->state->adjusted_mode.private_flags &
			MSM_MODE_FLAG_SEAMLESS_POMS) {
			display->poms_pending = true;
			/* Disable ESD thread, during panel mode switch */
			sde_connector_schedule_status_work(display->drm_conn,
				false);
		} else {
			display->poms_pending = false;
			sde_connector_helper_bridge_disable(display->drm_conn);
		}
	}

	rc = dsi_display_pre_disable(c_bridge->display);
	if (rc) {
@@ -363,9 +387,17 @@ static bool dsi_bridge_mode_fixup(struct drm_bridge *bridge,

		cur_mode = crtc_state->crtc->mode;

		/* No panel mode switch when drm pipeline is changing */
		if ((dsi_mode.panel_mode != cur_dsi_mode.panel_mode) &&
			(!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR)) &&
			(!crtc_state->active_changed ||
			display->is_cont_splash_enabled))
			dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_POMS;

		/* No DMS/VRR when drm pipeline is changing */
		if (!drm_mode_equal(&cur_mode, adjusted_mode) &&
			(!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_VRR)) &&
			(!(dsi_mode.dsi_mode_flags & DSI_MODE_FLAG_POMS)) &&
			(!crtc_state->active_changed ||
			 display->is_cont_splash_enabled))
			dsi_mode.dsi_mode_flags |= DSI_MODE_FLAG_DMS;
+128 −2
Original line number Diff line number Diff line
@@ -1360,6 +1360,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel)
{
	int rc = 0;
	struct dsi_parser_utils *utils = &panel->utils;
	bool panel_mode_switch_enabled;
	enum dsi_op_mode panel_mode;
	const char *mode;

@@ -1378,7 +1379,13 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel)
		goto error;
	}

	if (panel_mode == DSI_OP_VIDEO_MODE) {
	panel_mode_switch_enabled = utils->read_bool(utils->data,
					"qcom,mdss-dsi-panel-mode-switch");

	pr_info("%s: panel operating mode switch feature %s\n", __func__,
		(panel_mode_switch_enabled ? "enabled" : "disabled"));

	if (panel_mode == DSI_OP_VIDEO_MODE || panel_mode_switch_enabled) {
		rc = dsi_panel_parse_video_host_config(&panel->video_config,
						       utils,
						       panel->name);
@@ -1389,7 +1396,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel)
		}
	}

	if (panel_mode == DSI_OP_CMD_MODE) {
	if (panel_mode == DSI_OP_CMD_MODE || panel_mode_switch_enabled) {
		rc = dsi_panel_parse_cmd_host_config(&panel->cmd_config,
						     utils,
						     panel->name);
@@ -1401,6 +1408,7 @@ static int dsi_panel_parse_panel_mode(struct dsi_panel *panel)
	}

	panel->panel_mode = panel_mode;
	panel->panel_mode_switch_enabled = panel_mode_switch_enabled;
error:
	return rc;
}
@@ -2654,6 +2662,33 @@ static int dsi_panel_parse_partial_update_caps(struct dsi_display_mode *mode,
	return rc;
}

static int dsi_panel_parse_panel_mode_caps(struct dsi_display_mode *mode,
				struct dsi_parser_utils *utils)
{
	bool vid_mode_support, cmd_mode_support;

	if (!mode || !mode->priv_info) {
		pr_err("invalid arguments\n");
		return -EINVAL;
	}

	vid_mode_support = utils->read_bool(utils->data,
				"qcom,mdss-dsi-video-mode");

	cmd_mode_support = utils->read_bool(utils->data,
				"qcom,mdss-dsi-cmd-mode");

	if (cmd_mode_support)
		mode->panel_mode = DSI_OP_CMD_MODE;
	else if (vid_mode_support)
		mode->panel_mode = DSI_OP_VIDEO_MODE;
	else
		return -EINVAL;

	return 0;
};


static int dsi_panel_parse_dms_info(struct dsi_panel *panel)
{
	int dms_enabled;
@@ -3340,6 +3375,17 @@ int dsi_panel_get_mode(struct dsi_panel *panel,
		rc = dsi_panel_parse_partial_update_caps(mode, utils);
		if (rc)
			pr_err("failed to partial update caps, rc=%d\n", rc);

		if (panel->panel_mode_switch_enabled) {
			rc = dsi_panel_parse_panel_mode_caps(mode, utils);
			if (rc) {
				pr_err("PMS: failed to parse panel mode\n");
				rc = 0;
				mode->panel_mode = panel->panel_mode;
			}
		} else {
			mode->panel_mode = panel->panel_mode;
		}
	}
	goto done;

@@ -3698,6 +3744,86 @@ int dsi_panel_send_roi_dcs(struct dsi_panel *panel, int ctrl_idx,
	return rc;
}

int dsi_panel_pre_mode_switch_to_video(struct dsi_panel *panel)
{
	int rc = 0;

	if (!panel) {
		pr_err("Invalid params\n");
		return -EINVAL;
	}

	mutex_lock(&panel->panel_lock);

	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_CMD_TO_VID_SWITCH);
	if (rc)
		pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n",
		       panel->name, rc);

	mutex_unlock(&panel->panel_lock);
	return rc;
}

int dsi_panel_pre_mode_switch_to_cmd(struct dsi_panel *panel)
{
	int rc = 0;

	if (!panel) {
		pr_err("Invalid params\n");
		return -EINVAL;
	}

	mutex_lock(&panel->panel_lock);

	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_VID_TO_CMD_SWITCH);
	if (rc)
		pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n",
		       panel->name, rc);

	mutex_unlock(&panel->panel_lock);
	return rc;
}

int dsi_panel_mode_switch_to_cmd(struct dsi_panel *panel)
{
	int rc = 0;

	if (!panel) {
		pr_err("Invalid params\n");
		return -EINVAL;
	}

	mutex_lock(&panel->panel_lock);

	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_POST_VID_TO_CMD_SWITCH);
	if (rc)
		pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n",
		       panel->name, rc);

	mutex_unlock(&panel->panel_lock);
	return rc;
}

int dsi_panel_mode_switch_to_vid(struct dsi_panel *panel)
{
	int rc = 0;

	if (!panel) {
		pr_err("Invalid params\n");
		return -EINVAL;
	}

	mutex_lock(&panel->panel_lock);

	rc = dsi_panel_tx_cmd_set(panel, DSI_CMD_SET_POST_CMD_TO_VID_SWITCH);
	if (rc)
		pr_err("[%s] failed to send DSI_CMD_SET_CMD_TO_VID_SWITCH cmds, rc=%d\n",
		       panel->name, rc);

	mutex_unlock(&panel->panel_lock);
	return rc;
}

int dsi_panel_switch(struct dsi_panel *panel)
{
	int rc = 0;
Loading