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

Commit 99412a59 authored by Dhaval Patel's avatar Dhaval Patel
Browse files

drm/msm/sde: update autorefresh enable/disable support



Autorefresh feature enable should avoid the idle power
collapse entry and a normal frame update at the end of
autorefresh must re-enable the idle power collapse
thread. Autorefresh disable path does not need to disable
the VSYNC and turn off the feature between pp_done and
wrap-around TE from sdm845 target. Software can kickoff
the disable request and can wait for last auto-refresh
frame to finish before sending any kickoff from clients.
It avoids sde rsc HW not getting vsync case and avoids
mode-2 exit failure followed by pp_timeout.

Change-Id: If11b78f2013d8e166f31c7923027d349a89e1b5a
Signed-off-by: default avatarDhaval Patel <pdhaval@codeaurora.org>
parent 70be28c9
Loading
Loading
Loading
Loading
+10 −2
Original line number Diff line number Diff line
@@ -1335,6 +1335,7 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
		u32 sw_event)
{
	bool schedule_off = false;
	bool autorefresh_enabled = false;
	struct sde_encoder_virt *sde_enc;
	struct msm_drm_private *priv;
	struct msm_drm_thread *disp_thread;
@@ -1417,7 +1418,14 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
			return 0;
		}

		/* schedule delayed off work */
		/* schedule delayed off work if autorefresh is disabled */
		if (sde_enc->cur_master &&
			sde_enc->cur_master->ops.is_autorefresh_enabled)
			autorefresh_enabled =
				sde_enc->cur_master->ops.is_autorefresh_enabled(
							sde_enc->cur_master);

		if (!autorefresh_enabled)
			kthread_queue_delayed_work(
				&disp_thread->worker,
				&sde_enc->delayed_off_work,
+3 −0
Original line number Diff line number Diff line
@@ -125,6 +125,8 @@ struct sde_encoder_virt_ops {
 * @irq_control:		Handler to enable/disable all the encoder IRQs
 * @update_split_role:		Update the split role of the phys enc
 * @restore:			Restore all the encoder configs.
 * @is_autorefresh_enabled:	provides the autorefresh current
 *                              enable/disable state.
 */

struct sde_encoder_phys_ops {
@@ -164,6 +166,7 @@ struct sde_encoder_phys_ops {
	void (*update_split_role)(struct sde_encoder_phys *phys_enc,
			enum sde_enc_split_role role);
	void (*restore)(struct sde_encoder_phys *phys);
	bool (*is_autorefresh_enabled)(struct sde_encoder_phys *phys);
};

/**
+39 −86
Original line number Diff line number Diff line
@@ -707,9 +707,6 @@ static void sde_encoder_phys_cmd_tearcheck_config(

	tc_cfg.vsync_count = vsync_hz / (mode->vtotal * mode->vrefresh);

	/* enable external TE after kickoff to avoid premature autorefresh */
	tc_cfg.hw_vsync_mode = 0;

	/*
	 * By setting sync_cfg_height to near max register value, we essentially
	 * disable sde hw generated TE signal, since hw TE will arrive first.
@@ -721,6 +718,7 @@ static void sde_encoder_phys_cmd_tearcheck_config(
	tc_cfg.sync_threshold_continue = DEFAULT_TEARCHECK_SYNC_THRESH_CONTINUE;
	tc_cfg.start_pos = mode->vdisplay;
	tc_cfg.rd_ptr_irq = mode->vdisplay + 1;
	tc_cfg.hw_vsync_mode = true;

	SDE_DEBUG_CMDENC(cmd_enc,
		"tc %d vsync_clk_speed_hz %u vtotal %u vrefresh %u\n",
@@ -825,7 +823,7 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc)
	phys_enc->enable_state = SDE_ENC_ENABLED;
}

static bool _sde_encoder_phys_cmd_is_autorefresh_enabled(
static bool sde_encoder_phys_cmd_is_autorefresh_enabled(
		struct sde_encoder_phys *phys_enc)
{
	struct sde_hw_pingpong *hw_pp;
@@ -849,17 +847,6 @@ static bool _sde_encoder_phys_cmd_is_autorefresh_enabled(
	return cfg.enable;
}

static void _sde_encoder_phys_cmd_connect_te(
		struct sde_encoder_phys *phys_enc, bool enable)
{
	if (!phys_enc || !phys_enc->hw_pp ||
			!phys_enc->hw_pp->ops.connect_external_te)
		return;

	SDE_EVT32(DRMID(phys_enc->parent), enable);
	phys_enc->hw_pp->ops.connect_external_te(phys_enc->hw_pp, enable);
}

static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc)
{
	struct sde_encoder_phys_cmd *cmd_enc =
@@ -1083,42 +1070,31 @@ static void sde_encoder_phys_cmd_prepare_commit(
{
	struct sde_encoder_phys_cmd *cmd_enc =
		to_sde_encoder_phys_cmd(phys_enc);
	unsigned long lock_flags;

	if (!phys_enc)
		return;

	if (sde_encoder_phys_cmd_is_master(phys_enc)) {
		unsigned long lock_flags;

	if (!sde_encoder_phys_cmd_is_master(phys_enc))
		return;

	SDE_EVT32(DRMID(phys_enc->parent), phys_enc->intf_idx - INTF_0,
			cmd_enc->autorefresh.cfg.enable);

		if (!_sde_encoder_phys_cmd_is_autorefresh_enabled(phys_enc))
	if (!sde_encoder_phys_cmd_is_autorefresh_enabled(phys_enc))
		return;

	/**
	 * Autorefresh must be disabled carefully:
		 *  - Must disable while there is no ongoing transmission
		 *  - Receiving a TE will trigger the next Autorefresh TX
		 *  - Only safe to disable Autorefresh between PPDone and TE
		 *  - However, that is a small time window
		 *  - Disabling External TE gives large safe window, assuming
		 *    internally generated TE is set to a large counter value
	 *  - Autorefresh must be disabled between pp_done and te
	 *    signal prior to sdm845 targets. All targets after sdm845
	 *    supports autorefresh disable without turning off the
	 *    hardware TE and pp_done wait.
	 *
		 * If Autorefresh is active:
		 * 1. Disable external TE
		 *   - TE will run on an SDE counter set to large value (~200ms)
	 *  - Wait for TX to Complete
	 *    Wait for PPDone confirms the last frame transfer is complete.
	 *
		 * 2. Check for ongoing TX
		 *   - If ongoing TX, set pending_kickoff_cnt if not set already
		 *   - We don't want to wait for a ppdone that will never
		 *     arrive, so verify ongoing TX
		 *
		 * 3. Wait for TX to Complete
		 *  - Wait for PPDone pending count to reach 0
		 *
		 * 4. Leave Autorefresh Disabled
	 *  - Leave Autorefresh Disabled
	 *    - Assume disable of Autorefresh since it is now safe
	 *    - Can now safely Disable Encoder, do debug printing, etc.
	 *     without worrying that Autorefresh will kickoff
@@ -1126,43 +1102,19 @@ static void sde_encoder_phys_cmd_prepare_commit(

	spin_lock_irqsave(phys_enc->enc_spinlock, lock_flags);

		/* disable external TE to prevent next autorefresh */
		_sde_encoder_phys_cmd_connect_te(phys_enc, false);
	_sde_encoder_phys_cmd_config_autorefresh(phys_enc, 0);

		/* verify that we disabled TE during outstanding TX */
	/* check for outstanding TX */
	if (_sde_encoder_phys_cmd_is_ongoing_pptx(phys_enc))
		atomic_add_unless(&phys_enc->pending_kickoff_cnt, 1, 1);

	spin_unlock_irqrestore(phys_enc->enc_spinlock, lock_flags);

	/* wait for ppdone if necessary due to catching ongoing TX */
	if (_sde_encoder_phys_cmd_wait_for_idle(phys_enc))
			SDE_ERROR_CMDENC(cmd_enc,
					"pp:%d kickoff timed out\n",
		SDE_ERROR_CMDENC(cmd_enc, "pp:%d kickoff timed out\n",
				phys_enc->hw_pp->idx - PINGPONG_0);

		/*
		 * not strictly necessary for kickoff, but simplifies disable
		 * callflow since our disable is split across multiple phys_encs
		 */
		_sde_encoder_phys_cmd_config_autorefresh(phys_enc, 0);

		SDE_DEBUG_CMDENC(cmd_enc, "disabled autorefresh & ext TE\n");

	}
}

static void sde_encoder_phys_cmd_handle_post_kickoff(
		struct sde_encoder_phys *phys_enc)
{
	if (!phys_enc)
		return;

	/**
	 * re-enable external TE, either for the first time after enabling
	 * or if disabled for Autorefresh
	 */
	_sde_encoder_phys_cmd_connect_te(phys_enc, true);
	SDE_DEBUG_CMDENC(cmd_enc, "disabled autorefresh\n");
}

static void sde_encoder_phys_cmd_trigger_start(
@@ -1200,13 +1152,14 @@ static void sde_encoder_phys_cmd_init_ops(
	ops->wait_for_commit_done = sde_encoder_phys_cmd_wait_for_commit_done;
	ops->prepare_for_kickoff = sde_encoder_phys_cmd_prepare_for_kickoff;
	ops->wait_for_tx_complete = sde_encoder_phys_cmd_wait_for_tx_complete;
	ops->handle_post_kickoff = sde_encoder_phys_cmd_handle_post_kickoff;
	ops->trigger_start = sde_encoder_phys_cmd_trigger_start;
	ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
	ops->hw_reset = sde_encoder_helper_hw_reset;
	ops->irq_control = sde_encoder_phys_cmd_irq_control;
	ops->update_split_role = sde_encoder_phys_cmd_update_split_role;
	ops->restore = sde_encoder_phys_cmd_enable_helper;
	ops->is_autorefresh_enabled =
			sde_encoder_phys_cmd_is_autorefresh_enabled;
}

struct sde_encoder_phys *sde_encoder_phys_cmd_init(
+0 −24
Original line number Diff line number Diff line
@@ -231,29 +231,6 @@ static int sde_hw_pp_enable_te(struct sde_hw_pingpong *pp, bool enable)
	return 0;
}

static int sde_hw_pp_connect_external_te(struct sde_hw_pingpong *pp,
		bool enable_external_te)
{
	struct sde_hw_blk_reg_map *c = &pp->hw;
	u32 cfg;
	int orig;

	if (!pp)
		return -EINVAL;

	c = &pp->hw;
	cfg = SDE_REG_READ(c, PP_SYNC_CONFIG_VSYNC);
	orig = (bool)(cfg & BIT(20));
	if (enable_external_te)
		cfg |= BIT(20);
	else
		cfg &= ~BIT(20);
	SDE_REG_WRITE(c, PP_SYNC_CONFIG_VSYNC, cfg);
	SDE_EVT32(pp->idx - PINGPONG_0, cfg);

	return orig;
}

static int sde_hw_pp_get_vsync_info(struct sde_hw_pingpong *pp,
		struct sde_hw_pp_vsync_info *info)
{
@@ -280,7 +257,6 @@ static void _setup_pingpong_ops(struct sde_hw_pingpong_ops *ops,

	ops->setup_tearcheck = sde_hw_pp_setup_te_config;
	ops->enable_tearcheck = sde_hw_pp_enable_te;
	ops->connect_external_te = sde_hw_pp_connect_external_te;
	ops->get_vsync_info = sde_hw_pp_get_vsync_info;
	ops->setup_autorefresh = sde_hw_pp_setup_autorefresh_config;
	ops->setup_dsc = sde_hw_pp_setup_dsc;
+0 −7
Original line number Diff line number Diff line
@@ -79,13 +79,6 @@ struct sde_hw_pingpong_ops {
	int (*enable_tearcheck)(struct sde_hw_pingpong *pp,
			bool enable);

	/**
	 * read, modify, write to either set or clear listening to external TE
	 * @Return: 1 if TE was originally connected, 0 if not, or -ERROR
	 */
	int (*connect_external_te)(struct sde_hw_pingpong *pp,
			bool enable_external_te);

	/**
	 * provides the programmed and current
	 * line_count