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

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

drm/msm/sde: add seamless dsi panel operating mode transition



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 any display by avoiding crtc enable/disable during modeset.
It creates video and command mode physical encoders respectively
to support dynamic panel operating mode switch.

Change-Id: I825702de04b728976dd081e0169bb8e0af520b7b
Signed-off-by: default avatarLei Chen <chenlei@codeaurora.org>
parent 647ecf34
Loading
Loading
Loading
Loading
+3 −2
Original line number Diff line number Diff line
/*
 * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved.
 * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
 * Copyright (C) 2014 Red Hat
 * Author: Rob Clark <robdclark@gmail.com>
 *
@@ -87,7 +87,8 @@ static inline bool _msm_seamless_for_crtc(struct drm_atomic_state *state,
	int conn_cnt = 0;

	if (msm_is_mode_seamless(&crtc_state->mode) ||
		msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode))
		msm_is_mode_seamless_vrr(&crtc_state->adjusted_mode) ||
		msm_is_mode_seamless_poms(&crtc_state->adjusted_mode))
		return true;

	if (msm_is_mode_seamless_dms(&crtc_state->adjusted_mode) && !enable)
+13 −1
Original line number Diff line number Diff line
@@ -248,6 +248,18 @@ enum msm_display_caps {
	MSM_DISPLAY_CAP_MST_MODE	= BIT(5),
};

/**
 * enum panel_mode - panel operation mode
 * @MSM_DISPLAY_VIDEO_MODE: video mode panel
 * @MSM_DISPLAY_CMD_MODE:   Command mode panel
 * @MODE_MAX:
 */
enum panel_op_mode {
	MSM_DISPLAY_VIDEO_MODE = 0,
	MSM_DISPLAY_CMD_MODE,
	MSM_DISPLAY_MODE_MAX,
};

/**
 * enum msm_event_wait - type of HW events to wait for
 * @MSM_ENC_COMMIT_DONE - wait for the driver to flush the registers to HW
@@ -484,7 +496,7 @@ struct msm_mode_info {
struct msm_display_info {
	int intf_type;
	uint32_t capabilities;

	enum panel_op_mode curr_panel_mode;
	uint32_t num_of_h_tiles;
	uint32_t h_tile_instance[MAX_H_TILES_PER_DISPLAY];

+9 −0
Original line number Diff line number Diff line
@@ -38,6 +38,8 @@
#define MSM_MODE_FLAG_SEAMLESS_DMS			(1<<2)
/* Request to switch the fps */
#define MSM_MODE_FLAG_SEAMLESS_VRR			(1<<3)
/* Request to switch the panel mode */
#define MSM_MODE_FLAG_SEAMLESS_POMS			(1<<4)

/* As there are different display controller blocks depending on the
 * snapdragon version, the kms support is split out and the appropriate
@@ -212,6 +214,13 @@ static inline bool msm_is_mode_seamless_vrr(const struct drm_display_mode *mode)
		: false;
}

static inline bool msm_is_mode_seamless_poms(
		const struct drm_display_mode *mode)
{
	return mode ? (mode->private_flags & MSM_MODE_FLAG_SEAMLESS_POMS)
		: false;
}

static inline bool msm_needs_vblank_pre_modeset(
		const struct drm_display_mode *mode)
{
+12 −10
Original line number Diff line number Diff line
@@ -1824,8 +1824,8 @@ int sde_crtc_get_secure_transition_ops(struct drm_crtc *crtc,
	 */
	drm_for_each_encoder_mask(encoder, crtc->dev,
			crtc->state->encoder_mask) {
		post_commit |= sde_encoder_check_mode(encoder,
						MSM_DISPLAY_CAP_VID_MODE);
		post_commit |= sde_encoder_check_curr_mode(encoder,
						MSM_DISPLAY_VIDEO_MODE);
	}

	SDE_DEBUG("crtc%d: secure_level %d old_valid_fb %d post_commit %d\n",
@@ -3164,8 +3164,8 @@ static void sde_crtc_atomic_begin(struct drm_crtc *crtc,
	_sde_crtc_dest_scaler_setup(crtc);

	/* cancel the idle notify delayed work */
	if (sde_encoder_check_mode(sde_crtc->mixers[0].encoder,
					MSM_DISPLAY_CAP_VID_MODE) &&
	if (sde_encoder_check_curr_mode(sde_crtc->mixers[0].encoder,
					MSM_DISPLAY_VIDEO_MODE) &&
		kthread_cancel_delayed_work_sync(&sde_crtc->idle_notify_work))
		SDE_DEBUG("idle notify work cancelled\n");

@@ -3281,8 +3281,9 @@ static void sde_crtc_atomic_flush(struct drm_crtc *crtc,
	_sde_crtc_wait_for_fences(crtc);

	/* schedule the idle notify delayed work */
	if (idle_time && sde_encoder_check_mode(sde_crtc->mixers[0].encoder,
						MSM_DISPLAY_CAP_VID_MODE)) {
	if (idle_time && sde_encoder_check_curr_mode(
						sde_crtc->mixers[0].encoder,
						MSM_DISPLAY_VIDEO_MODE)) {
		kthread_queue_delayed_work(&event_thread->worker,
					&sde_crtc->idle_notify_work,
					msecs_to_jiffies(idle_time));
@@ -4373,8 +4374,8 @@ static int _sde_crtc_check_secure_state_smmu_translation(struct drm_crtc *crtc,

	drm_for_each_encoder_mask(encoder, crtc->dev,
			crtc->state->encoder_mask) {
		is_video_mode |= sde_encoder_check_mode(encoder,
						MSM_DISPLAY_CAP_VID_MODE);
		is_video_mode |= sde_encoder_check_curr_mode(encoder,
			MSM_DISPLAY_VIDEO_MODE);
	}

	/*
@@ -5059,8 +5060,9 @@ static int _sde_crtc_get_output_fence(struct drm_crtc *crtc,
	cstate = to_sde_crtc_state(state);

	drm_for_each_encoder_mask(encoder, crtc->dev, state->encoder_mask) {
		is_vid |= sde_encoder_check_mode(encoder,
						MSM_DISPLAY_CAP_VID_MODE);
		if (sde_encoder_check_curr_mode(encoder,
						MSM_DISPLAY_VIDEO_MODE))
			is_vid = true;
		if (is_vid)
			break;
	}
+95 −34
Original line number Diff line number Diff line
@@ -170,6 +170,8 @@ enum sde_enc_rc_states {
 * @te_source:		vsync source pin information
 * @num_phys_encs:	Actual number of physical encoders contained.
 * @phys_encs:		Container of physical encoders managed.
 * @phys_vid_encs:		Video physical encoders for panel mode switch.
 * @phys_cmd_encs:		Command physical encoders for panel mode switch.
 * @cur_master:		Pointer to the current master in this mode. Optimization
 *			Only valid after enable. Cleared as disable.
 * @hw_pp		Handle to the pingpong blocks used for the display. No.
@@ -235,6 +237,8 @@ struct sde_encoder_virt {

	unsigned int num_phys_encs;
	struct sde_encoder_phys *phys_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL];
	struct sde_encoder_phys *phys_vid_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL];
	struct sde_encoder_phys *phys_cmd_encs[MAX_PHYS_ENCODERS_PER_VIRTUAL];
	struct sde_encoder_phys *cur_master;
	struct sde_hw_pingpong *hw_pp[MAX_CHANNELS_PER_ENC];
	struct sde_hw_dsc *hw_dsc[MAX_CHANNELS_PER_ENC];
@@ -767,8 +771,16 @@ void sde_encoder_destroy(struct drm_encoder *drm_enc)
	sde_rsc_client_destroy(sde_enc->rsc_client);

	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
		struct sde_encoder_phys *phys;

		phys = sde_enc->phys_vid_encs[i];
		if (phys && phys->ops.destroy) {
			phys->ops.destroy(phys);
			--sde_enc->num_phys_encs;
			sde_enc->phys_encs[i] = NULL;
		}

		phys = sde_enc->phys_cmd_encs[i];
		if (phys && phys->ops.destroy) {
			phys->ops.destroy(phys);
			--sde_enc->num_phys_encs;
@@ -1789,7 +1801,7 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc,
		return;
	}

	if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) {
	if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_CMD_MODE)) {
		if (is_dummy)
			vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_0 -
					sde_enc->te_source;
@@ -1999,9 +2011,9 @@ static int _sde_encoder_update_rsc_client(
	if (sde_encoder_in_clone_mode(drm_enc) || !disp_info->is_primary ||
			  (disp_info->is_primary && qsync_mode))
		rsc_state = enable ? SDE_RSC_CLK_STATE : SDE_RSC_IDLE_STATE;
	else if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)
	else if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE))
		rsc_state = enable ? SDE_RSC_CMD_STATE : SDE_RSC_IDLE_STATE;
	else if (disp_info->capabilities & MSM_DISPLAY_CAP_VID_MODE)
	else if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_VIDEO_MODE))
		rsc_state = enable ? SDE_RSC_VID_STATE : SDE_RSC_IDLE_STATE;

	SDE_EVT32(rsc_state, qsync_mode);
@@ -2139,14 +2151,15 @@ static int _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
	struct sde_kms *sde_kms;
	struct sde_encoder_virt *sde_enc;
	int rc;
	bool is_cmd_mode, is_primary;
	bool is_cmd_mode = false, is_primary;

	sde_enc = to_sde_encoder_virt(drm_enc);
	priv = drm_enc->dev->dev_private;
	sde_kms = to_sde_kms(priv->kms);

	is_cmd_mode = sde_enc->disp_info.capabilities &
			MSM_DISPLAY_CAP_CMD_MODE;
	if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE))
		is_cmd_mode = true;

	is_primary = sde_enc->disp_info.is_primary;

	SDE_DEBUG_ENC(sde_enc, "enable:%d\n", enable);
@@ -2722,9 +2735,8 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
	}
	sde_enc = to_sde_encoder_virt(drm_enc);
	priv = drm_enc->dev->dev_private;
	is_vid_mode = sde_enc->disp_info.capabilities &
						MSM_DISPLAY_CAP_VID_MODE;

	if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_VIDEO_MODE))
		is_vid_mode = true;
	/*
	 * when idle_pc is not supported, process only KICKOFF, STOP and MODESET
	 * events and return early for other events (ie wb display).
@@ -2783,6 +2795,32 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
	return ret;
}

static void sde_encoder_virt_mode_switch(enum sde_intf_mode intf_mode,
					struct sde_encoder_virt *sde_enc,
					struct drm_display_mode *adj_mode)
{
	int i = 0;

	if (intf_mode == INTF_MODE_CMD) {
		for (i = 0; i < sde_enc->num_phys_encs; i++)
			sde_enc->phys_encs[i] = sde_enc->phys_vid_encs[i];
		sde_enc->disp_info.curr_panel_mode = MSM_DISPLAY_VIDEO_MODE;
		SDE_DEBUG_ENC(sde_enc, "switch to video physical encoder\n");
		SDE_EVT32(DRMID(&sde_enc->base), intf_mode,
			msm_is_mode_seamless_poms(adj_mode),
			SDE_EVTLOG_FUNC_CASE1);
	}
	if (intf_mode == INTF_MODE_VIDEO) {
		for (i = 0; i < sde_enc->num_phys_encs; i++)
			sde_enc->phys_encs[i] = sde_enc->phys_cmd_encs[i];
		sde_enc->disp_info.curr_panel_mode = MSM_DISPLAY_CMD_MODE;
		SDE_EVT32(DRMID(&sde_enc->base), intf_mode,
			msm_is_mode_seamless_poms(adj_mode),
			SDE_EVTLOG_FUNC_CASE2);
		SDE_DEBUG_ENC(sde_enc, "switch to command physical encoder\n");
	}
}

static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
				      struct drm_display_mode *mode,
				      struct drm_display_mode *adj_mode)
@@ -2796,6 +2834,8 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
	struct sde_connector *sde_conn = NULL;
	struct sde_rm_hw_iter dsc_iter, pp_iter;
	struct sde_rm_hw_request request_hw;
	enum sde_intf_mode intf_mode;

	int i = 0, ret;

	if (!drm_enc) {
@@ -2852,6 +2892,11 @@ static void sde_encoder_virt_mode_set(struct drm_encoder *drm_enc,
			return;
		}
	}
	intf_mode = sde_encoder_get_intf_mode(drm_enc);

	/* Switch pysical encoder */
	if (msm_is_mode_seamless_poms(adj_mode))
		sde_encoder_virt_mode_switch(intf_mode, sde_enc, adj_mode);

	/* release resources before seamless mode change */
	if (msm_is_mode_seamless_dms(adj_mode)) {
@@ -3265,8 +3310,8 @@ static void sde_encoder_virt_enable(struct drm_encoder *drm_enc)
				phys->ops.enable(phys);
		}

		if (sde_enc->misr_enable && (sde_enc->disp_info.capabilities &
		     MSM_DISPLAY_CAP_VID_MODE) && phys->ops.setup_misr)
		if (sde_enc->misr_enable  && phys->ops.setup_misr &&
		(sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_VIDEO_MODE)))
			phys->ops.setup_misr(phys, true,
						sde_enc->misr_frame_count);
	}
@@ -3622,7 +3667,8 @@ static void sde_encoder_frame_done_callback(
{
	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
	unsigned int i;
	bool trigger = true, is_cmd_mode;
	bool trigger = true;
	bool is_cmd_mode = false;
	enum sde_rm_topology_name topology = SDE_RM_TOPOLOGY_NONE;

	if (!drm_enc || !sde_enc->cur_master) {
@@ -3633,8 +3679,8 @@ static void sde_encoder_frame_done_callback(

	sde_enc->crtc_frame_event_cb_data.connector =
				sde_enc->cur_master->connector;
	is_cmd_mode = sde_enc->disp_info.capabilities &
					MSM_DISPLAY_CAP_CMD_MODE;
	if (sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE))
		is_cmd_mode = true;

	if (event & (SDE_ENCODER_FRAME_EVENT_DONE
			| SDE_ENCODER_FRAME_EVENT_ERROR
@@ -3953,8 +3999,8 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
		return;
	}

	is_vid_mode = sde_enc->disp_info.capabilities &
					MSM_DISPLAY_CAP_VID_MODE;
	if (sde_encoder_check_curr_mode(&sde_enc->base, MSM_DISPLAY_VIDEO_MODE))
		is_vid_mode = true;

	/* don't perform flush/start operations for slave encoders */
	for (i = 0; i < sde_enc->num_phys_encs; i++) {
@@ -4161,7 +4207,7 @@ static void _sde_encoder_update_master(struct drm_encoder *drm_enc,
	}
}

bool sde_encoder_check_mode(struct drm_encoder *drm_enc, u32 mode)
bool sde_encoder_check_curr_mode(struct drm_encoder *drm_enc, u32 mode)
{
	struct sde_encoder_virt *sde_enc;
	struct msm_display_info *disp_info;
@@ -4174,7 +4220,7 @@ bool sde_encoder_check_mode(struct drm_encoder *drm_enc, u32 mode)
	sde_enc = to_sde_encoder_virt(drm_enc);
	disp_info = &sde_enc->disp_info;

	return (disp_info->capabilities & mode);
	return (disp_info->curr_panel_mode == mode);
}

void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc)
@@ -4208,7 +4254,8 @@ void sde_encoder_trigger_kickoff_pending(struct drm_encoder *drm_enc)

			/* update only for command mode primary ctl */
			if ((phys == sde_enc->cur_master) &&
			   (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)
				(sde_encoder_check_curr_mode(drm_enc,
					MSM_DISPLAY_CMD_MODE))
				&& ctl->ops.trigger_pending)
				ctl->ops.trigger_pending(ctl);
		}
@@ -4697,7 +4744,7 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,


	if (sde_enc->cur_master && sde_enc->cur_master->connector &&
	    disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE)
	    sde_encoder_check_curr_mode(drm_enc, MSM_DISPLAY_CMD_MODE))
		sde_enc->frame_trigger_mode = sde_connector_get_property(
			sde_enc->cur_master->connector->state,
			CONNECTOR_PROP_CMD_FRAME_TRIGGER_MODE);
@@ -5225,11 +5272,12 @@ static void sde_encoder_early_unregister(struct drm_encoder *encoder)
}

static int sde_encoder_virt_add_phys_encs(
		u32 display_caps,
		struct msm_display_info *disp_info,
		struct sde_encoder_virt *sde_enc,
		struct sde_enc_phys_init_params *params)
{
	struct sde_encoder_phys *enc = NULL;
	u32 display_caps = disp_info->capabilities;

	SDE_DEBUG_ENC(sde_enc, "\n");

@@ -5253,8 +5301,7 @@ static int sde_encoder_virt_add_phys_encs(
			return !enc ? -EINVAL : PTR_ERR(enc);
		}

		sde_enc->phys_encs[sde_enc->num_phys_encs] = enc;
		++sde_enc->num_phys_encs;
		sde_enc->phys_vid_encs[sde_enc->num_phys_encs] = enc;
	}

	if (display_caps & MSM_DISPLAY_CAP_CMD_MODE) {
@@ -5265,10 +5312,17 @@ static int sde_encoder_virt_add_phys_encs(
				PTR_ERR(enc));
			return !enc ? -EINVAL : PTR_ERR(enc);
		}
		sde_enc->phys_cmd_encs[sde_enc->num_phys_encs] = enc;
	}

	if (disp_info->curr_panel_mode == MSM_DISPLAY_VIDEO_MODE)
		sde_enc->phys_encs[sde_enc->num_phys_encs] =
			sde_enc->phys_vid_encs[sde_enc->num_phys_encs];
	else
		sde_enc->phys_encs[sde_enc->num_phys_encs] =
			sde_enc->phys_cmd_encs[sde_enc->num_phys_encs];

		sde_enc->phys_encs[sde_enc->num_phys_encs] = enc;
	++sde_enc->num_phys_encs;
	}

	return 0;
}
@@ -5418,7 +5472,7 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,
						&phys_params);
			else
				ret = sde_encoder_virt_add_phys_encs(
						disp_info->capabilities,
						disp_info,
						sde_enc,
						&phys_params);
			if (ret)
@@ -5428,12 +5482,19 @@ static int sde_encoder_setup_display(struct sde_encoder_virt *sde_enc,
	}

	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];
		struct sde_encoder_phys *vid_phys = sde_enc->phys_vid_encs[i];
		struct sde_encoder_phys *cmd_phys = sde_enc->phys_cmd_encs[i];

		if (phys) {
			atomic_set(&phys->vsync_cnt, 0);
			atomic_set(&phys->underrun_cnt, 0);
		if (vid_phys) {
			atomic_set(&vid_phys->vsync_cnt, 0);
			atomic_set(&vid_phys->underrun_cnt, 0);
		}

		if (cmd_phys) {
			atomic_set(&cmd_phys->vsync_cnt, 0);
			atomic_set(&cmd_phys->underrun_cnt, 0);
		}

	}
	mutex_unlock(&sde_enc->enc_lock);

@@ -5508,7 +5569,7 @@ struct drm_encoder *sde_encoder_init(
		sde_enc->rsc_client = NULL;
	}

	if (disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) {
	if (disp_info->curr_panel_mode == MSM_DISPLAY_CMD_MODE) {
		ret = _sde_encoder_input_handler(sde_enc);
		if (ret)
			SDE_ERROR(
Loading