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

Commit 39c9b323 authored by Dhaval Patel's avatar Dhaval Patel
Browse files

drm/msm/sde: use atomic counter for pending frame done



SDE encoder uses bit mask logic to check the pending
pp_done IRQ for dual dsi and trigger release fence
when both IRQs are received. It causes issue when
encoder triggers two frames back to back without
waiting for first frame done. Bitmask clears the
frame done prematurely and miss to trigger the release
fence for second frame. Atomic counter usages allows
to track the number of pending IRQ and release
frame_done when both pp_done IRQs are received.

Change-Id: I669aea124bae922641afe5d34f8f035dc5de82b3
Signed-off-by: default avatarDhaval Patel <pdhaval@codeaurora.org>
parent 7ef8c71c
Loading
Loading
Loading
Loading
+45 −46
Original line number Diff line number Diff line
@@ -189,9 +189,8 @@ enum sde_enc_rc_states {
 * @debugfs_root:		Debug file system root file node
 * @enc_lock:			Lock around physical encoder create/destroy and
				access.
 * @frame_busy_mask:		Bitmask tracking which phys_enc we are still
 *				busy processing current command.
 *				Bit0 = phys_encs[0] etc.
 * @frame_done_cnt:		Atomic counter for tracking which phys_enc is
 *				done with frame processing.
 * @crtc_frame_event_cb:	callback handler for frame event
 * @crtc_frame_event_cb_data:	callback handler private data
 * @vsync_event_timer:		vsync timer
@@ -244,7 +243,7 @@ struct sde_encoder_virt {

	struct dentry *debugfs_root;
	struct mutex enc_lock;
	DECLARE_BITMAP(frame_busy_mask, MAX_PHYS_ENCODERS_PER_VIRTUAL);
	atomic_t frame_done_cnt[MAX_PHYS_ENCODERS_PER_VIRTUAL];
	void (*crtc_frame_event_cb)(void *data, u32 event);
	struct sde_crtc_frame_event_cb_data crtc_frame_event_cb_data;

@@ -2239,6 +2238,17 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
			return -EINVAL;
		}

		/*
		 * schedule off work item only when there are no
		 * frames pending
		 */
		if (sde_crtc_frame_pending(sde_enc->crtc) > 1) {
			SDE_DEBUG_ENC(sde_enc, "skip schedule work");
			SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_EVTLOG_FUNC_CASE2);
			return 0;
		}

		if (sde_enc->crtc->index >= ARRAY_SIZE(priv->disp_thread)) {
			SDE_ERROR("invalid crtc index :%u\n",
					sde_enc->crtc->index);
@@ -2260,17 +2270,6 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
			return -EINVAL;
		}

		/*
		 * schedule off work item only when there are no
		 * frames pending
		 */
		if (sde_crtc_frame_pending(sde_enc->crtc) > 1) {
			SDE_DEBUG_ENC(sde_enc, "skip schedule work");
			SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				SDE_EVTLOG_FUNC_CASE2);
			return 0;
		}

		/* schedule delayed off work if autorefresh is disabled */
		if (sde_enc->cur_master &&
			sde_enc->cur_master->ops.is_autorefresh_enabled)
@@ -2471,17 +2470,10 @@ static int sde_encoder_resource_control(struct drm_encoder *drm_enc,
					SDE_EVTLOG_ERROR);
			mutex_unlock(&sde_enc->rc_lock);
			return 0;
		}

		/*
		 * if we are in ON but a frame was just kicked off,
		 * ignore the IDLE event, it's probably a stale timer event
		 */
		if (sde_enc->frame_busy_mask[0]) {
			SDE_ERROR_ENC(sde_enc,
					"sw_event:%d, rc:%d frame pending\n",
					sw_event, sde_enc->rc_state);
		} else if (sde_crtc_frame_pending(sde_enc->crtc) > 1) {
			SDE_DEBUG_ENC(sde_enc, "skip idle entry");
			SDE_EVT32(DRMID(drm_enc), sw_event, sde_enc->rc_state,
				sde_crtc_frame_pending(sde_enc->crtc),
				SDE_EVTLOG_ERROR);
			mutex_unlock(&sde_enc->rc_lock);
			return 0;
@@ -3344,6 +3336,8 @@ static void sde_encoder_frame_done_callback(
{
	struct sde_encoder_virt *sde_enc = to_sde_encoder_virt(drm_enc);
	unsigned int i;
	bool trigger = true;
	enum sde_rm_topology_name topology = SDE_RM_TOPOLOGY_NONE;

	if (!drm_enc || !sde_enc->cur_master) {
		SDE_ERROR("invalid param: drm_enc %x, cur_master %x\n",
@@ -3358,25 +3352,34 @@ static void sde_encoder_frame_done_callback(
			| SDE_ENCODER_FRAME_EVENT_ERROR
			| SDE_ENCODER_FRAME_EVENT_PANEL_DEAD)) {

		if (!sde_enc->frame_busy_mask[0]) {
			/**
			 * suppress frame_done without waiter,
			 * likely autorefresh
			 */
			SDE_EVT32(DRMID(drm_enc), event, ready_phys->intf_idx);
			return;
		}
		if (ready_phys->connector)
			topology = sde_connector_get_topology_name(
							ready_phys->connector);

		/* One of the physical encoders has become idle */
		for (i = 0; i < sde_enc->num_phys_encs; i++) {
			if (sde_enc->phys_encs[i] == ready_phys) {
				clear_bit(i, sde_enc->frame_busy_mask);
			if ((sde_enc->phys_encs[i] == ready_phys) ||
			     (event & SDE_ENCODER_FRAME_EVENT_ERROR)) {
				SDE_EVT32_VERBOSE(DRMID(drm_enc), i,
					sde_enc->frame_busy_mask[0]);
				     atomic_read(&sde_enc->frame_done_cnt[i]));
				if (!atomic_add_unless(
					&sde_enc->frame_done_cnt[i], 1, 1)) {
					SDE_EVT32(DRMID(drm_enc), event,
						ready_phys->intf_idx,
						SDE_EVTLOG_ERROR);
					SDE_ERROR_ENC(sde_enc,
						"intf idx:%d, event:%d\n",
						ready_phys->intf_idx, event);
					return;
				}
			}

		if (!sde_enc->frame_busy_mask[0]) {
			if (topology != SDE_RM_TOPOLOGY_PPSPLIT &&
			    atomic_read(&sde_enc->frame_done_cnt[i]) != 1)
				trigger = false;
		}

		if (trigger) {
			sde_encoder_resource_control(drm_enc,
					SDE_ENC_RC_EVENT_FRAME_DONE);

@@ -3384,6 +3387,8 @@ static void sde_encoder_frame_done_callback(
				sde_enc->crtc_frame_event_cb(
					&sde_enc->crtc_frame_event_cb_data,
					event);
			for (i = 0; i < sde_enc->num_phys_encs; i++)
				atomic_set(&sde_enc->frame_done_cnt[i], 0);
		}
	} else {
		if (sde_enc->crtc_frame_event_cb)
@@ -3700,14 +3705,6 @@ static void _sde_encoder_kickoff_phys(struct sde_encoder_virt *sde_enc)
		if (phys->connector)
			topology = sde_connector_get_topology_name(
					phys->connector);
		/*
		 * 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_SKIP)
			set_bit(i, sde_enc->frame_busy_mask);

		if (!phys->ops.needs_single_flush ||
				!phys->ops.needs_single_flush(phys)) {
@@ -5082,6 +5079,8 @@ struct drm_encoder *sde_encoder_init(
	sde_enc->cur_master = NULL;
	spin_lock_init(&sde_enc->enc_spinlock);
	mutex_init(&sde_enc->vblank_ctl_lock);
	for (i = 0; i < MAX_PHYS_ENCODERS_PER_VIRTUAL; i++)
		atomic_set(&sde_enc->frame_done_cnt[i], 0);
	drm_enc = &sde_enc->base;
	drm_encoder_init(dev, drm_enc, &sde_encoder_funcs, drm_enc_mode, NULL);
	drm_encoder_helper_add(drm_enc, &sde_encoder_helper_funcs);