[Intel-gfx] [PATCH 3/4] drm/i915: Enable asynchronous nuclear flips

Matt Roper matthew.d.roper at intel.com
Sat Jan 31 12:07:56 PST 2015


On Sat, Jan 31, 2015 at 10:30:29AM +0100, Daniel Vetter wrote:
> On Fri, Jan 30, 2015 at 04:22:38PM -0800, Matt Roper wrote:
> > The initial i915 nuclear pageflip support rejected asynchronous updates.
> > Allow all work after we swap in the new state structures to run
> > asynchronously.  We also need to start sending completion events to
> > userspace if they were requested.
> > 
> > Signed-off-by: Matt Roper <matthew.d.roper at intel.com>
> > ---
> >  drivers/gpu/drm/i915/i915_drv.h     |   3 +
> >  drivers/gpu/drm/i915/intel_atomic.c | 162 +++++++++++++++++++++++++++++++-----
> >  drivers/gpu/drm/i915/intel_drv.h    |   8 ++
> >  3 files changed, 150 insertions(+), 23 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
> > index 8fad702..c7a520a 100644
> > --- a/drivers/gpu/drm/i915/i915_drv.h
> > +++ b/drivers/gpu/drm/i915/i915_drv.h
> > @@ -1777,6 +1777,9 @@ struct drm_i915_private {
> >  	struct drm_crtc *pipe_to_crtc_mapping[I915_MAX_PIPES];
> >  	wait_queue_head_t pending_flip_queue;
> >  
> > +	/* CRTC mask of pending atomic flips */
> > +	uint32_t pending_atomic;
> > +
> >  #ifdef CONFIG_DEBUG_FS
> >  	struct intel_pipe_crc pipe_crc[I915_MAX_PIPES];
> >  #endif
> > diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
> > index 19a9dd5..5dd7897 100644
> > --- a/drivers/gpu/drm/i915/intel_atomic.c
> > +++ b/drivers/gpu/drm/i915/intel_atomic.c
> > @@ -76,6 +76,8 @@ int intel_atomic_check(struct drm_device *dev,
> >  	state->allow_modeset = false;
> >  	for (i = 0; i < ncrtcs; i++) {
> >  		struct intel_crtc *crtc = to_intel_crtc(state->crtcs[i]);
> > +		if (crtc)
> > +			state->crtc_states[i]->enable = crtc->active;
> >  		if (crtc && crtc->pipe != nuclear_pipe)
> >  			not_nuclear = true;
> >  	}
> > @@ -96,6 +98,87 @@ int intel_atomic_check(struct drm_device *dev,
> >  }
> >  
> >  
> > +/*
> > + * Wait until CRTC's have no pending flip, then atomically mark those CRTC's
> > + * as busy.
> > + */
> > +static int wait_for_pending_flip(uint32_t crtc_mask,
> > +				 struct intel_pending_atomic *commit)
> > +{
> > +	struct drm_i915_private *dev_priv = commit->dev->dev_private;
> > +	int ret;
> > +
> > +	spin_lock_irq(&dev_priv->pending_flip_queue.lock);
> > +	ret = wait_event_interruptible_locked(dev_priv->pending_flip_queue,
> > +					      !(dev_priv->pending_atomic & crtc_mask));
> > +	if (ret == 0)
> > +		dev_priv->pending_atomic |= crtc_mask;
> > +	spin_unlock_irq(&dev_priv->pending_flip_queue.lock);
> > +
> > +	return ret;
> > +}
> > +
> > +/* Finish pending flip operation on specified CRTC's */
> > +static void flip_completion(struct intel_pending_atomic *commit)
> > +{
> > +	struct drm_i915_private *dev_priv = commit->dev->dev_private;
> > +
> > +	spin_lock_irq(&dev_priv->pending_flip_queue.lock);
> > +	dev_priv->pending_atomic &= ~commit->crtc_mask;
> > +	wake_up_all_locked(&dev_priv->pending_flip_queue);
> > +	spin_unlock_irq(&dev_priv->pending_flip_queue.lock);
> > +}
> > +
> > +/*
> > + * Finish an atomic commit.  The work here can be performed asynchronously
> > + * if desired.  The new state has already been applied to the DRM objects
> > + * and no modeset locks are needed.
> > + */
> > +static void finish_atomic_commit(struct work_struct *work)
> > +{
> > +	struct intel_pending_atomic *commit =
> > +		container_of(work, struct intel_pending_atomic, work);
> > +	struct drm_device *dev = commit->dev;
> > +	struct drm_crtc *crtc;
> > +	struct drm_atomic_state *state = commit->state;
> > +	int i;
> > +
> > +	/*
> > +	 * FIXME:  The proper sequence here will eventually be:
> > +	 *
> > +	 * drm_atomic_helper_commit_pre_planes(dev, state);
> > +	 * drm_atomic_helper_commit_planes(dev, state);
> > +	 * drm_atomic_helper_commit_post_planes(dev, state);
> > +	 * drm_atomic_helper_wait_for_vblanks(dev, state);
> > +	 * drm_atomic_helper_cleanup_planes(dev, state);
> > +	 * drm_atomic_state_free(state);
> > +	 *
> > +	 * once we have full atomic modeset.  For now, just manually update
> > +	 * plane states to avoid clobbering good states with dummy states
> > +	 * while nuclear pageflipping.
> > +	 */
> > +	drm_atomic_helper_commit_planes(dev, state);
> > +	drm_atomic_helper_wait_for_vblanks(dev, state);
> > +
> > +	/* Send CRTC completion events. */
> > +	for (i = 0; i < dev->mode_config.num_crtc; i++) {
> > +		crtc = state->crtcs[i];
> > +		if (crtc && crtc->state->event) {
> > +			spin_lock_irq(&dev->event_lock);
> > +			drm_send_vblank_event(dev, to_intel_crtc(crtc)->pipe,
> > +					      crtc->state->event);
> > +			spin_unlock_irq(&dev->event_lock);
> > +			crtc->state->event = NULL;
> > +		}
> > +	}
> > +
> > +	drm_atomic_helper_cleanup_planes(dev, state);
> > +	drm_atomic_state_free(state);
> > +
> > +	flip_completion(commit);
> > +	kfree(commit);
> > +}
> > +
> >  /**
> >   * intel_atomic_commit - commit validated state object
> >   * @dev: DRM device
> > @@ -116,34 +199,48 @@ int intel_atomic_commit(struct drm_device *dev,
> >  			struct drm_atomic_state *state,
> >  			bool async)
> >  {
> > +	struct intel_pending_atomic *commit;
> >  	int ret;
> >  	int i;
> >  
> > -	if (async) {
> > -		DRM_DEBUG_KMS("i915 does not yet support async commit\n");
> > -		return -EINVAL;
> > -	}
> > -
> >  	ret = drm_atomic_helper_prepare_planes(dev, state);
> >  	if (ret)
> >  		return ret;
> >  
> > -	/* Point of no return */
> > +	commit = kzalloc(sizeof(*commit), GFP_KERNEL);
> > +	if (!commit)
> > +		return -ENOMEM;
> > +
> > +	commit->dev = dev;
> > +	commit->state = state;
> > +
> > +	for (i = 0; i < dev->mode_config.num_crtc; i++)
> > +		if (state->crtcs[i])
> > +			commit->crtc_mask |=
> > +				(1 << drm_crtc_index(state->crtcs[i]));
> >  
> >  	/*
> > -	 * FIXME:  The proper sequence here will eventually be:
> > -	 *
> > -	 * drm_atomic_helper_swap_state(dev, state)
> > -	 * drm_atomic_helper_commit_pre_planes(dev, state);
> > -	 * drm_atomic_helper_commit_planes(dev, state);
> > -	 * drm_atomic_helper_commit_post_planes(dev, state);
> > -	 * drm_atomic_helper_wait_for_vblanks(dev, state);
> > -	 * drm_atomic_helper_cleanup_planes(dev, state);
> > -	 * drm_atomic_state_free(state);
> > -	 *
> > -	 * once we have full atomic modeset.  For now, just manually update
> > -	 * plane states to avoid clobbering good states with dummy states
> > -	 * while nuclear pageflipping.
> > +	 * If there's already a flip pending, we won't schedule another one
> > +	 * until it completes.  We may relax this in the future, but for now
> > +	 * this matches the legacy pageflip behavior.
> > +	 */
> > +	ret = wait_for_pending_flip(commit->crtc_mask, commit);
> > +	if (ret) {
> > +		kfree(commit);
> > +		return ret;
> > +	}
> > +
> > +	/*
> > +	 * Point of no return; everything from here on can't fail, so swap
> > +	 * the new state into the DRM objects.
> > +	 */
> > +
> > +	/*
> > +	 * FIXME:  Should eventually use drm_atomic_helper_swap_state().
> > +	 * However since we only handle planes at the moment, we don't
> > +	 * want to clobber our good crtc state with something bogus,
> > +	 * so just manually swap the plane states and copy over any completion
> > +	 * events for CRTC's.
> >  	 */
> >  	for (i = 0; i < dev->mode_config.num_total_plane; i++) {
> >  		struct drm_plane *plane = state->planes[i];
> > @@ -155,10 +252,29 @@ int intel_atomic_commit(struct drm_device *dev,
> >  		swap(state->plane_states[i], plane->state);
> >  		plane->state->state = NULL;
> >  	}
> > -	drm_atomic_helper_commit_planes(dev, state);
> > -	drm_atomic_helper_wait_for_vblanks(dev, state);
> > -	drm_atomic_helper_cleanup_planes(dev, state);
> > -	drm_atomic_state_free(state);
> > +
> > +	for (i = 0; i < dev->mode_config.num_crtc; i++) {
> > +		struct drm_crtc *crtc = state->crtcs[i];
> > +		struct drm_crtc_state *crtc_state = state->crtc_states[i];
> > +
> > +		if (!crtc || !crtc_state)
> > +			continue;
> > +
> > +		/* n.b., we only copy the event and enabled flag for now */
> > +		swap(crtc->state->event, crtc_state->event);
> > +		swap(crtc->state->enable, crtc_state->enable);
> > +	}
> 
> Before we can swap in the new state we need to stall for any oustanding
> async flip to complete. At least until we have a full-blown flip queue.

I think the wait_for_pending_flip() call above the two comment blocks
before the loop should take care of that --- it waits until all the
CRTC's in commit->crtc_mask have no pending flip, then atomically marks
them as busy before proceeding.


Matt



More information about the Intel-gfx mailing list