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

Commit 8e69ad00 authored by Clarence Ip's avatar Clarence Ip Committed by Narendra Muppalla
Browse files

drm/msm/sde: enable ping pong split support



Add support for using a single layer mixer to drive two video
or command mode interfaces. This configuration frees up one of
the layer mixer paths for other use cases/displays.

Change-Id: I3a0ca07e37f121bf56ea8984c0cca25af7bc647e
Signed-off-by: default avatarClarence Ip <cip@codeaurora.org>
parent 613fd8ad
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -58,6 +58,8 @@ Required properties
- qcom,sde-pp-off:		Array of offset addresses for the available
				pingpong blocks. These offsets are calculated
				from register "mdp_phys" defined in reg property.
- qcom,sde-pp-slave:		Array of flags indicating whether each ping pong
				block may be configured as a pp slave.
- qcom,sde-intf-off:		Array of offset addresses for the available SDE
				interface blocks that can drive data to a
				panel controller. The offsets are calculated
@@ -297,6 +299,7 @@ Example:
    qcom,sde-intf-type = "none", "dsi", "dsi", "hdmi";
    qcom,sde-pp-off = <0x00071000 0x00071800
			  0x00072000 0x00072800>;
    qcom,sde-pp-slave = <0x0 0x0 0x0 0x0>;
    qcom,sde-cdm-off = <0x0007a200>;
    qcom,sde-dsc-off = <0x00081000 0x00081400>;
    qcom,sde-intf-max-prefetch-lines = <0x15 0x15 0x15 0x15>;
+7 −4
Original line number Diff line number Diff line
@@ -291,8 +291,11 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
		/* stage config flush mask */
		ctl->ops.update_pending_flush(ctl, mixer[i].flush_mask);

		SDE_DEBUG("lm %d ctl %d add mask 0x%x to pending flush\n",
			mixer[i].hw_lm->idx, ctl->idx, mixer[i].flush_mask);
		SDE_DEBUG("lm %d, op_mode 0x%X, ctl %d, flush mask 0x%x\n",
			mixer[i].hw_lm->idx - LM_0,
			mixer[i].mixer_op_mode,
			ctl->idx - CTL_0,
			mixer[i].flush_mask);

		ctl->ops.setup_blendstage(ctl, mixer[i].hw_lm->idx,
			&sde_crtc->stage_cfg, i);
@@ -493,7 +496,7 @@ static void _sde_crtc_setup_mixer_for_encoder(
		/* CTL may be <= LMs, if <, multiple LMs controlled by 1 CTL */
		if (!sde_rm_get_hw(rm, &ctl_iter)) {
			SDE_DEBUG("no ctl assigned to lm %d, using previous\n",
					mixer->hw_lm->idx);
					mixer->hw_lm->idx - LM_0);
			mixer->hw_ctl = last_valid_ctl;
		} else {
			mixer->hw_ctl = (struct sde_hw_ctl *)ctl_iter.hw;
@@ -503,7 +506,7 @@ static void _sde_crtc_setup_mixer_for_encoder(
		/* Shouldn't happen, mixers are always >= ctls */
		if (!mixer->hw_ctl) {
			SDE_ERROR("no valid ctls found for lm %d\n",
					mixer->hw_lm->idx);
					mixer->hw_lm->idx - LM_0);
			return;
		}

+59 −6
Original line number Diff line number Diff line
@@ -244,6 +244,57 @@ void sde_encoder_destroy(struct drm_encoder *drm_enc)
	kfree(sde_enc);
}

void sde_encoder_helper_split_config(
		struct sde_encoder_phys *phys_enc,
		enum sde_intf interface)
{
	struct sde_encoder_virt *sde_enc;
	struct split_pipe_cfg cfg = { 0 };
	struct sde_hw_mdp *hw_mdptop;
	enum sde_rm_topology_name topology;

	if (!phys_enc || !phys_enc->hw_mdptop || !phys_enc->parent) {
		SDE_ERROR("invalid arg(s), encoder %d\n", phys_enc != 0);
		return;
	}

	sde_enc = to_sde_encoder_virt(phys_enc->parent);
	hw_mdptop = phys_enc->hw_mdptop;
	cfg.en = phys_enc->split_role != ENC_ROLE_SOLO;
	cfg.mode = phys_enc->intf_mode;
	cfg.intf = interface;

	if (cfg.en && phys_enc->ops.needs_single_flush &&
			phys_enc->ops.needs_single_flush(phys_enc))
		cfg.split_flush_en = true;

	topology = sde_connector_get_topology_name(phys_enc->connector);
	if (topology == SDE_RM_TOPOLOGY_PPSPLIT)
		cfg.pp_split_slave = cfg.intf;
	else
		cfg.pp_split_slave = INTF_MAX;

	if (phys_enc->split_role != ENC_ROLE_SLAVE) {
		/* master/solo encoder */
		SDE_DEBUG_ENC(sde_enc, "enable %d\n", cfg.en);

		if (hw_mdptop->ops.setup_split_pipe)
			hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg);
	} else {
		/*
		 * slave encoder
		 * - determine split index from master index,
		 *   assume master is first pp
		 */
		cfg.pp_split_index = sde_enc->hw_pp[0]->idx - PINGPONG_0;
		SDE_DEBUG_ENC(sde_enc, "master using pp%d\n",
				cfg.pp_split_index);

		if (hw_mdptop->ops.setup_pp_split)
			hw_mdptop->ops.setup_pp_split(hw_mdptop, &cfg);
	}
}

static int sde_encoder_virt_atomic_check(
		struct drm_encoder *drm_enc,
		struct drm_crtc_state *crtc_state,
@@ -579,6 +630,7 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
		struct sde_encoder_phys *phys, uint32_t extra_flush_bits)
{
	struct sde_hw_ctl *ctl;
	int pending_kickoff_cnt;

	if (!drm_enc || !phys) {
		SDE_ERROR("invalid argument(s), drm_enc %d, phys_enc %d\n",
@@ -592,6 +644,10 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
		return;
	}

	pending_kickoff_cnt = sde_encoder_phys_inc_pending(phys);
	SDE_EVT32(DRMID(&to_sde_encoder_virt(drm_enc)->base),
			phys->intf_idx, pending_kickoff_cnt);

	if (extra_flush_bits && ctl->ops.update_pending_flush)
		ctl->ops.update_pending_flush(ctl, extra_flush_bits);

@@ -672,7 +728,6 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
	struct sde_hw_ctl *ctl;
	uint32_t i, pending_flush;
	unsigned long lock_flags;
	int pending_kickoff_cnt;

	if (!sde_enc) {
		SDE_ERROR("invalid encoder\n");
@@ -687,18 +742,16 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
	/* don't perform flush/start operations for slave encoders */
	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		struct sde_encoder_phys *phys = sde_enc->phys_encs[i];

		if (!phys || phys->enable_state == SDE_ENC_DISABLED)
			continue;

		pending_kickoff_cnt = sde_encoder_phys_inc_pending(phys);
		SDE_EVT32(DRMID(&sde_enc->base), i, pending_kickoff_cnt);

		ctl = phys->hw_ctl;
		if (!ctl)
			continue;

		if (!phys->ops.needs_split_flush ||
				!phys->ops.needs_split_flush(phys))
		if (!phys->ops.needs_single_flush ||
				!phys->ops.needs_single_flush(phys))
			_sde_encoder_trigger_flush(&sde_enc->base, phys, 0x0);
		else if (ctl->ops.get_pending_flush)
			pending_flush |= ctl->ops.get_pending_flush(ctl);
+13 −2
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ struct sde_encoder_virt_ops {
 *				For CMD encoder, may wait for previous tx done
 * @handle_post_kickoff:	Do any work necessary post-kickoff work
 * @trigger_start:		Process start event on physical encoder
 * @needs_split_flush:		Whether encoder type needs split flush
 * @needs_single_flush:		Whether encoder slaves need to be flushed
 * @setup_misr:		Sets up MISR, enable and disables based on sysfs
 * @collect_misr:		Collects MISR data on frame update
 */
@@ -114,7 +114,7 @@ struct sde_encoder_phys_ops {
	void (*prepare_for_kickoff)(struct sde_encoder_phys *phys_enc);
	void (*handle_post_kickoff)(struct sde_encoder_phys *phys_enc);
	void (*trigger_start)(struct sde_encoder_phys *phys_enc);
	bool (*needs_split_flush)(struct sde_encoder_phys *phys_enc);
	bool (*needs_single_flush)(struct sde_encoder_phys *phys_enc);

	void (*setup_misr)(struct sde_encoder_phys *phys_encs,
			struct sde_misr_params *misr_map);
@@ -388,4 +388,15 @@ static inline enum sde_3d_blend_mode sde_encoder_helper_get_3d_blend_mode(
	return BLEND_3D_NONE;
}

/**
 * sde_encoder_helper_split_config - split display configuration helper function
 *	This helper function may be used by physical encoders to configure
 *	the split display related registers.
 * @phys_enc: Pointer to physical encoder structure
 * @interface: enum sde_intf setting
 */
void sde_encoder_helper_split_config(
		struct sde_encoder_phys *phys_enc,
		enum sde_intf interface);

#endif /* __sde_encoder_phys_H__ */
+64 −53
Original line number Diff line number Diff line
@@ -80,8 +80,7 @@ static void sde_encoder_phys_cmd_mode_set(
	/* Retrieve previously allocated HW Resources. Shouldn't fail */
	sde_rm_init_hw_iter(&iter, phys_enc->parent->base.id, SDE_HW_BLK_CTL);
	for (i = 0; i <= instance; i++) {
		sde_rm_get_hw(rm, &iter);
		if (i == instance)
		if (sde_rm_get_hw(rm, &iter))
			phys_enc->hw_ctl = (struct sde_hw_ctl *)iter.hw;
	}

@@ -129,6 +128,22 @@ static void sde_encoder_phys_cmd_pp_rd_ptr_irq(void *arg, int irq_idx)
			phys_enc);
}

static bool _sde_encoder_phys_is_ppsplit_slave(
		struct sde_encoder_phys *phys_enc)
{
	enum sde_rm_topology_name topology;

	if (!phys_enc)
		return false;

	topology = sde_connector_get_topology_name(phys_enc->connector);
	if (topology == SDE_RM_TOPOLOGY_PPSPLIT &&
			phys_enc->split_role == ENC_ROLE_SLAVE)
		return true;

	return false;
}

static int _sde_encoder_phys_cmd_wait_for_idle(
		struct sde_encoder_phys *phys_enc)
{
@@ -137,6 +152,15 @@ static int _sde_encoder_phys_cmd_wait_for_idle(
	u32 irq_status;
	int ret;

	if (!phys_enc) {
		SDE_ERROR("invalid encoder\n");
		return -EINVAL;
	}

	/* slave encoder doesn't enable for ppsplit */
	if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
		return 0;

	/* return EWOULDBLOCK since we know the wait isn't necessary */
	if (phys_enc->enable_state == SDE_ENC_DISABLED) {
		SDE_ERROR_CMDENC(cmd_enc, "encoder is disabled\n");
@@ -374,34 +398,16 @@ static void sde_encoder_phys_cmd_pingpong_config(
	sde_encoder_phys_cmd_tearcheck_config(phys_enc);
}

static bool sde_encoder_phys_cmd_needs_split_flush(
static bool sde_encoder_phys_cmd_needs_single_flush(
		struct sde_encoder_phys *phys_enc)
{
	return false;
}

static void sde_encoder_phys_cmd_split_config(
		struct sde_encoder_phys *phys_enc, bool enable)
{
	struct sde_encoder_phys_cmd *cmd_enc =
		to_sde_encoder_phys_cmd(phys_enc);
	struct sde_hw_mdp *hw_mdptop = phys_enc->hw_mdptop;
	struct split_pipe_cfg cfg = { 0 };

	if (!phys_enc) {
		SDE_ERROR("invalid encoder\n");
		return;
	}
	SDE_DEBUG_CMDENC(cmd_enc, "enable %d\n", enable);
	enum sde_rm_topology_name topology;

	cfg.en = enable;
	cfg.mode = INTF_MODE_CMD;
	cfg.intf = cmd_enc->intf_idx;
	cfg.split_flush_en = enable &&
		sde_encoder_phys_cmd_needs_split_flush(phys_enc);
	if (!phys_enc)
		return false;

	if (hw_mdptop && hw_mdptop->ops.setup_split_pipe)
		hw_mdptop->ops.setup_split_pipe(hw_mdptop, &cfg);
	topology = sde_connector_get_topology_name(phys_enc->connector);
	return topology == SDE_RM_TOPOLOGY_PPSPLIT;
}

static int sde_encoder_phys_cmd_control_vblank_irq(
@@ -453,28 +459,26 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc)
		to_sde_encoder_phys_cmd(phys_enc);
	struct sde_hw_ctl *ctl;
	u32 flush_mask;
	int ret = 0;
	int ret;

	if (!phys_enc) {
		SDE_ERROR("invalid encoder\n");
	if (!phys_enc || !phys_enc->hw_ctl) {
		SDE_ERROR("invalid arg(s), encoder %d\n", phys_enc != 0);
		return;
	}
	SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);

	if (WARN_ON(phys_enc->enable_state == SDE_ENC_ENABLED))
	if (phys_enc->enable_state == SDE_ENC_ENABLED) {
		SDE_ERROR("already enabled\n");
		return;
	}

	/*
	 * Only master configures master/slave configuration, so no slave check
	 * In solo configuration, solo encoder needs to program no-split
	 */
	if (phys_enc->split_role == ENC_ROLE_MASTER)
		sde_encoder_phys_cmd_split_config(phys_enc, true);
	else if (phys_enc->split_role == ENC_ROLE_SOLO)
		sde_encoder_phys_cmd_split_config(phys_enc, false);
	sde_encoder_helper_split_config(phys_enc, cmd_enc->intf_idx);

	sde_encoder_phys_cmd_pingpong_config(phys_enc);

	if (_sde_encoder_phys_is_ppsplit_slave(phys_enc))
		goto update_flush;

	/* Both master and slave need to register for pp_tx_done */
	ret = sde_encoder_phys_cmd_register_irq(phys_enc,
			SDE_IRQ_TYPE_PING_PONG_COMP,
@@ -503,6 +507,7 @@ static void sde_encoder_phys_cmd_enable(struct sde_encoder_phys *phys_enc)
		return;
	}

update_flush:
	ctl = phys_enc->hw_ctl;
	ctl->ops.get_bitmask_intf(ctl, &flush_mask, cmd_enc->intf_idx);
	ctl->ops.update_pending_flush(ctl, flush_mask);
@@ -524,24 +529,30 @@ static void sde_encoder_phys_cmd_disable(struct sde_encoder_phys *phys_enc)
	}
	SDE_DEBUG_CMDENC(cmd_enc, "pp %d\n", phys_enc->hw_pp->idx - PINGPONG_0);

	if (WARN_ON(phys_enc->enable_state == SDE_ENC_DISABLED))
	if (phys_enc->enable_state == SDE_ENC_DISABLED) {
		SDE_ERROR_CMDENC(cmd_enc, "already disabled\n");
		return;
	}

	SDE_EVT32(DRMID(phys_enc->parent), phys_enc->hw_pp->idx - PINGPONG_0);

	if (!_sde_encoder_phys_is_ppsplit_slave(phys_enc)) {
		ret = _sde_encoder_phys_cmd_wait_for_idle(phys_enc);
		if (ret) {
			atomic_set(&phys_enc->pending_kickoff_cnt, 0);
			SDE_ERROR_CMDENC(cmd_enc,
				"pp %d failed wait for idle at disable: %d\n",
					"pp %d failed wait for idle, %d\n",
					phys_enc->hw_pp->idx - PINGPONG_0, ret);
			SDE_EVT32(DRMID(phys_enc->parent),
					phys_enc->hw_pp->idx - PINGPONG_0, ret);
		}

	sde_encoder_phys_cmd_unregister_irq(phys_enc, INTR_IDX_UNDERRUN);
		sde_encoder_phys_cmd_unregister_irq(
				phys_enc, INTR_IDX_UNDERRUN);
		sde_encoder_phys_cmd_control_vblank_irq(phys_enc, false);
	sde_encoder_phys_cmd_unregister_irq(phys_enc, INTR_IDX_PINGPONG);
		sde_encoder_phys_cmd_unregister_irq(
				phys_enc, INTR_IDX_PINGPONG);
	}

	phys_enc->enable_state = SDE_ENC_DISABLED;

@@ -634,7 +645,7 @@ 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->trigger_start = sde_encoder_helper_trigger_start;
	ops->needs_split_flush = sde_encoder_phys_cmd_needs_split_flush;
	ops->needs_single_flush = sde_encoder_phys_cmd_needs_single_flush;
}

struct sde_encoder_phys *sde_encoder_phys_cmd_init(
Loading