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

Commit 73fb809e authored by Lloyd Atkinson's avatar Lloyd Atkinson
Browse files

drm/msm/sde: support left/right only partial updates



Add support for partial update regions of interest that fall
entirely on one other side of the display panel. In this case we
want to send the update only to the affected panel.

Change-Id: Idf951da02e1f591a9eaf524d1a8fbce41e150106
Signed-off-by: default avatarLloyd Atkinson <latkinso@codeaurora.org>
parent 0e6032e3
Loading
Loading
Loading
Loading
+75 −28
Original line number Diff line number Diff line
@@ -864,56 +864,83 @@ static int _sde_crtc_set_lm_roi(struct drm_crtc *crtc,
	lm_bounds = &crtc_state->lm_bounds[lm_idx];
	lm_roi = &crtc_state->lm_roi[lm_idx];

	if (!sde_kms_rect_is_null(crtc_roi)) {
		sde_kms_rect_intersect(crtc_roi, lm_bounds, lm_roi);
		if (sde_kms_rect_is_null(lm_roi)) {
			SDE_ERROR("unsupported R/L only partial update\n");
			return -EINVAL;
		}
	} else {
	if (sde_kms_rect_is_null(crtc_roi))
		memcpy(lm_roi, lm_bounds, sizeof(*lm_roi));
	}
	else
		sde_kms_rect_intersect(crtc_roi, lm_bounds, lm_roi);

	SDE_DEBUG("%s: lm%d roi (%d,%d,%d,%d)\n", sde_crtc->name, lm_idx,
			lm_roi->x, lm_roi->y, lm_roi->w, lm_roi->h);

	/* if any dimension is zero, clear all dimensions for clarity */
	if (sde_kms_rect_is_null(lm_roi))
		memset(lm_roi, 0, sizeof(*lm_roi));

	return 0;
}

static u32 _sde_crtc_get_displays_affected(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	u32 disp_bitmask = 0;
	int i;

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(state);

	for (i = 0; i < sde_crtc->num_mixers; i++) {
		if (!sde_kms_rect_is_null(&crtc_state->lm_roi[i]))
			disp_bitmask |= BIT(i);
	}

	SDE_DEBUG("affected displays 0x%x\n", disp_bitmask);

	return disp_bitmask;
}

static int _sde_crtc_check_rois_centered_and_symmetric(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *crtc_state;
	const struct sde_rect *roi_prv, *roi_cur;
	int lm_idx;
	const struct sde_rect *roi[CRTC_DUAL_MIXERS];

	if (!crtc || !state)
		return -EINVAL;

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(state);

	if (sde_crtc->num_mixers == 1)
		return 0;

	if (sde_crtc->num_mixers > CRTC_DUAL_MIXERS) {
		SDE_ERROR("%s: unsupported number of mixers: %d\n",
				sde_crtc->name, sde_crtc->num_mixers);
		return -EINVAL;
	}

	/*
	 * On certain HW, ROIs must be centered on the split between LMs,
	 * and be of equal width.
	 */
	roi[0] = &crtc_state->lm_roi[0];
	roi[1] = &crtc_state->lm_roi[1];

	sde_crtc = to_sde_crtc(crtc);
	crtc_state = to_sde_crtc_state(state);

	roi_prv = &crtc_state->lm_roi[0];
	for (lm_idx = 1; lm_idx < sde_crtc->num_mixers; lm_idx++) {
		roi_cur = &crtc_state->lm_roi[lm_idx];
	/* if one of the roi is null it's a left/right-only update */
	if (sde_kms_rect_is_null(roi[0]) || sde_kms_rect_is_null(roi[1]))
		return 0;

	/* check lm rois are equal width & first roi ends at 2nd roi */
		if (((roi_prv->x + roi_prv->w) != roi_cur->x) ||
				(roi_prv->w != roi_cur->w)) {
			SDE_ERROR("%s: roi lm%d x %d w %d lm%d x %d w %d\n",
					sde_crtc->name,
					lm_idx-1, roi_prv->x, roi_prv->w,
					lm_idx, roi_cur->x, roi_cur->w);
	if (roi[0]->x + roi[0]->w != roi[1]->x || roi[0]->w != roi[1]->w) {
		SDE_ERROR(
			"%s: rois not centered and symmetric: roi0 x %d w %d roi1 x %d w %d\n",
				sde_crtc->name, roi[0]->x, roi[0]->w,
				roi[1]->x, roi[1]->w);
		return -EINVAL;
	}
		roi_prv = roi_cur;
	}

	return 0;
}
@@ -1188,13 +1215,21 @@ static void _sde_crtc_blend_setup_mixer(struct drm_crtc *crtc,
 */
static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
{
	struct sde_crtc *sde_crtc = to_sde_crtc(crtc);
	struct sde_crtc_mixer *mixer = sde_crtc->mixers;
	struct sde_crtc *sde_crtc;
	struct sde_crtc_state *sde_crtc_state;
	struct sde_crtc_mixer *mixer;
	struct sde_hw_ctl *ctl;
	struct sde_hw_mixer *lm;

	int i;

	if (!crtc)
		return;

	sde_crtc = to_sde_crtc(crtc);
	sde_crtc_state = to_sde_crtc_state(crtc->state);
	mixer = sde_crtc->mixers;

	SDE_DEBUG("%s\n", sde_crtc->name);

	if (sde_crtc->num_mixers > CRTC_DUAL_MIXERS) {
@@ -1225,9 +1260,19 @@ static void _sde_crtc_blend_setup(struct drm_crtc *crtc)
	_sde_crtc_blend_setup_mixer(crtc, sde_crtc, mixer);

	for (i = 0; i < sde_crtc->num_mixers; i++) {
		const struct sde_rect *lm_roi = &sde_crtc_state->lm_roi[i];

		ctl = mixer[i].hw_ctl;
		lm = mixer[i].hw_lm;

		if (sde_kms_rect_is_null(lm_roi)) {
			SDE_DEBUG(
				"%s: lm%d leave ctl%d mask 0 since null roi\n",
					sde_crtc->name, lm->idx - LM_0,
					ctl->idx - CTL_0);
			continue;
		}

		lm->ops.setup_alpha_out(lm, mixer[i].mixer_op_mode);

		mixer[i].flush_mask |= ctl->ops.get_bitmask_mixer(ctl,
@@ -1912,6 +1957,8 @@ void sde_crtc_commit_kickoff(struct drm_crtc *crtc)
		 * If so, it may delay and flush at an irq event (e.g. ppdone)
		 */
		params.inline_rotate_prefill = cstate->sbuf_prefill_line;
		params.affected_displays = _sde_crtc_get_displays_affected(crtc,
				crtc->state);
		sde_encoder_prepare_for_kickoff(encoder, &params);
	}

+3 −3
Original line number Diff line number Diff line
@@ -248,10 +248,10 @@ struct sde_crtc_respool {
 * @num_connectors: Number of associated drm connectors
 * @intf_mode     : Interface mode of the primary connector
 * @rsc_client    : sde rsc client when mode is valid
 * @lm_bounds     : LM boundaries based on current mode full resolution, no ROI.
 *                  Origin top left of CRTC.
 * @crtc_roi      : Current CRTC ROI. Possibly sub-rectangle of mode.
 *                  Origin top left of CRTC.
 * @lm_bounds     : LM boundaries based on current mode full resolution, no ROI.
 *                  Origin top left of CRTC.
 * @lm_roi        : Current LM ROI, possibly sub-rectangle of mode.
 *                  Origin top left of CRTC.
 * @user_roi_list : List of user's requested ROIs as from set property
@@ -274,8 +274,8 @@ struct sde_crtc_state {
	struct sde_rsc_client *rsc_client;
	bool rsc_update;

	struct sde_rect lm_bounds[CRTC_DUAL_MIXERS];
	struct sde_rect crtc_roi;
	struct sde_rect lm_bounds[CRTC_DUAL_MIXERS];
	struct sde_rect lm_roi[CRTC_DUAL_MIXERS];
	struct msm_roi_list user_roi_list;

+82 −4
Original line number Diff line number Diff line
@@ -1463,6 +1463,14 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
		return;
	}

	if (phys->split_role == ENC_ROLE_SKIP) {
		SDE_DEBUG_ENC(to_sde_encoder_virt(phys->parent),
				"skip flush pp%d ctl%d\n",
				phys->hw_pp->idx - PINGPONG_0,
				ctl->idx - CTL_0);
		return;
	}

	pending_kickoff_cnt = sde_encoder_phys_inc_pending(phys);

	if (extra_flush_bits && ctl->ops.update_pending_flush)
@@ -1484,11 +1492,21 @@ static inline void _sde_encoder_trigger_flush(struct drm_encoder *drm_enc,
 */
static inline void _sde_encoder_trigger_start(struct sde_encoder_phys *phys)
{
	struct sde_hw_ctl *ctl;

	if (!phys) {
		SDE_ERROR("invalid encoder\n");
		return;
	}

	ctl = phys->hw_ctl;
	if (phys->split_role == ENC_ROLE_SKIP) {
		SDE_DEBUG_ENC(to_sde_encoder_virt(phys->parent),
				"skip start pp%d ctl%d\n",
				phys->hw_pp->idx - PINGPONG_0,
				ctl->idx - CTL_0);
		return;
	}
	if (phys->ops.trigger_start && phys->enable_state != SDE_ENC_DISABLED)
		phys->ops.trigger_start(phys);
}
@@ -1620,9 +1638,13 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
			topology = sde_connector_get_topology_name(
					phys->connector);

		/* don't wait on ppsplit slaves, they dont register irqs */
		/*
		 * don't wait on ppsplit slaves or skipped encoders because
		 * they dont receive irqs
		 */
		if (!(topology == SDE_RM_TOPOLOGY_PPSPLIT &&
				phys->split_role == ENC_ROLE_SLAVE))
				phys->split_role == ENC_ROLE_SLAVE) &&
				phys->split_role != ENC_ROLE_SKIP)
			set_bit(i, sde_enc->frame_busy_mask);

		if (!phys->ops.needs_single_flush ||
@@ -1645,6 +1667,60 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
	spin_unlock_irqrestore(&sde_enc->enc_spinlock, lock_flags);
}

static void _sde_encoder_update_master(struct drm_encoder *drm_enc,
		struct sde_encoder_kickoff_params *params)
{
	struct sde_encoder_virt *sde_enc;
	struct sde_encoder_phys *phys;
	int i, num_active_phys;
	bool master_assigned = false;

	if (!drm_enc || !params)
		return;

	sde_enc = to_sde_encoder_virt(drm_enc);

	if (sde_enc->num_phys_encs <= 1)
		return;

	/* count bits set */
	num_active_phys = hweight_long(params->affected_displays);

	SDE_DEBUG_ENC(sde_enc, "affected_displays 0x%lx num_active_phys %d\n",
			params->affected_displays, num_active_phys);

	for (i = 0; i < sde_enc->num_phys_encs; i++) {
		enum sde_enc_split_role prv_role, new_role;
		bool active;

		phys = sde_enc->phys_encs[i];
		if (!phys || !phys->ops.update_split_role)
			continue;

		active = test_bit(i, &params->affected_displays);
		prv_role = phys->split_role;

		if (active && num_active_phys == 1)
			new_role = ENC_ROLE_SOLO;
		else if (active && !master_assigned)
			new_role = ENC_ROLE_MASTER;
		else if (active)
			new_role = ENC_ROLE_SLAVE;
		else
			new_role = ENC_ROLE_SKIP;

		phys->ops.update_split_role(phys, new_role);
		if (new_role == ENC_ROLE_SOLO || new_role == ENC_ROLE_MASTER) {
			sde_enc->cur_master = phys;
			master_assigned = true;
		}

		SDE_DEBUG_ENC(sde_enc, "pp %d role prv %d new %d active %d\n",
				phys->hw_pp->idx - PINGPONG_0, prv_role,
				phys->split_role, active);
	}
}

void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
		struct sde_encoder_kickoff_params *params)
{
@@ -1654,8 +1730,8 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
	unsigned int i;
	int rc;

	if (!drm_enc) {
		SDE_ERROR("invalid encoder\n");
	if (!drm_enc || !params) {
		SDE_ERROR("invalid args\n");
		return;
	}
	sde_enc = to_sde_encoder_virt(drm_enc);
@@ -1685,6 +1761,8 @@ void sde_encoder_prepare_for_kickoff(struct drm_encoder *drm_enc,
		}
	}

	_sde_encoder_update_master(drm_enc, params);

	if (sde_enc->cur_master && sde_enc->cur_master->connector) {
		rc = sde_connector_pre_kickoff(sde_enc->cur_master->connector);
		if (rc)
+3 −0
Original line number Diff line number Diff line
@@ -47,9 +47,12 @@ struct sde_encoder_hw_resources {
/**
 * sde_encoder_kickoff_params - info encoder requires at kickoff
 * @inline_rotate_prefill: number of lines to prefill for inline rotation
 * @affected_displays:  bitmask, bit set means the ROI of the commit lies within
 *                      the bounds of the physical display at the bit index
 */
struct sde_encoder_kickoff_params {
	u32 inline_rotate_prefill;
	unsigned long affected_displays;
};

/**
+6 −2
Original line number Diff line number Diff line
@@ -41,11 +41,13 @@
 * @ENC_ROLE_SOLO:	This is the one and only panel. This encoder is master.
 * @ENC_ROLE_MASTER:	This encoder is the master of a split panel config.
 * @ENC_ROLE_SLAVE:	This encoder is not the master of a split panel config.
 * @ENC_ROLE_SKIP:	This encoder is not participating in kickoffs
 */
enum sde_enc_split_role {
	ENC_ROLE_SOLO,
	ENC_ROLE_MASTER,
	ENC_ROLE_SLAVE
	ENC_ROLE_SLAVE,
	ENC_ROLE_SKIP
};

/**
@@ -118,6 +120,7 @@ struct sde_encoder_virt_ops {
 * @hw_reset:			Issue HW recovery such as CTL reset and clear
 *				SDE_ENC_ERR_NEEDS_HW_RESET state
 * @irq_control:		Handler to enable/disable all the encoder IRQs
 * @update_split_role:		Update the split role of the phys enc
 */

struct sde_encoder_phys_ops {
@@ -152,6 +155,8 @@ struct sde_encoder_phys_ops {
	u32 (*collect_misr)(struct sde_encoder_phys *phys_enc);
	void (*hw_reset)(struct sde_encoder_phys *phys_enc);
	void (*irq_control)(struct sde_encoder_phys *phys, bool enable);
	void (*update_split_role)(struct sde_encoder_phys *phys_enc,
			enum sde_enc_split_role role);
};

/**
@@ -265,7 +270,6 @@ struct sde_encoder_phys_vid {
 */
struct sde_encoder_phys_cmd {
	struct sde_encoder_phys base;
	int intf_idx;
	int stream_sel;
	int irq_idx[INTR_IDX_MAX];
	struct sde_irq_callback irq_cb[INTR_IDX_MAX];
Loading