Commit 8e7cb179 authored by Chris Wilson's avatar Chris Wilson
Browse files

drm/i915: Extract intel_frontbuffer active tracking



Move the active tracking for the frontbuffer operations out of the
i915_gem_object and into its own first class (refcounted) object. In the
process of detangling, we switch from low level request tracking to the
easier i915_active -- with the plan that this avoids any potential
atomic callbacks as the frontbuffer tracking wishes to sleep as it
flushes.

Signed-off-by: default avatarChris Wilson <chris@chris-wilson.co.uk>
Reviewed-by: default avatarMatthew Auld <matthew.auld@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20190816074635.26062-1-chris@chris-wilson.co.uk
parent e5dadff4
Loading
Loading
Loading
Loading
+0 −3
Original line number Diff line number Diff line
@@ -91,9 +91,6 @@ Frontbuffer Tracking
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_frontbuffer.c
   :internal:

.. kernel-doc:: drivers/gpu/drm/i915/i915_gem.c
   :functions: i915_gem_track_fb

Display FIFO Underrun Reporting
-------------------------------

+33 −37
Original line number Diff line number Diff line
@@ -3049,12 +3049,13 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
{
	struct drm_device *dev = crtc->base.dev;
	struct drm_i915_private *dev_priv = to_i915(dev);
	struct drm_i915_gem_object *obj = NULL;
	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
	struct drm_framebuffer *fb = &plane_config->fb->base;
	u32 base_aligned = round_down(plane_config->base, PAGE_SIZE);
	u32 size_aligned = round_up(plane_config->base + plane_config->size,
				    PAGE_SIZE);
	struct drm_i915_gem_object *obj;
	bool ret = false;

	size_aligned -= base_aligned;

@@ -3096,7 +3097,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
		break;
	default:
		MISSING_CASE(plane_config->tiling);
		return false;
		goto out;
	}

	mode_cmd.pixel_format = fb->format->format;
@@ -3108,16 +3109,15 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,

	if (intel_framebuffer_init(to_intel_framebuffer(fb), obj, &mode_cmd)) {
		DRM_DEBUG_KMS("intel fb init failed\n");
		goto out_unref_obj;
		goto out;
	}


	DRM_DEBUG_KMS("initial plane fb obj %p\n", obj);
	return true;

out_unref_obj:
	ret = true;
out:
	i915_gem_object_put(obj);
	return false;
	return ret;
}

static void
@@ -3174,6 +3174,12 @@ static void intel_plane_disable_noatomic(struct intel_crtc *crtc,
	intel_disable_plane(plane, crtc_state);
}

static struct intel_frontbuffer *
to_intel_frontbuffer(struct drm_framebuffer *fb)
{
	return fb ? to_intel_framebuffer(fb)->frontbuffer : NULL;
}

static void
intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
			     struct intel_initial_plane_config *plane_config)
@@ -3181,7 +3187,6 @@ intel_find_initial_plane_obj(struct intel_crtc *intel_crtc,
	struct drm_device *dev = intel_crtc->base.dev;
	struct drm_i915_private *dev_priv = to_i915(dev);
	struct drm_crtc *c;
	struct drm_i915_gem_object *obj;
	struct drm_plane *primary = intel_crtc->base.primary;
	struct drm_plane_state *plane_state = primary->state;
	struct intel_plane *intel_plane = to_intel_plane(primary);
@@ -3257,8 +3262,7 @@ valid_fb:
		return;
	}

	obj = intel_fb_obj(fb);
	intel_fb_obj_flush(obj, ORIGIN_DIRTYFB);
	intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_DIRTYFB);

	plane_state->src_x = 0;
	plane_state->src_y = 0;
@@ -3273,14 +3277,14 @@ valid_fb:
	intel_state->base.src = drm_plane_state_src(plane_state);
	intel_state->base.dst = drm_plane_state_dest(plane_state);

	if (i915_gem_object_is_tiled(obj))
	if (plane_config->tiling)
		dev_priv->preserve_bios_swizzle = true;

	plane_state->fb = fb;
	plane_state->crtc = &intel_crtc->base;

	atomic_or(to_intel_plane(primary)->frontbuffer_bit,
		  &obj->frontbuffer_bits);
		  &to_intel_frontbuffer(fb)->bits);
}

static int skl_max_plane_width(const struct drm_framebuffer *fb,
@@ -14132,8 +14136,8 @@ static void intel_atomic_track_fbs(struct intel_atomic_state *state)

	for_each_oldnew_intel_plane_in_state(state, plane, old_plane_state,
					     new_plane_state, i)
		i915_gem_track_fb(intel_fb_obj(old_plane_state->base.fb),
				  intel_fb_obj(new_plane_state->base.fb),
		intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->base.fb),
					to_intel_frontbuffer(new_plane_state->base.fb),
					plane->frontbuffer_bit);
}

@@ -14418,7 +14422,7 @@ intel_prepare_plane_fb(struct drm_plane *plane,
		return ret;

	fb_obj_bump_render_priority(obj);
	intel_fb_obj_flush(obj, ORIGIN_DIRTYFB);
	intel_frontbuffer_flush(obj->frontbuffer, ORIGIN_DIRTYFB);

	if (!new_state->fence) { /* implicit fencing */
		struct dma_fence *fence;
@@ -14681,13 +14685,12 @@ intel_legacy_cursor_update(struct drm_plane *plane,
			   struct drm_modeset_acquire_ctx *ctx)
{
	struct drm_i915_private *dev_priv = to_i915(crtc->dev);
	int ret;
	struct drm_plane_state *old_plane_state, *new_plane_state;
	struct intel_plane *intel_plane = to_intel_plane(plane);
	struct drm_framebuffer *old_fb;
	struct intel_crtc_state *crtc_state =
		to_intel_crtc_state(crtc->state);
	struct intel_crtc_state *new_crtc_state;
	int ret;

	/*
	 * When crtc is inactive or there is a modeset pending,
@@ -14755,10 +14758,9 @@ intel_legacy_cursor_update(struct drm_plane *plane,
	if (ret)
		goto out_unlock;

	intel_fb_obj_flush(intel_fb_obj(fb), ORIGIN_FLIP);

	old_fb = old_plane_state->fb;
	i915_gem_track_fb(intel_fb_obj(old_fb), intel_fb_obj(fb),
	intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_FLIP);
	intel_frontbuffer_track(to_intel_frontbuffer(old_plane_state->fb),
				to_intel_frontbuffer(fb),
				intel_plane->frontbuffer_bit);

	/* Swap plane state */
@@ -15540,15 +15542,9 @@ static void intel_setup_outputs(struct drm_i915_private *dev_priv)
static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
{
	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
	struct drm_i915_gem_object *obj = intel_fb_obj(fb);

	drm_framebuffer_cleanup(fb);

	i915_gem_object_lock(obj);
	WARN_ON(!obj->framebuffer_references--);
	i915_gem_object_unlock(obj);

	i915_gem_object_put(obj);
	intel_frontbuffer_put(intel_fb->frontbuffer);

	kfree(intel_fb);
}
@@ -15576,7 +15572,7 @@ static int intel_user_framebuffer_dirty(struct drm_framebuffer *fb,
	struct drm_i915_gem_object *obj = intel_fb_obj(fb);

	i915_gem_object_flush_if_display(obj);
	intel_fb_obj_flush(obj, ORIGIN_DIRTYFB);
	intel_frontbuffer_flush(to_intel_frontbuffer(fb), ORIGIN_DIRTYFB);

	return 0;
}
@@ -15598,8 +15594,11 @@ static int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
	int ret = -EINVAL;
	int i;

	intel_fb->frontbuffer = intel_frontbuffer_get(obj);
	if (!intel_fb->frontbuffer)
		return -ENOMEM;

	i915_gem_object_lock(obj);
	obj->framebuffer_references++;
	tiling = i915_gem_object_get_tiling(obj);
	stride = i915_gem_object_get_stride(obj);
	i915_gem_object_unlock(obj);
@@ -15716,9 +15715,7 @@ static int intel_framebuffer_init(struct intel_framebuffer *intel_fb,
	return 0;

err:
	i915_gem_object_lock(obj);
	obj->framebuffer_references--;
	i915_gem_object_unlock(obj);
	intel_frontbuffer_put(intel_fb->frontbuffer);
	return ret;
}

@@ -15736,7 +15733,6 @@ intel_user_framebuffer_create(struct drm_device *dev,
		return ERR_PTR(-ENOENT);

	fb = intel_framebuffer_create(obj, &mode_cmd);
	if (IS_ERR(fb))
	i915_gem_object_put(obj);

	return fb;
+1 −0
Original line number Diff line number Diff line
@@ -84,6 +84,7 @@ enum intel_broadcast_rgb {

struct intel_framebuffer {
	struct drm_framebuffer base;
	struct intel_frontbuffer *frontbuffer;
	struct intel_rotation_info rot_info;

	/* for each plane in the normal GTT view */
+16 −24
Original line number Diff line number Diff line
@@ -47,13 +47,14 @@
#include "intel_fbdev.h"
#include "intel_frontbuffer.h"

static void intel_fbdev_invalidate(struct intel_fbdev *ifbdev)
static struct intel_frontbuffer *to_frontbuffer(struct intel_fbdev *ifbdev)
{
	struct drm_i915_gem_object *obj = intel_fb_obj(&ifbdev->fb->base);
	unsigned int origin =
		ifbdev->vma_flags & PLANE_HAS_FENCE ? ORIGIN_GTT : ORIGIN_CPU;
	return ifbdev->fb->frontbuffer;
}

	intel_fb_obj_invalidate(obj, origin);
static void intel_fbdev_invalidate(struct intel_fbdev *ifbdev)
{
	intel_frontbuffer_invalidate(to_frontbuffer(ifbdev), ORIGIN_CPU);
}

static int intel_fbdev_set_par(struct fb_info *info)
@@ -120,7 +121,7 @@ static int intelfb_alloc(struct drm_fb_helper *helper,
	struct drm_i915_private *dev_priv = to_i915(dev);
	struct drm_mode_fb_cmd2 mode_cmd = {};
	struct drm_i915_gem_object *obj;
	int size, ret;
	int size;

	/* we don't do packed 24bpp */
	if (sizes->surface_bpp == 24)
@@ -147,24 +148,16 @@ static int intelfb_alloc(struct drm_fb_helper *helper,
		obj = i915_gem_object_create_shmem(dev_priv, size);
	if (IS_ERR(obj)) {
		DRM_ERROR("failed to allocate framebuffer\n");
		ret = PTR_ERR(obj);
		goto err;
		return PTR_ERR(obj);
	}

	fb = intel_framebuffer_create(obj, &mode_cmd);
	if (IS_ERR(fb)) {
		ret = PTR_ERR(fb);
		goto err_obj;
	}
	i915_gem_object_put(obj);
	if (IS_ERR(fb))
		return PTR_ERR(fb);

	ifbdev->fb = to_intel_framebuffer(fb);

	return 0;

err_obj:
	i915_gem_object_put(obj);
err:
	return ret;
}

static int intelfb_create(struct drm_fb_helper *helper,
@@ -180,7 +173,6 @@ static int intelfb_create(struct drm_fb_helper *helper,
	const struct i915_ggtt_view view = {
		.type = I915_GGTT_VIEW_NORMAL,
	};
	struct drm_framebuffer *fb;
	intel_wakeref_t wakeref;
	struct fb_info *info;
	struct i915_vma *vma;
@@ -226,8 +218,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
		goto out_unlock;
	}

	fb = &ifbdev->fb->base;
	intel_fb_obj_flush(intel_fb_obj(fb), ORIGIN_DIRTYFB);
	intel_frontbuffer_flush(to_frontbuffer(ifbdev), ORIGIN_DIRTYFB);

	info = drm_fb_helper_alloc_fbi(helper);
	if (IS_ERR(info)) {
@@ -236,7 +227,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
		goto out_unpin;
	}

	ifbdev->helper.fb = fb;
	ifbdev->helper.fb = &ifbdev->fb->base;

	info->fbops = &intelfb_ops;

@@ -263,13 +254,14 @@ static int intelfb_create(struct drm_fb_helper *helper,
	 * If the object is stolen however, it will be full of whatever
	 * garbage was left in there.
	 */
	if (intel_fb_obj(fb)->stolen && !prealloc)
	if (vma->obj->stolen && !prealloc)
		memset_io(info->screen_base, 0, info->screen_size);

	/* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */

	DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x\n",
		      fb->width, fb->height, i915_ggtt_offset(vma));
		      ifbdev->fb->base.width, ifbdev->fb->base.height,
		      i915_ggtt_offset(vma));
	ifbdev->vma = vma;
	ifbdev->vma_flags = flags;

+182 −73
Original line number Diff line number Diff line
@@ -30,11 +30,11 @@
 * Many features require us to track changes to the currently active
 * frontbuffer, especially rendering targeted at the frontbuffer.
 *
 * To be able to do so GEM tracks frontbuffers using a bitmask for all possible
 * frontbuffer slots through i915_gem_track_fb(). The function in this file are
 * then called when the contents of the frontbuffer are invalidated, when
 * frontbuffer rendering has stopped again to flush out all the changes and when
 * the frontbuffer is exchanged with a flip. Subsystems interested in
 * To be able to do so we track frontbuffers using a bitmask for all possible
 * frontbuffer slots through intel_frontbuffer_track(). The functions in this
 * file are then called when the contents of the frontbuffer are invalidated,
 * when frontbuffer rendering has stopped again to flush out all the changes
 * and when the frontbuffer is exchanged with a flip. Subsystems interested in
 * frontbuffer changes (e.g. PSR, FBC, DRRS) should directly put their callbacks
 * into the relevant places and filter for the frontbuffer slots that they are
 * interested int.
@@ -63,28 +63,9 @@
#include "intel_frontbuffer.h"
#include "intel_psr.h"

void __intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
			       enum fb_op_origin origin,
			       unsigned int frontbuffer_bits)
{
	struct drm_i915_private *dev_priv = to_i915(obj->base.dev);

	if (origin == ORIGIN_CS) {
		spin_lock(&dev_priv->fb_tracking.lock);
		dev_priv->fb_tracking.busy_bits |= frontbuffer_bits;
		dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits;
		spin_unlock(&dev_priv->fb_tracking.lock);
	}

	might_sleep();
	intel_psr_invalidate(dev_priv, frontbuffer_bits, origin);
	intel_edp_drrs_invalidate(dev_priv, frontbuffer_bits);
	intel_fbc_invalidate(dev_priv, frontbuffer_bits, origin);
}

/**
 * intel_frontbuffer_flush - flush frontbuffer
 * @dev_priv: i915 device
 * frontbuffer_flush - flush frontbuffer
 * @i915: i915 device
 * @frontbuffer_bits: frontbuffer plane tracking bits
 * @origin: which operation caused the flush
 *
@@ -94,45 +75,27 @@ void __intel_fb_obj_invalidate(struct drm_i915_gem_object *obj,
 *
 * Can be called without any locks held.
 */
static void intel_frontbuffer_flush(struct drm_i915_private *dev_priv,
				    unsigned frontbuffer_bits,
static void frontbuffer_flush(struct drm_i915_private *i915,
			      unsigned int frontbuffer_bits,
			      enum fb_op_origin origin)
{
	/* Delay flushing when rings are still busy.*/
	spin_lock(&dev_priv->fb_tracking.lock);
	frontbuffer_bits &= ~dev_priv->fb_tracking.busy_bits;
	spin_unlock(&dev_priv->fb_tracking.lock);
	spin_lock(&i915->fb_tracking.lock);
	frontbuffer_bits &= ~i915->fb_tracking.busy_bits;
	spin_unlock(&i915->fb_tracking.lock);

	if (!frontbuffer_bits)
		return;

	might_sleep();
	intel_edp_drrs_flush(dev_priv, frontbuffer_bits);
	intel_psr_flush(dev_priv, frontbuffer_bits, origin);
	intel_fbc_flush(dev_priv, frontbuffer_bits, origin);
}

void __intel_fb_obj_flush(struct drm_i915_gem_object *obj,
			  enum fb_op_origin origin,
			  unsigned int frontbuffer_bits)
{
	struct drm_i915_private *dev_priv = to_i915(obj->base.dev);

	if (origin == ORIGIN_CS) {
		spin_lock(&dev_priv->fb_tracking.lock);
		/* Filter out new bits since rendering started. */
		frontbuffer_bits &= dev_priv->fb_tracking.busy_bits;
		dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits;
		spin_unlock(&dev_priv->fb_tracking.lock);
	}

	if (frontbuffer_bits)
		intel_frontbuffer_flush(dev_priv, frontbuffer_bits, origin);
	intel_edp_drrs_flush(i915, frontbuffer_bits);
	intel_psr_flush(i915, frontbuffer_bits, origin);
	intel_fbc_flush(i915, frontbuffer_bits, origin);
}

/**
 * intel_frontbuffer_flip_prepare - prepare asynchronous frontbuffer flip
 * @dev_priv: i915 device
 * @i915: i915 device
 * @frontbuffer_bits: frontbuffer plane tracking bits
 *
 * This function gets called after scheduling a flip on @obj. The actual
@@ -142,19 +105,19 @@ void __intel_fb_obj_flush(struct drm_i915_gem_object *obj,
 *
 * Can be called without any locks held.
 */
void intel_frontbuffer_flip_prepare(struct drm_i915_private *dev_priv,
void intel_frontbuffer_flip_prepare(struct drm_i915_private *i915,
				    unsigned frontbuffer_bits)
{
	spin_lock(&dev_priv->fb_tracking.lock);
	dev_priv->fb_tracking.flip_bits |= frontbuffer_bits;
	spin_lock(&i915->fb_tracking.lock);
	i915->fb_tracking.flip_bits |= frontbuffer_bits;
	/* Remove stale busy bits due to the old buffer. */
	dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits;
	spin_unlock(&dev_priv->fb_tracking.lock);
	i915->fb_tracking.busy_bits &= ~frontbuffer_bits;
	spin_unlock(&i915->fb_tracking.lock);
}

/**
 * intel_frontbuffer_flip_complete - complete asynchronous frontbuffer flip
 * @dev_priv: i915 device
 * @i915: i915 device
 * @frontbuffer_bits: frontbuffer plane tracking bits
 *
 * This function gets called after the flip has been latched and will complete
@@ -162,23 +125,22 @@ void intel_frontbuffer_flip_prepare(struct drm_i915_private *dev_priv,
 *
 * Can be called without any locks held.
 */
void intel_frontbuffer_flip_complete(struct drm_i915_private *dev_priv,
void intel_frontbuffer_flip_complete(struct drm_i915_private *i915,
				     unsigned frontbuffer_bits)
{
	spin_lock(&dev_priv->fb_tracking.lock);
	spin_lock(&i915->fb_tracking.lock);
	/* Mask any cancelled flips. */
	frontbuffer_bits &= dev_priv->fb_tracking.flip_bits;
	dev_priv->fb_tracking.flip_bits &= ~frontbuffer_bits;
	spin_unlock(&dev_priv->fb_tracking.lock);
	frontbuffer_bits &= i915->fb_tracking.flip_bits;
	i915->fb_tracking.flip_bits &= ~frontbuffer_bits;
	spin_unlock(&i915->fb_tracking.lock);

	if (frontbuffer_bits)
		intel_frontbuffer_flush(dev_priv,
					frontbuffer_bits, ORIGIN_FLIP);
		frontbuffer_flush(i915, frontbuffer_bits, ORIGIN_FLIP);
}

/**
 * intel_frontbuffer_flip - synchronous frontbuffer flip
 * @dev_priv: i915 device
 * @i915: i915 device
 * @frontbuffer_bits: frontbuffer plane tracking bits
 *
 * This function gets called after scheduling a flip on @obj. This is for
@@ -187,13 +149,160 @@ void intel_frontbuffer_flip_complete(struct drm_i915_private *dev_priv,
 *
 * Can be called without any locks held.
 */
void intel_frontbuffer_flip(struct drm_i915_private *dev_priv,
void intel_frontbuffer_flip(struct drm_i915_private *i915,
			    unsigned frontbuffer_bits)
{
	spin_lock(&dev_priv->fb_tracking.lock);
	spin_lock(&i915->fb_tracking.lock);
	/* Remove stale busy bits due to the old buffer. */
	dev_priv->fb_tracking.busy_bits &= ~frontbuffer_bits;
	spin_unlock(&dev_priv->fb_tracking.lock);
	i915->fb_tracking.busy_bits &= ~frontbuffer_bits;
	spin_unlock(&i915->fb_tracking.lock);

	frontbuffer_flush(i915, frontbuffer_bits, ORIGIN_FLIP);
}

void __intel_fb_invalidate(struct intel_frontbuffer *front,
			   enum fb_op_origin origin,
			   unsigned int frontbuffer_bits)
{
	struct drm_i915_private *i915 = to_i915(front->obj->base.dev);

	if (origin == ORIGIN_CS) {
		spin_lock(&i915->fb_tracking.lock);
		i915->fb_tracking.busy_bits |= frontbuffer_bits;
		i915->fb_tracking.flip_bits &= ~frontbuffer_bits;
		spin_unlock(&i915->fb_tracking.lock);
	}

	might_sleep();
	intel_psr_invalidate(i915, frontbuffer_bits, origin);
	intel_edp_drrs_invalidate(i915, frontbuffer_bits);
	intel_fbc_invalidate(i915, frontbuffer_bits, origin);
}

void __intel_fb_flush(struct intel_frontbuffer *front,
		      enum fb_op_origin origin,
		      unsigned int frontbuffer_bits)
{
	struct drm_i915_private *i915 = to_i915(front->obj->base.dev);

	if (origin == ORIGIN_CS) {
		spin_lock(&i915->fb_tracking.lock);
		/* Filter out new bits since rendering started. */
		frontbuffer_bits &= i915->fb_tracking.busy_bits;
		i915->fb_tracking.busy_bits &= ~frontbuffer_bits;
		spin_unlock(&i915->fb_tracking.lock);
	}

	if (frontbuffer_bits)
		frontbuffer_flush(i915, frontbuffer_bits, origin);
}

static int frontbuffer_active(struct i915_active *ref)
{
	struct intel_frontbuffer *front =
		container_of(ref, typeof(*front), write);

	kref_get(&front->ref);
	return 0;
}

static void frontbuffer_retire(struct i915_active *ref)
{
	struct intel_frontbuffer *front =
		container_of(ref, typeof(*front), write);

	intel_frontbuffer_flush(front, ORIGIN_CS);
	intel_frontbuffer_put(front);
}

static void frontbuffer_release(struct kref *ref)
	__releases(&to_i915(front->obj->base.dev)->fb_tracking.lock)
{
	struct intel_frontbuffer *front =
		container_of(ref, typeof(*front), ref);

	front->obj->frontbuffer = NULL;
	spin_unlock(&to_i915(front->obj->base.dev)->fb_tracking.lock);

	i915_gem_object_put(front->obj);
	kfree(front);
}

struct intel_frontbuffer *
intel_frontbuffer_get(struct drm_i915_gem_object *obj)
{
	struct drm_i915_private *i915 = to_i915(obj->base.dev);
	struct intel_frontbuffer *front;

	spin_lock(&i915->fb_tracking.lock);
	front = obj->frontbuffer;
	if (front)
		kref_get(&front->ref);
	spin_unlock(&i915->fb_tracking.lock);
	if (front)
		return front;

	intel_frontbuffer_flush(dev_priv, frontbuffer_bits, ORIGIN_FLIP);
	front = kmalloc(sizeof(*front), GFP_KERNEL);
	if (!front)
		return NULL;

	front->obj = obj;
	kref_init(&front->ref);
	atomic_set(&front->bits, 0);
	i915_active_init(i915, &front->write,
			 frontbuffer_active, frontbuffer_retire);

	spin_lock(&i915->fb_tracking.lock);
	if (obj->frontbuffer) {
		kfree(front);
		front = obj->frontbuffer;
		kref_get(&front->ref);
	} else {
		i915_gem_object_get(obj);
		obj->frontbuffer = front;
	}
	spin_unlock(&i915->fb_tracking.lock);

	return front;
}

void intel_frontbuffer_put(struct intel_frontbuffer *front)
{
	kref_put_lock(&front->ref,
		      frontbuffer_release,
		      &to_i915(front->obj->base.dev)->fb_tracking.lock);
}

/**
 * intel_frontbuffer_track - update frontbuffer tracking
 * @old: current buffer for the frontbuffer slots
 * @new: new buffer for the frontbuffer slots
 * @frontbuffer_bits: bitmask of frontbuffer slots
 *
 * This updates the frontbuffer tracking bits @frontbuffer_bits by clearing them
 * from @old and setting them in @new. Both @old and @new can be NULL.
 */
void intel_frontbuffer_track(struct intel_frontbuffer *old,
			     struct intel_frontbuffer *new,
			     unsigned int frontbuffer_bits)
{
	/*
	 * Control of individual bits within the mask are guarded by
	 * the owning plane->mutex, i.e. we can never see concurrent
	 * manipulation of individual bits. But since the bitfield as a whole
	 * is updated using RMW, we need to use atomics in order to update
	 * the bits.
	 */
	BUILD_BUG_ON(INTEL_FRONTBUFFER_BITS_PER_PIPE * I915_MAX_PIPES >
		     BITS_PER_TYPE(atomic_t));

	if (old) {
		WARN_ON(!(atomic_read(&old->bits) & frontbuffer_bits));
		atomic_andnot(frontbuffer_bits, &old->bits);
	}

	if (new) {
		WARN_ON(atomic_read(&new->bits) & frontbuffer_bits);
		atomic_or(frontbuffer_bits, &new->bits);
	}
}
Loading