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

Commit 8bfd3d1f authored by Ingrid Gallardo's avatar Ingrid Gallardo Committed by Gerrit - the friendly Code Review server
Browse files

drm/msm/sde: wait for vsync before rsc amc mode



When driver switch the rsc state from clock
mode to amc mode, a race condition can happen
where driver doesn't wait for the vsync before
the switch to amc mode happens.
This change, adds a refcount to make sure rsc
waits for the vsync before the mode switch.

Change-Id: I8db60afebc8bf91d56a1b71e6643d0db70230a5c
Signed-off-by: default avatarIngrid Gallardo <ingridg@codeaurora.org>
parent 223afe42
Loading
Loading
Loading
Loading
+12 −0
Original line number Diff line number Diff line
@@ -1511,6 +1511,7 @@ static int _sde_encoder_update_rsc_client(
	struct drm_crtc *primary_crtc;
	int pipe = -1;
	int rc = 0;
	int wait_refcount;

	if (!drm_enc || !drm_enc->dev) {
		SDE_ERROR("invalid encoder arguments\n");
@@ -1598,6 +1599,12 @@ static int _sde_encoder_update_rsc_client(
		return ret;
	}

	if (wait_vblank_crtc_id)
		wait_refcount =
			sde_rsc_client_get_vsync_refcount(sde_enc->rsc_client);
	SDE_EVT32_VERBOSE(DRMID(drm_enc), wait_vblank_crtc_id, wait_refcount,
			SDE_EVTLOG_FUNC_ENTRY);

	if (crtc->base.id != wait_vblank_crtc_id) {
		primary_crtc = drm_crtc_find(drm_enc->dev, wait_vblank_crtc_id);
		if (!primary_crtc) {
@@ -1635,6 +1642,11 @@ static int _sde_encoder_update_rsc_client(
		SDE_EVT32(DRMID(drm_enc), wait_vblank_crtc_id, wait_count,
				SDE_EVTLOG_ERROR);

	if (wait_refcount)
		sde_rsc_client_reset_vsync_refcount(sde_enc->rsc_client);
	SDE_EVT32_VERBOSE(DRMID(drm_enc), wait_vblank_crtc_id, wait_refcount,
			SDE_EVTLOG_FUNC_EXIT);

	return ret;
}

+94 −0
Original line number Diff line number Diff line
@@ -561,6 +561,23 @@ static int sde_rsc_switch_to_clk(struct sde_rsc_priv *rsc,
			msleep(PRIMARY_VBLANK_WORST_CASE_MS);
		} else {
			*wait_vblank_crtc_id = rsc->primary_client->crtc_id;

			/* increase refcount, so we wait for the next vsync */
			atomic_inc(&rsc->rsc_vsync_wait);
			SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait));
		}
	} else if (atomic_read(&rsc->rsc_vsync_wait)) {
		SDE_EVT32(rsc->primary_client, rsc->current_state,
			atomic_read(&rsc->rsc_vsync_wait));

		/* Wait for the vsync, if the refcount is set */
		rc = wait_event_timeout(rsc->rsc_vsync_waitq,
			atomic_read(&rsc->rsc_vsync_wait) == 0,
			msecs_to_jiffies(PRIMARY_VBLANK_WORST_CASE_MS*2));
		if (!rc) {
			pr_err("Timeout waiting for vsync\n");
			SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait),
				SDE_EVTLOG_ERROR);
		}
	}
end:
@@ -600,6 +617,23 @@ static int sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
			msleep(PRIMARY_VBLANK_WORST_CASE_MS);
		} else {
			*wait_vblank_crtc_id = rsc->primary_client->crtc_id;

			/* increase refcount, so we wait for the next vsync */
			atomic_inc(&rsc->rsc_vsync_wait);
			SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait));
		}
	} else if (atomic_read(&rsc->rsc_vsync_wait)) {
		SDE_EVT32(rsc->primary_client, rsc->current_state,
			atomic_read(&rsc->rsc_vsync_wait));

		/* Wait for the vsync, if the refcount is set */
		rc = wait_event_timeout(rsc->rsc_vsync_waitq,
			atomic_read(&rsc->rsc_vsync_wait) == 0,
			msecs_to_jiffies(PRIMARY_VBLANK_WORST_CASE_MS*2));
		if (!rc) {
			pr_err("Timeout waiting for vsync\n");
			SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait),
				SDE_EVTLOG_ERROR);
		}
	}

@@ -607,6 +641,65 @@ static int sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
	return rc;
}

/**
 * sde_rsc_client_get_vsync_refcount() - returns the status of the vsync
 * refcount, to signal if the client needs to reset the refcounting logic
 * @client:	 Client pointer provided by sde_rsc_client_create().
 *
 * Return: value of the vsync refcount.
 */
int sde_rsc_client_get_vsync_refcount(
		struct sde_rsc_client *caller_client)
{
	struct sde_rsc_priv *rsc;

	if (!caller_client) {
		pr_err("invalid client for rsc state update\n");
		return -EINVAL;
	} else if (caller_client->rsc_index >= MAX_RSC_COUNT) {
		pr_err("invalid rsc index\n");
		return -EINVAL;
	}

	rsc = rsc_prv_list[caller_client->rsc_index];
	if (!rsc)
		return 0;

	return atomic_read(&rsc->rsc_vsync_wait);
}

/**
 * sde_rsc_client_reset_vsync_refcount() - reduces the refcounting
 * logic that waits for the vsync.
 * @client:	 Client pointer provided by sde_rsc_client_create().
 *
 * Return: zero if refcount was already zero.
 */
int sde_rsc_client_reset_vsync_refcount(
		struct sde_rsc_client *caller_client)
{
	struct sde_rsc_priv *rsc;
	int ret;

	if (!caller_client) {
		pr_err("invalid client for rsc state update\n");
		return -EINVAL;
	} else if (caller_client->rsc_index >= MAX_RSC_COUNT) {
		pr_err("invalid rsc index\n");
		return -EINVAL;
	}

	rsc = rsc_prv_list[caller_client->rsc_index];
	if (!rsc)
		return 0;

	ret = atomic_add_unless(&rsc->rsc_vsync_wait, -1, 0);
	wake_up_all(&rsc->rsc_vsync_waitq);
	SDE_EVT32(atomic_read(&rsc->rsc_vsync_wait));

	return ret;
}

/**
 * sde_rsc_client_is_state_update_complete() - check if state update is complete
 * RSC state transition is not complete until HW receives VBLANK signal. This
@@ -1307,6 +1400,7 @@ static int sde_rsc_probe(struct platform_device *pdev)
	INIT_LIST_HEAD(&rsc->client_list);
	INIT_LIST_HEAD(&rsc->event_list);
	mutex_init(&rsc->client_lock);
	init_waitqueue_head(&rsc->rsc_vsync_waitq);

	pr_info("sde rsc index:%d probed successfully\n",
				SDE_RSC_INDEX + counter);
+4 −0
Original line number Diff line number Diff line
@@ -149,6 +149,8 @@ struct sde_rsc_timer_config {
 *			and ab/ib vote on display rsc
 * master_drm:		Primary client waits for vsync on this drm object based
 *			on crtc id
 * rsc_vsync_wait:   Refcount to indicate if we have to wait for the vsync.
 * rsc_vsync_waitq:   Queue to wait for the vsync.
 */
struct sde_rsc_priv {
	u32 version;
@@ -177,6 +179,8 @@ struct sde_rsc_priv {
	struct sde_rsc_client *primary_client;

	struct drm_device *master_drm;
	atomic_t rsc_vsync_wait;
	wait_queue_head_t rsc_vsync_waitq;
};

/**
+32 −0
Original line number Diff line number Diff line
@@ -185,6 +185,26 @@ int sde_rsc_client_state_update(struct sde_rsc_client *client,
	struct sde_rsc_cmd_config *config, int crtc_id,
	int *wait_vblank_crtc_id);

/**
 * sde_rsc_client_get_vsync_refcount() - returns the status of the vsync
 * refcount, to signal if the client needs to reset the refcounting logic
 * @client:	 Client pointer provided by sde_rsc_client_create().
 *
 * Return: true if the state update has completed.
 */
int sde_rsc_client_get_vsync_refcount(
		struct sde_rsc_client *caller_client);

/**
 * sde_rsc_client_reset_vsync_refcount() - reduces the refcounting
 * logic that waits for the vsync.
 * @client:	 Client pointer provided by sde_rsc_client_create().
 *
 * Return: true if the state update has completed.
 */
int sde_rsc_client_reset_vsync_refcount(
		struct sde_rsc_client *caller_client);

/**
 * sde_rsc_client_is_state_update_complete() - check if state update is complete
 * RSC state transition is not complete until HW receives VBLANK signal. This
@@ -265,6 +285,18 @@ static inline int sde_rsc_client_state_update(struct sde_rsc_client *client,
	return 0;
}

int sde_rsc_client_get_vsync_refcount(
		struct sde_rsc_client *caller_client)
{
	return 0;
}

int sde_rsc_client_reset_vsync_refcount(
		struct sde_rsc_client *caller_client)
{
	return 0;
}

static inline bool sde_rsc_client_is_state_update_complete(
		struct sde_rsc_client *caller_client)
{