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

Commit 4b37577b authored by Steve Cohen's avatar Steve Cohen
Browse files

drm/msm/sde: properly disable DSC with ACTIVE CTL



The ACTIVE CTL present in recent HW revs requires some special
handling for disabling DSC. This patch includes the following
changes:
-Disconnect DSC binding from it's associated ping-pong
-Make sure DSC block is properly flushed in DSC disable path
-Clear the DSC ACTIVE bits in CTL

These changes are required to handle seamless mode switching
between DSC and non-DSC modes.

Change-Id: I76e18560240c293797275ea42df520ff9d816e15
Signed-off-by: default avatarSteve Cohen <cohens@codeaurora.org>
parent 17b302c0
Loading
Loading
Loading
Loading
+75 −15
Original line number Diff line number Diff line
@@ -177,6 +177,7 @@ enum sde_enc_rc_states {
 * @hw_pp		Handle to the pingpong blocks used for the display. No.
 *			pingpong blocks can be different than num_phys_encs.
 * @hw_dsc:		Array of DSC block handles used for the display.
 * @dirty_dsc_ids:	Cached dsc indexes for dirty DSC blocks needing flush
 * @intfs_swapped	Whether or not the phys_enc interfaces have been swapped
 *			for partial update right-only cases, such as pingpong
 *			split where virtual pingpong does not generate IRQs
@@ -236,6 +237,7 @@ struct sde_encoder_virt {
	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];
	enum sde_dsc dirty_dsc_ids[MAX_CHANNELS_PER_ENC];

	bool intfs_swapped;

@@ -1188,8 +1190,20 @@ static void _sde_encoder_dsc_pipe_cfg(struct sde_hw_dsc *hw_dsc,
		u32 common_mode, bool ich_reset, bool enable)
{
	if (!enable) {
		if (hw_pp->ops.disable_dsc)
		if (hw_pp && hw_pp->ops.disable_dsc)
			hw_pp->ops.disable_dsc(hw_pp);

		if (hw_dsc && hw_dsc->ops.dsc_disable)
			hw_dsc->ops.dsc_disable(hw_dsc);

		if (hw_dsc && hw_dsc->ops.bind_pingpong_blk)
			hw_dsc->ops.bind_pingpong_blk(hw_dsc, false,
					PINGPONG_MAX);
		return;
	}

	if (!dsc || !hw_dsc || !hw_pp) {
		SDE_ERROR("invalid params %d %d %d\n", !dsc, !hw_dsc, !hw_pp);
		return;
	}

@@ -1279,10 +1293,6 @@ static int _sde_encoder_dsc_n_lm_1_enc_1_intf(struct sde_encoder_virt *sde_enc)

	_sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, dsc, dsc_common_mode,
			ich_res, true);
	if (cfg.dsc_count >= MAX_DSC_PER_CTL_V1) {
		pr_err("Invalid dsc count:%d\n", cfg.dsc_count);
		return -EINVAL;
	}
	cfg.dsc[cfg.dsc_count++] = hw_dsc->idx;

	/* setup dsc active configuration in the control path */
@@ -1402,8 +1412,7 @@ static int _sde_encoder_dsc_2_lm_2_enc_2_intf(struct sde_encoder_virt *sde_enc,
						cfg.dsc_count);
				return -EINVAL;
			}
			cfg.dsc[i] = hw_dsc[i]->idx;
			cfg.dsc_count++;
			cfg.dsc[cfg.dsc_count++] = hw_dsc[i]->idx;

			if (hw_ctl->ops.update_bitmask_dsc)
				hw_ctl->ops.update_bitmask_dsc(hw_ctl,
@@ -1713,32 +1722,47 @@ static void _sde_encoder_update_vsync_source(struct sde_encoder_virt *sde_enc,
	}
}

static int _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc)
static void _sde_encoder_dsc_disable(struct sde_encoder_virt *sde_enc)
{
	int i, ret = 0;
	int i;
	struct sde_hw_pingpong *hw_pp = NULL;
	struct sde_hw_dsc *hw_dsc = NULL;
	struct sde_hw_ctl *hw_ctl = NULL;
	struct sde_ctl_dsc_cfg cfg;

	if (!sde_enc || !sde_enc->phys_encs[0] ||
			!sde_enc->phys_encs[0]->connector) {
		SDE_ERROR("invalid params %d %d\n",
			!sde_enc, sde_enc ? !sde_enc->phys_encs[0] : -1);
		return -EINVAL;
		return;
	}

	if (sde_enc->cur_master)
		hw_ctl = sde_enc->cur_master->hw_ctl;

	/* Disable DSC for all the pp's present in this topology */
	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
		hw_pp = sde_enc->hw_pp[i];
		hw_dsc = sde_enc->hw_dsc[i];

		if (hw_pp && hw_pp->ops.disable_dsc)
			hw_pp->ops.disable_dsc(hw_pp);
		_sde_encoder_dsc_pipe_cfg(hw_dsc, hw_pp, NULL, 0, 0, 0);

		if (hw_dsc && hw_dsc->ops.dsc_disable)
			hw_dsc->ops.dsc_disable(hw_dsc);
		if (hw_dsc)
			sde_enc->dirty_dsc_ids[i] = hw_dsc->idx;
	}

	return ret;
	/* Clear the DSC ACTIVE config for this CTL */
	if (hw_ctl && hw_ctl->ops.setup_dsc_cfg) {
		memset(&cfg, 0, sizeof(cfg));
		hw_ctl->ops.setup_dsc_cfg(hw_ctl, &cfg);
	}

	/**
	 * Since pending flushes from previous commit get cleared
	 * sometime after this point, setting DSC flush bits now
	 * will have no effect. Therefore dirty_dsc_ids track which
	 * DSC blocks must be flushed for the next trigger.
	 */
}

static int _sde_encoder_switch_to_watchdog_vsync(struct drm_encoder *drm_enc)
@@ -4335,6 +4359,40 @@ static int _helper_flush_qsync(struct sde_encoder_phys *phys_enc)
	return 0;
}

static bool _sde_encoder_dsc_is_dirty(struct sde_encoder_virt *sde_enc)
{
	int i;

	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
		/**
		 * This dirty_dsc_hw field is set during DSC disable to
		 * indicate which DSC blocks need to be flushed
		 */
		if (sde_enc->dirty_dsc_ids[i])
			return true;
	}

	return false;
}

static void _helper_flush_dsc(struct sde_encoder_virt *sde_enc)
{
	int i;
	struct sde_hw_ctl *hw_ctl = NULL;
	enum sde_dsc dsc_idx;

	if (sde_enc->cur_master)
		hw_ctl = sde_enc->cur_master->hw_ctl;

	for (i = 0; i < MAX_CHANNELS_PER_ENC; i++) {
		dsc_idx = sde_enc->dirty_dsc_ids[i];
		if (dsc_idx && hw_ctl && hw_ctl->ops.update_bitmask_dsc)
			hw_ctl->ops.update_bitmask_dsc(hw_ctl, dsc_idx, 1);

		sde_enc->dirty_dsc_ids[i] = DSC_NONE;
	}
}

int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
		struct sde_encoder_kickoff_params *params)
{
@@ -4444,6 +4502,8 @@ int sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
			SDE_ERROR_ENC(sde_enc, "failed to setup DSC: %d\n", rc);
			ret = rc;
		}
	} else if (_sde_encoder_dsc_is_dirty(sde_enc)) {
		_helper_flush_dsc(sde_enc);
	}

end: