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

Commit aab9b520 authored by Dhaval Patel's avatar Dhaval Patel
Browse files

drm/msm/sde: update vsync source to dummy before mode-2 entry



DRM driver turns off the vsync from software side and requests
for mode-2 entry for gdsc power collapse. The display panel is
ON and still generating TE (vsync) signal because this sequence
is triggered during idle power collapse state. In this state, RSC
driver has to turn ON the solver mode to enter in mode-2 and
SDE rsc hardware does not expect any vsync signal. Hardware goes
to bad state if panel TE signal and SW mode-2 entry request received
at the same time. This cause invalid mode-2 entry after idle power
restore + frame kickoff (rising edge for idle). This race condition
between mode-2 entry and vsync can be avoided by selecting watchdog
timer as vsync source without actually enabling watchdog timer. That
provides guarantee of no vsync signal when software request
for mode-2 entry.

Change-Id: I1377cb1ba532f65e4c00becb9636148e403b2672
Signed-off-by: default avatarDhaval Patel <pdhaval@codeaurora.org>
parent 814e5c69
Loading
Loading
Loading
Loading
+74 −35
Original line number Diff line number Diff line
@@ -1101,6 +1101,62 @@ static int _sde_encoder_dsc_setup(struct sde_encoder_virt *sde_enc,
	return ret;
}

static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc,
			struct msm_display_info *disp_info, bool is_dummy)
{
	struct sde_vsync_source_cfg vsync_cfg = { 0 };
	struct msm_drm_private *priv;
	struct sde_kms *sde_kms;
	struct sde_hw_mdp *hw_mdptop;
	struct drm_encoder *drm_enc;
	int i;

	if (!sde_enc || !disp_info) {
		SDE_ERROR("invalid param sde_enc:%d or disp_info:%d\n",
					sde_enc != NULL, disp_info != NULL);
		return;
	} else if (sde_enc->num_phys_encs > ARRAY_SIZE(sde_enc->hw_pp)) {
		SDE_ERROR("invalid num phys enc %d/%d\n",
				sde_enc->num_phys_encs,
				(int) ARRAY_SIZE(sde_enc->hw_pp));
		return;
	}

	drm_enc = &sde_enc->base;
	/* this pointers are checked in virt_enable_helper */
	priv = drm_enc->dev->dev_private;

	sde_kms = to_sde_kms(priv->kms);
	if (!sde_kms) {
		SDE_ERROR("invalid sde_kms\n");
		return;
	}

	hw_mdptop = sde_kms->hw_mdp;
	if (!hw_mdptop) {
		SDE_ERROR("invalid mdptop\n");
		return;
	}

	if (hw_mdptop->ops.setup_vsync_source &&
			disp_info->capabilities & MSM_DISPLAY_CAP_CMD_MODE) {
		for (i = 0; i < sde_enc->num_phys_encs; i++)
			vsync_cfg.ppnumber[i] = sde_enc->hw_pp[i]->idx;

		vsync_cfg.pp_count = sde_enc->num_phys_encs;
		vsync_cfg.frame_rate = sde_enc->disp_info.frame_rate;
		if (is_dummy)
			vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_1;
		else if (disp_info->is_te_using_watchdog_timer)
			vsync_cfg.vsync_source = SDE_VSYNC_SOURCE_WD_TIMER_0;
		else
			vsync_cfg.vsync_source = SDE_VSYNC0_SOURCE_GPIO;
		vsync_cfg.is_dummy = is_dummy;

		hw_mdptop->ops.setup_vsync_source(hw_mdptop, &vsync_cfg);
	}
}

static int sde_encoder_update_rsc_client(
		struct drm_encoder *drm_enc,
		struct sde_encoder_rsc_config *config, bool enable)
@@ -1216,6 +1272,9 @@ static void _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
		rsc_cfg.inline_rotate_prefill =
				sde_crtc_get_inline_prefill(drm_enc->crtc);

		_sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info,
									false);

		/* enable RSC */
		sde_encoder_update_rsc_client(drm_enc, &rsc_cfg, true);

@@ -1224,6 +1283,14 @@ static void _sde_encoder_resource_control_helper(struct drm_encoder *drm_enc,
		/* disable RSC */
		sde_encoder_update_rsc_client(drm_enc, NULL, false);

		/**
		 * this call is for hardware workaround on sdm845 and should
		 * not be removed without considering the design changes for
		 * sde rsc + command mode concurrency. It may lead to pp
		 * timeout due to vsync from panel for command mode panel.
		 */
		_sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info,
									true);
		/* disable all the irq */
		for (i = 0; i < sde_enc->num_phys_encs; i++) {
			struct sde_encoder_phys *phys =
@@ -1550,33 +1617,22 @@ static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
	struct sde_encoder_virt *sde_enc = NULL;
	struct msm_drm_private *priv;
	struct sde_kms *sde_kms;
	struct sde_hw_mdp *hw_mdptop;
	int i = 0;
	struct sde_watchdog_te_status te_cfg = { 0 };

	if (!drm_enc || !drm_enc->dev || !drm_enc->dev->dev_private) {
		SDE_ERROR("invalid parameters\n");
		return;
	}
	priv = drm_enc->dev->dev_private;

	sde_enc = to_sde_encoder_virt(drm_enc);
	if (!sde_enc || !sde_enc->cur_master) {
		SDE_ERROR("invalid sde encoder/master\n");
		return;
	}

	priv = drm_enc->dev->dev_private;
	sde_kms = to_sde_kms(priv->kms);
	hw_mdptop = sde_kms->hw_mdp;

	if (!hw_mdptop) {
		SDE_ERROR("invalid mdptop\n");
	if (!sde_kms) {
		SDE_ERROR("invalid sde_kms\n");
		return;
	}

	sde_kms = to_sde_kms(priv->kms);
	if (!sde_kms) {
		SDE_ERROR("invalid sde_kms\n");
	sde_enc = to_sde_encoder_virt(drm_enc);
	if (!sde_enc || !sde_enc->cur_master) {
		SDE_ERROR("invalid sde encoder/master\n");
		return;
	}

@@ -1586,24 +1642,7 @@ static void _sde_encoder_virt_enable_helper(struct drm_encoder *drm_enc)
				sde_enc->cur_master->hw_mdptop,
				sde_kms->catalog);

	if (sde_enc->num_phys_encs > ARRAY_SIZE(te_cfg.ppnumber) ||
			sde_enc->num_phys_encs > ARRAY_SIZE(sde_enc->hw_pp)) {
		SDE_ERROR("invalid num phys enc %d/%d/%d\n",
				sde_enc->num_phys_encs,
				(int) ARRAY_SIZE(te_cfg.ppnumber),
				(int) ARRAY_SIZE(sde_enc->hw_pp));
		return;
	}

	if (hw_mdptop->ops.setup_vsync_sel) {
		for (i = 0; i < sde_enc->num_phys_encs; i++)
			te_cfg.ppnumber[i] = sde_enc->hw_pp[i]->idx;

		te_cfg.pp_count = sde_enc->num_phys_encs;
		te_cfg.frame_rate = sde_enc->disp_info.frame_rate;
		hw_mdptop->ops.setup_vsync_sel(hw_mdptop, &te_cfg,
				sde_enc->disp_info.is_te_using_watchdog_timer);
	}
	_sde_encoder_update_vsync_source(sde_enc, &sde_enc->disp_info, false);

	memset(&sde_enc->prv_conn_roi, 0, sizeof(sde_enc->prv_conn_roi));
	memset(&sde_enc->cur_conn_roi, 0, sizeof(sde_enc->cur_conn_roi));
+13 −0
Original line number Diff line number Diff line
@@ -82,6 +82,19 @@ enum sde_format_flags {
#define SDE_BLEND_BG_INV_MOD_ALPHA	(1 << 12)
#define SDE_BLEND_BG_TRANSP_EN		(1 << 13)

#define SDE_VSYNC0_SOURCE_GPIO		0
#define SDE_VSYNC1_SOURCE_GPIO		1
#define SDE_VSYNC2_SOURCE_GPIO		2
#define SDE_VSYNC_SOURCE_INTF_0		3
#define SDE_VSYNC_SOURCE_INTF_1		4
#define SDE_VSYNC_SOURCE_INTF_2		5
#define SDE_VSYNC_SOURCE_INTF_3		6
#define SDE_VSYNC_SOURCE_WD_TIMER_4	11
#define SDE_VSYNC_SOURCE_WD_TIMER_3	12
#define SDE_VSYNC_SOURCE_WD_TIMER_2	13
#define SDE_VSYNC_SOURCE_WD_TIMER_1	14
#define SDE_VSYNC_SOURCE_WD_TIMER_0	15

enum sde_hw_blk_type {
	SDE_HW_BLK_TOP = 0,
	SDE_HW_BLK_SSPP,
+67 −19
Original line number Diff line number Diff line
@@ -38,6 +38,18 @@
#define MDP_WD_TIMER_0_CTL                0x380
#define MDP_WD_TIMER_0_CTL2               0x384
#define MDP_WD_TIMER_0_LOAD_VALUE         0x388
#define MDP_WD_TIMER_1_CTL                0x390
#define MDP_WD_TIMER_1_CTL2               0x394
#define MDP_WD_TIMER_1_LOAD_VALUE         0x398
#define MDP_WD_TIMER_2_CTL                0x420
#define MDP_WD_TIMER_2_CTL2               0x424
#define MDP_WD_TIMER_2_LOAD_VALUE         0x428
#define MDP_WD_TIMER_3_CTL                0x430
#define MDP_WD_TIMER_3_CTL2               0x434
#define MDP_WD_TIMER_3_LOAD_VALUE         0x438
#define MDP_WD_TIMER_4_CTL                0x440
#define MDP_WD_TIMER_4_CTL2               0x444
#define MDP_WD_TIMER_4_LOAD_VALUE         0x448

#define MDP_TICK_COUNT                    16
#define XO_CLK_RATE                       19200
@@ -204,38 +216,74 @@ static void sde_hw_get_danger_status(struct sde_hw_mdp *mdp,
	status->wb[WB_3] = 0;
}

static void sde_hw_setup_vsync_sel(struct sde_hw_mdp *mdp,
		struct sde_watchdog_te_status *cfg, bool watchdog_te)
static void sde_hw_setup_vsync_source(struct sde_hw_mdp *mdp,
		struct sde_vsync_source_cfg *cfg)
{
	struct sde_hw_blk_reg_map *c = &mdp->hw;
	u32 reg = 0;
	int i = 0;
	u32 pp_offset[] = {0xC, 0x8, 0x4, 0x13};
	struct sde_hw_blk_reg_map *c;
	u32 reg, wd_load_value, wd_ctl, wd_ctl2, i;
	static const u32 pp_offset[PINGPONG_MAX] = {0xC, 0x8, 0x4, 0x13, 0x18};

	if (!mdp || !cfg || (cfg->pp_count > ARRAY_SIZE(cfg->ppnumber)))
		return;

	c = &mdp->hw;
	reg = SDE_REG_READ(c, MDP_VSYNC_SEL);
	for (i = 0; i < cfg->pp_count; i++) {
		int pp_idx = cfg->ppnumber[i] - PINGPONG_0;
		if (pp_idx >= ARRAY_SIZE(pp_offset))
			continue;

		if (watchdog_te)
			reg |= 0xF << pp_offset[pp_idx];
		else
			reg &= ~(0xF << pp_offset[pp_idx]);
		reg &= ~(0xf << pp_offset[pp_idx]);
		reg |= (cfg->vsync_source & 0xf) << pp_offset[pp_idx];
	}

	SDE_REG_WRITE(c, MDP_VSYNC_SEL, reg);

	if (watchdog_te) {
		SDE_REG_WRITE(c, MDP_WD_TIMER_0_LOAD_VALUE,
	if (cfg->vsync_source >= SDE_VSYNC_SOURCE_WD_TIMER_4 &&
			cfg->vsync_source <= SDE_VSYNC_SOURCE_WD_TIMER_0) {
		switch (cfg->vsync_source) {
		case SDE_VSYNC_SOURCE_WD_TIMER_4:
			wd_load_value = MDP_WD_TIMER_4_LOAD_VALUE;
			wd_ctl = MDP_WD_TIMER_4_CTL;
			wd_ctl2 = MDP_WD_TIMER_4_CTL2;
			break;
		case SDE_VSYNC_SOURCE_WD_TIMER_3:
			wd_load_value = MDP_WD_TIMER_3_LOAD_VALUE;
			wd_ctl = MDP_WD_TIMER_3_CTL;
			wd_ctl2 = MDP_WD_TIMER_3_CTL2;
			break;
		case SDE_VSYNC_SOURCE_WD_TIMER_2:
			wd_load_value = MDP_WD_TIMER_2_LOAD_VALUE;
			wd_ctl = MDP_WD_TIMER_2_CTL;
			wd_ctl2 = MDP_WD_TIMER_2_CTL2;
			break;
		case SDE_VSYNC_SOURCE_WD_TIMER_1:
			wd_load_value = MDP_WD_TIMER_1_LOAD_VALUE;
			wd_ctl = MDP_WD_TIMER_1_CTL;
			wd_ctl2 = MDP_WD_TIMER_1_CTL2;
			break;
		case SDE_VSYNC_SOURCE_WD_TIMER_0:
		default:
			wd_load_value = MDP_WD_TIMER_0_LOAD_VALUE;
			wd_ctl = MDP_WD_TIMER_0_CTL;
			wd_ctl2 = MDP_WD_TIMER_0_CTL2;
			break;
		}

		if (cfg->is_dummy) {
			SDE_REG_WRITE(c, wd_ctl2, 0x0);
		} else {
			SDE_REG_WRITE(c, wd_load_value,
				CALCULATE_WD_LOAD_VALUE(cfg->frame_rate));

		SDE_REG_WRITE(c, MDP_WD_TIMER_0_CTL, BIT(0)); /* clear timer */
		reg = SDE_REG_READ(c, MDP_WD_TIMER_0_CTL2);
			SDE_REG_WRITE(c, wd_ctl, BIT(0)); /* clear timer */
			reg = SDE_REG_READ(c, wd_ctl2);
			reg |= BIT(8);		/* enable heartbeat timer */
			reg |= BIT(0);		/* enable WD timer */
		SDE_REG_WRITE(c, MDP_WD_TIMER_0_CTL2, reg);
			SDE_REG_WRITE(c, wd_ctl2, reg);
		}

		/* make sure that timers are enabled/disabled for vsync state */
		wmb();
	}
}

@@ -308,7 +356,7 @@ static void _setup_mdp_ops(struct sde_hw_mdp_ops *ops,
	ops->setup_cdm_output = sde_hw_setup_cdm_output;
	ops->setup_clk_force_ctrl = sde_hw_setup_clk_force_ctrl;
	ops->get_danger_status = sde_hw_get_danger_status;
	ops->setup_vsync_sel = sde_hw_setup_vsync_sel;
	ops->setup_vsync_source = sde_hw_setup_vsync_source;
	ops->get_safe_status = sde_hw_get_safe_status;
	ops->setup_dce = sde_hw_setup_dce;
	ops->reset_ubwc = sde_hw_reset_ubwc;
+12 −7
Original line number Diff line number Diff line
@@ -78,15 +78,21 @@ struct sde_danger_safe_status {
};

/**
 * struct sde_watchdog_te_status - configure watchdog timer to generate TE
 * struct sde_vsync_source_cfg - configure vsync source and configure the
 *                                    watchdog timers if required.
 * @pp_count: number of ping pongs active
 * @frame_rate: Display frame rate
 * @ppnumber: ping pong index array
 * @vsync_source: vsync source selection
 * @is_dummy: a dummy source of vsync selection. It must not be selected for
 *           any case other than sde rsc idle request.
 */
struct sde_watchdog_te_status {
struct sde_vsync_source_cfg {
	u32 pp_count;
	u32 frame_rate;
	u32 ppnumber[PINGPONG_MAX];
	u32 vsync_source;
	bool is_dummy;
};

/**
@@ -155,13 +161,12 @@ struct sde_hw_mdp_ops {
			struct sde_danger_safe_status *status);

	/**
	 * setup_vsync_sel - get vsync configuration details
	 * setup_vsync_source - setup vsync source configuration details
	 * @mdp: mdp top context driver
	 * @cfg: watchdog timer configuration
	 * @watchdog_te: watchdog timer enable
	 * @cfg: vsync source selection configuration
	 */
	void (*setup_vsync_sel)(struct sde_hw_mdp *mdp,
			struct sde_watchdog_te_status *cfg, bool watchdog_te);
	void (*setup_vsync_source)(struct sde_hw_mdp *mdp,
				struct sde_vsync_source_cfg *cfg);

	/**
	 * get_safe_status - get safe status