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

Commit f86afecf authored by Rob Clark's avatar Rob Clark
Browse files

drm/msm: block incoming update on pending updates



We can't have multiple updates pending on a given CRTC, and we don't
want a sync update to race w/ an async update that preceeded it.  So
keep track of which CRTCs have updates in flight, and block later
updates that would conflict.

Signed-off-by: default avatarRob Clark <robdclark@gmail.com>
parent 5acb07ea
Loading
Loading
Loading
Loading
+1 −10
Original line number Diff line number Diff line
@@ -331,17 +331,8 @@ static int mdp4_crtc_atomic_check(struct drm_crtc *crtc,
		struct drm_crtc_state *state)
{
	struct mdp4_crtc *mdp4_crtc = to_mdp4_crtc(crtc);
	struct drm_device *dev = crtc->dev;

	DBG("%s: check", mdp4_crtc->name);

	if (mdp4_crtc->event) {
		dev_err(dev->dev, "already pending flip!\n");
		return -EBUSY;
	}

	// TODO anything else to check?

	return 0;
}

@@ -357,7 +348,7 @@ static void mdp4_crtc_atomic_flush(struct drm_crtc *crtc)
	struct drm_device *dev = crtc->dev;
	unsigned long flags;

	DBG("%s: flush", mdp4_crtc->name);
	DBG("%s: event: %p", mdp4_crtc->name, crtc->state->event);

	WARN_ON(mdp4_crtc->event);

+1 −6
Original line number Diff line number Diff line
@@ -303,11 +303,6 @@ static int mdp5_crtc_atomic_check(struct drm_crtc *crtc,

	DBG("%s: check", mdp5_crtc->name);

	if (mdp5_crtc->event) {
		dev_err(dev->dev, "already pending flip!\n");
		return -EBUSY;
	}

	/* request a free CTL, if none is already allocated for this CRTC */
	if (state->enable && !mdp5_crtc->ctl) {
		mdp5_crtc->ctl = mdp5_ctlm_request(mdp5_kms->ctlm, crtc);
@@ -364,7 +359,7 @@ static void mdp5_crtc_atomic_flush(struct drm_crtc *crtc)
	struct drm_device *dev = crtc->dev;
	unsigned long flags;

	DBG("%s: flush", mdp5_crtc->name);
	DBG("%s: event: %p", mdp5_crtc->name, crtc->state->event);

	WARN_ON(mdp5_crtc->event);

+68 −1
Original line number Diff line number Diff line
@@ -23,10 +23,41 @@ struct msm_commit {
	struct drm_atomic_state *state;
	uint32_t fence;
	struct msm_fence_cb fence_cb;
	uint32_t crtc_mask;
};

static void fence_cb(struct msm_fence_cb *cb);

/* block until specified crtcs are no longer pending update, and
 * atomically mark them as pending update
 */
static int start_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
{
	int ret;

	spin_lock(&priv->pending_crtcs_event.lock);
	ret = wait_event_interruptible_locked(priv->pending_crtcs_event,
			!(priv->pending_crtcs & crtc_mask));
	if (ret == 0) {
		DBG("start: %08x", crtc_mask);
		priv->pending_crtcs |= crtc_mask;
	}
	spin_unlock(&priv->pending_crtcs_event.lock);

	return ret;
}

/* clear specified crtcs (no longer pending update)
 */
static void end_atomic(struct msm_drm_private *priv, uint32_t crtc_mask)
{
	spin_lock(&priv->pending_crtcs_event.lock);
	DBG("end: %08x", crtc_mask);
	priv->pending_crtcs &= ~crtc_mask;
	wake_up_all_locked(&priv->pending_crtcs_event);
	spin_unlock(&priv->pending_crtcs_event.lock);
}

static struct msm_commit *new_commit(struct drm_atomic_state *state)
{
	struct msm_commit *c = kzalloc(sizeof(*c), GFP_KERNEL);
@@ -58,12 +89,27 @@ static void complete_commit(struct msm_commit *c)

	drm_atomic_helper_commit_post_planes(dev, state);

	/* NOTE: _wait_for_vblanks() only waits for vblank on
	 * enabled CRTCs.  So we end up faulting when disabling
	 * due to (potentially) unref'ing the outgoing fb's
	 * before the vblank when the disable has latched.
	 *
	 * But if it did wait on disabled (or newly disabled)
	 * CRTCs, that would be racy (ie. we could have missed
	 * the irq.  We need some way to poll for pipe shut
	 * down.  Or just live with occasionally hitting the
	 * timeout in the CRTC disable path (which really should
	 * not be critical path)
	 */

	drm_atomic_helper_wait_for_vblanks(dev, state);

	drm_atomic_helper_cleanup_planes(dev, state);

	drm_atomic_state_free(state);

	end_atomic(dev->dev_private, c->crtc_mask);

	kfree(c);
}

@@ -97,8 +143,9 @@ static void add_fb(struct msm_commit *c, struct drm_framebuffer *fb)
int msm_atomic_commit(struct drm_device *dev,
		struct drm_atomic_state *state, bool async)
{
	struct msm_commit *c;
	int nplanes = dev->mode_config.num_total_plane;
	int ncrtcs = dev->mode_config.num_crtc;
	struct msm_commit *c;
	int i, ret;

	ret = drm_atomic_helper_prepare_planes(dev, state);
@@ -106,6 +153,18 @@ int msm_atomic_commit(struct drm_device *dev,
		return ret;

	c = new_commit(state);
	if (!c)
		return -ENOMEM;

	/*
	 * Figure out what crtcs we have:
	 */
	for (i = 0; i < ncrtcs; i++) {
		struct drm_crtc *crtc = state->crtcs[i];
		if (!crtc)
			continue;
		c->crtc_mask |= (1 << drm_crtc_index(crtc));
	}

	/*
	 * Figure out what fence to wait for:
@@ -121,6 +180,14 @@ int msm_atomic_commit(struct drm_device *dev,
			add_fb(c, new_state->fb);
	}

	/*
	 * Wait for pending updates on any of the same crtc's and then
	 * mark our set of crtc's as busy:
	 */
	ret = start_atomic(dev->dev_private, c->crtc_mask);
	if (ret)
		return ret;

	/*
	 * This is the point of no return - everything below never fails except
	 * when the hw goes bonghits. Which means we can commit the new state on
+1 −0
Original line number Diff line number Diff line
@@ -193,6 +193,7 @@ static int msm_load(struct drm_device *dev, unsigned long flags)

	priv->wq = alloc_ordered_workqueue("msm", 0);
	init_waitqueue_head(&priv->fence_event);
	init_waitqueue_head(&priv->pending_crtcs_event);

	INIT_LIST_HEAD(&priv->inactive_list);
	INIT_LIST_HEAD(&priv->fence_cbs);
+4 −0
Original line number Diff line number Diff line
@@ -96,6 +96,10 @@ struct msm_drm_private {
	/* callbacks deferred until bo is inactive: */
	struct list_head fence_cbs;

	/* crtcs pending async atomic updates: */
	uint32_t pending_crtcs;
	wait_queue_head_t pending_crtcs_event;

	/* registered MMUs: */
	unsigned int num_mmus;
	struct msm_mmu *mmus[NUM_DOMAINS];