diff mbox

[8/8] drm/i915: remove early fb allocation dependency on CONFIG_FB v2

Message ID 1394211475-2646-8-git-send-email-jbarnes@virtuousgeek.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jesse Barnes March 7, 2014, 4:57 p.m. UTC
By stuffing the fb allocation into the crtc, we get mode set lifetime
refcounting for free, but have to handle the initial pin & fence
slightly differently.  It also means we can move the shared fb handling
into the core rather than leaving it out in the fbdev code.

v2: null out crtc->fb on error (Daniel)
    take fbdev fb ref and remove unused error path (Daniel)

Requested-by: Daniel Vetter <daniel@ffwll.ch>
Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
---
 drivers/gpu/drm/i915/intel_display.c | 145 ++++++++++++++++++++++++-----------
 drivers/gpu/drm/i915/intel_drv.h     |   1 -
 drivers/gpu/drm/i915/intel_fbdev.c   |  38 +--------
 3 files changed, 105 insertions(+), 79 deletions(-)

Comments

Daniel Vetter March 8, 2014, 10:33 a.m. UTC | #1
On Fri, Mar 07, 2014 at 08:57:55AM -0800, Jesse Barnes wrote:
> By stuffing the fb allocation into the crtc, we get mode set lifetime
> refcounting for free, but have to handle the initial pin & fence
> slightly differently.  It also means we can move the shared fb handling
> into the core rather than leaving it out in the fbdev code.
> 
> v2: null out crtc->fb on error (Daniel)
>     take fbdev fb ref and remove unused error path (Daniel)
> 
> Requested-by: Daniel Vetter <daniel@ffwll.ch>
> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>

Ok, I've merged patches 1-4 and this one here from the series. Not
terribly happy that you didn't squash in this fixup, since now people
might stumble over the strange lifetime rules of the previous patches. But
meh ;-)

Thanks, Daniel
> ---
>  drivers/gpu/drm/i915/intel_display.c | 145 ++++++++++++++++++++++++-----------
>  drivers/gpu/drm/i915/intel_drv.h     |   1 -
>  drivers/gpu/drm/i915/intel_fbdev.c   |  38 +--------
>  3 files changed, 105 insertions(+), 79 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
> index d450ab6..718cc73 100644
> --- a/drivers/gpu/drm/i915/intel_display.c
> +++ b/drivers/gpu/drm/i915/intel_display.c
> @@ -2068,7 +2068,7 @@ int intel_format_to_fourcc(int format)
>  	}
>  }
>  
> -static void intel_alloc_plane_obj(struct intel_crtc *crtc,
> +static bool intel_alloc_plane_obj(struct intel_crtc *crtc,
>  				  struct intel_plane_config *plane_config)
>  {
>  	struct drm_device *dev = crtc->base.dev;
> @@ -2076,38 +2076,76 @@ static void intel_alloc_plane_obj(struct intel_crtc *crtc,
>  	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
>  	u32 base = plane_config->base;
>  
> -	if (!plane_config->fb)
> -		return;
> -
>  	obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base,
>  							     plane_config->size);
>  	if (!obj)
> -		return;
> +		return false;
>  
>  	if (plane_config->tiled) {
>  		obj->tiling_mode = I915_TILING_X;
> -		obj->stride = plane_config->fb->base.pitches[0];
> +		obj->stride = crtc->base.fb->pitches[0];
>  	}
>  
> -	mode_cmd.pixel_format = plane_config->fb->base.pixel_format;
> -	mode_cmd.width = plane_config->fb->base.width;
> -	mode_cmd.height = plane_config->fb->base.height;
> -	mode_cmd.pitches[0] = plane_config->fb->base.pitches[0];
> +	mode_cmd.pixel_format = crtc->base.fb->pixel_format;
> +	mode_cmd.width = crtc->base.fb->width;
> +	mode_cmd.height = crtc->base.fb->height;
> +	mode_cmd.pitches[0] = crtc->base.fb->pitches[0];
>  
>  	mutex_lock(&dev->struct_mutex);
>  
> -	if (intel_framebuffer_init(dev, plane_config->fb, &mode_cmd, obj)) {
> +	if (intel_framebuffer_init(dev, to_intel_framebuffer(crtc->base.fb),
> +				   &mode_cmd, obj)) {
>  		DRM_DEBUG_KMS("intel fb init failed\n");
>  		goto out_unref_obj;
>  	}
>  
>  	mutex_unlock(&dev->struct_mutex);
> -	DRM_DEBUG_KMS("plane fb obj %p\n", plane_config->fb->obj);
> -	return;
> +
> +	DRM_DEBUG_KMS("plane fb obj %p\n", obj);
> +	return true;
>  
>  out_unref_obj:
>  	drm_gem_object_unreference(&obj->base);
>  	mutex_unlock(&dev->struct_mutex);
> +	return false;
> +}
> +
> +static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
> +				 struct intel_plane_config *plane_config)
> +{
> +	struct drm_device *dev = intel_crtc->base.dev;
> +	struct drm_crtc *c;
> +	struct intel_crtc *i;
> +	struct intel_framebuffer *fb;
> +
> +	if (!intel_crtc->base.fb)
> +		return;
> +
> +	if (intel_alloc_plane_obj(intel_crtc, plane_config))
> +		return;
> +
> +	kfree(intel_crtc->base.fb);
> +
> +	/*
> +	 * Failed to alloc the obj, check to see if we should share
> +	 * an fb with another CRTC instead
> +	 */
> +	list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
> +		i = to_intel_crtc(c);
> +
> +		if (c == &intel_crtc->base)
> +			continue;
> +
> +		if (!i->active || !c->fb)
> +			continue;
> +
> +		fb = to_intel_framebuffer(c->fb);
> +		if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) {
> +			drm_framebuffer_reference(c->fb);
> +			intel_crtc->base.fb = c->fb;
> +			break;
> +		}
> +	}
>  }
>  
>  static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
> @@ -5636,8 +5674,8 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc,
>  	int fourcc, pixel_format;
>  	int aligned_height;
>  
> -	plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL);
> -	if (!plane_config->fb) {
> +	crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
> +	if (!crtc->base.fb) {
>  		DRM_DEBUG_KMS("failed to alloc fb\n");
>  		return;
>  	}
> @@ -5650,8 +5688,8 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc,
>  
>  	pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
>  	fourcc = intel_format_to_fourcc(pixel_format);
> -	plane_config->fb->base.pixel_format = fourcc;
> -	plane_config->fb->base.bits_per_pixel =
> +	crtc->base.fb->pixel_format = fourcc;
> +	crtc->base.fb->bits_per_pixel =
>  		drm_format_plane_cpp(fourcc, 0) * 8;
>  
>  	if (INTEL_INFO(dev)->gen >= 4) {
> @@ -5666,23 +5704,23 @@ static void i9xx_get_plane_config(struct intel_crtc *crtc,
>  	plane_config->base = base;
>  
>  	val = I915_READ(PIPESRC(pipe));
> -	plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1;
> -	plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1;
> +	crtc->base.fb->width = ((val >> 16) & 0xfff) + 1;
> +	crtc->base.fb->height = ((val >> 0) & 0xfff) + 1;
>  
>  	val = I915_READ(DSPSTRIDE(pipe));
> -	plane_config->fb->base.pitches[0] = val & 0xffffff80;
> +	crtc->base.fb->pitches[0] = val & 0xffffff80;
>  
> -	aligned_height = intel_align_height(dev, plane_config->fb->base.height,
> +	aligned_height = intel_align_height(dev, crtc->base.fb->height,
>  					    plane_config->tiled);
>  
> -	plane_config->size = ALIGN(plane_config->fb->base.pitches[0] *
> +	plane_config->size = ALIGN(crtc->base.fb->pitches[0] *
>  				   aligned_height, PAGE_SIZE);
>  
>  	DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
> -		      pipe, plane, plane_config->fb->base.width,
> -		      plane_config->fb->base.height,
> -		      plane_config->fb->base.bits_per_pixel, base,
> -		      plane_config->fb->base.pitches[0],
> +		      pipe, plane, crtc->base.fb->width,
> +		      crtc->base.fb->height,
> +		      crtc->base.fb->bits_per_pixel, base,
> +		      crtc->base.fb->pitches[0],
>  		      plane_config->size);
>  
>  }
> @@ -6644,8 +6682,8 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc,
>  	int fourcc, pixel_format;
>  	int aligned_height;
>  
> -	plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL);
> -	if (!plane_config->fb) {
> +	crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
> +	if (!crtc->base.fb) {
>  		DRM_DEBUG_KMS("failed to alloc fb\n");
>  		return;
>  	}
> @@ -6658,8 +6696,8 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc,
>  
>  	pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
>  	fourcc = intel_format_to_fourcc(pixel_format);
> -	plane_config->fb->base.pixel_format = fourcc;
> -	plane_config->fb->base.bits_per_pixel =
> +	crtc->base.fb->pixel_format = fourcc;
> +	crtc->base.fb->bits_per_pixel =
>  		drm_format_plane_cpp(fourcc, 0) * 8;
>  
>  	base = I915_READ(DSPSURF(plane)) & 0xfffff000;
> @@ -6674,23 +6712,23 @@ static void ironlake_get_plane_config(struct intel_crtc *crtc,
>  	plane_config->base = base;
>  
>  	val = I915_READ(PIPESRC(pipe));
> -	plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1;
> -	plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1;
> +	crtc->base.fb->width = ((val >> 16) & 0xfff) + 1;
> +	crtc->base.fb->height = ((val >> 0) & 0xfff) + 1;
>  
>  	val = I915_READ(DSPSTRIDE(pipe));
> -	plane_config->fb->base.pitches[0] = val & 0xffffff80;
> +	crtc->base.fb->pitches[0] = val & 0xffffff80;
>  
> -	aligned_height = intel_align_height(dev, plane_config->fb->base.height,
> +	aligned_height = intel_align_height(dev, crtc->base.fb->height,
>  					    plane_config->tiled);
>  
> -	plane_config->size = ALIGN(plane_config->fb->base.pitches[0] *
> +	plane_config->size = ALIGN(crtc->base.fb->pitches[0] *
>  				   aligned_height, PAGE_SIZE);
>  
>  	DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
> -		      pipe, plane, plane_config->fb->base.width,
> -		      plane_config->fb->base.height,
> -		      plane_config->fb->base.bits_per_pixel, base,
> -		      plane_config->fb->base.pitches[0],
> +		      pipe, plane, crtc->base.fb->width,
> +		      crtc->base.fb->height,
> +		      crtc->base.fb->bits_per_pixel, base,
> +		      crtc->base.fb->pitches[0],
>  		      plane_config->size);
>  }
>  
> @@ -11258,10 +11296,7 @@ void intel_modeset_init(struct drm_device *dev)
>  		if (!crtc->active)
>  			continue;
>  
> -#if IS_ENABLED(CONFIG_FB)
>  		/*
> -		 * We don't have a good way of freeing the buffer w/o the FB
> -		 * layer owning it...
>  		 * Note that reserving the BIOS fb up front prevents us
>  		 * from stuffing other stolen allocations like the ring
>  		 * on top.  This prevents some ugliness at boot time, and
> @@ -11275,9 +11310,8 @@ void intel_modeset_init(struct drm_device *dev)
>  			 * If the fb is shared between multiple heads, we'll
>  			 * just get the first one.
>  			 */
> -			intel_alloc_plane_obj(crtc, &crtc->plane_config);
> +			intel_find_plane_obj(crtc, &crtc->plane_config);
>  		}
> -#endif
>  	}
>  }
>  
> @@ -11648,9 +11682,32 @@ void intel_modeset_setup_hw_state(struct drm_device *dev,
>  
>  void intel_modeset_gem_init(struct drm_device *dev)
>  {
> +	struct drm_crtc *c;
> +	struct intel_framebuffer *fb;
> +
>  	intel_modeset_init_hw(dev);
>  
>  	intel_setup_overlay(dev);
> +
> +	/*
> +	 * Make sure any fbs we allocated at startup are properly
> +	 * pinned & fenced.  When we do the allocation it's too early
> +	 * for this.
> +	 */
> +	mutex_lock(&dev->struct_mutex);
> +	list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
> +		if (!c->fb)
> +			continue;
> +
> +		fb = to_intel_framebuffer(c->fb);
> +		if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) {
> +			DRM_ERROR("failed to pin boot fb on pipe %d\n",
> +				  to_intel_crtc(c)->pipe);
> +			drm_framebuffer_unreference(c->fb);
> +			c->fb = NULL;
> +		}
> +	}
> +	mutex_unlock(&dev->struct_mutex);
>  }
>  
>  void intel_connector_unregister(struct intel_connector *intel_connector)
> diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
> index 3d404ab..2ed72cf 100644
> --- a/drivers/gpu/drm/i915/intel_drv.h
> +++ b/drivers/gpu/drm/i915/intel_drv.h
> @@ -220,7 +220,6 @@ typedef struct dpll {
>  } intel_clock_t;
>  
>  struct intel_plane_config {
> -	struct intel_framebuffer *fb; /* ends up managed by intel_fbdev.c */
>  	bool tiled;
>  	int size;
>  	u32 base;
> diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
> index 950469c..f81e3db 100644
> --- a/drivers/gpu/drm/i915/intel_fbdev.c
> +++ b/drivers/gpu/drm/i915/intel_fbdev.c
> @@ -480,7 +480,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
>  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
>  		intel_crtc = to_intel_crtc(crtc);
>  
> -		if (!intel_crtc->active || !intel_crtc->plane_config.fb) {
> +		if (!intel_crtc->active || !crtc->fb) {
>  			DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n",
>  				      pipe_name(intel_crtc->pipe));
>  			continue;
> @@ -490,7 +490,7 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
>  			DRM_DEBUG_KMS("found possible fb from plane %c\n",
>  				      pipe_name(intel_crtc->pipe));
>  			plane_config = &intel_crtc->plane_config;
> -			fb = plane_config->fb;
> +			fb = to_intel_framebuffer(crtc->fb);
>  			max_size = plane_config->size;
>  		}
>  	}
> @@ -542,43 +542,15 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
>  			      max_size, cur_size);
>  	}
>  
> -	/* Free unused fbs */
> -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> -		struct intel_framebuffer *cur_fb;
> -
> -		intel_crtc = to_intel_crtc(crtc);
> -		cur_fb = intel_crtc->plane_config.fb;
> -
> -		if (cur_fb && cur_fb != fb)
> -			drm_framebuffer_unreference(&cur_fb->base);
> -	}
> -
>  	if (!fb) {
>  		DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n");
>  		goto out;
>  	}
>  
> -	ifbdev->preferred_bpp = plane_config->fb->base.bits_per_pixel;
> +	ifbdev->preferred_bpp = fb->base.bits_per_pixel;
>  	ifbdev->fb = fb;
>  
> -	/* Assuming a single fb across all pipes here */
> -	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> -		intel_crtc = to_intel_crtc(crtc);
> -
> -		if (!intel_crtc->active)
> -			continue;
> -
> -		/*
> -		 * This should only fail on the first one so we don't need
> -		 * to cleanup any secondary crtc->fbs
> -		 */
> -		if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL))
> -			goto out_unref_obj;
> -
> -		crtc->fb = &fb->base;
> -		drm_gem_object_reference(&fb->obj->base);
> -		drm_framebuffer_reference(&fb->base);
> -	}
> +	drm_framebuffer_reference(&ifbdev->fb->base);
>  
>  	/* Final pass to check if any active pipes don't have fbs */
>  	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
> @@ -596,8 +568,6 @@ static bool intel_fbdev_init_bios(struct drm_device *dev,
>  	DRM_DEBUG_KMS("using BIOS fb for initial console\n");
>  	return true;
>  
> -out_unref_obj:
> -	drm_framebuffer_unreference(&fb->base);
>  out:
>  
>  	return false;
> -- 
> 1.8.4.2
> 
> _______________________________________________
> Intel-gfx mailing list
> Intel-gfx@lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/intel-gfx
Jesse Barnes March 9, 2014, 5:33 p.m. UTC | #2
On Sat, 8 Mar 2014 11:33:15 +0100
Daniel Vetter <daniel@ffwll.ch> wrote:

> On Fri, Mar 07, 2014 at 08:57:55AM -0800, Jesse Barnes wrote:
> > By stuffing the fb allocation into the crtc, we get mode set lifetime
> > refcounting for free, but have to handle the initial pin & fence
> > slightly differently.  It also means we can move the shared fb handling
> > into the core rather than leaving it out in the fbdev code.
> > 
> > v2: null out crtc->fb on error (Daniel)
> >     take fbdev fb ref and remove unused error path (Daniel)
> > 
> > Requested-by: Daniel Vetter <daniel@ffwll.ch>
> > Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
> 
> Ok, I've merged patches 1-4 and this one here from the series. Not
> terribly happy that you didn't squash in this fixup, since now people
> might stumble over the strange lifetime rules of the previous patches. But
> meh ;-)

Yeah even though there's a handoff from the core to the fbdev side, I
still think they're correct as far as fbs go.  The underlying obj ref
may not be correct though; I think that was papering over an earlier
bug.

Either way those should manifest as a leaked object (the stolen fb will
stick around forever) rather than a crash or something.  Whereas this
last patch is more likely to cause serious trouble I think since it's a
bit more invasive and relies on some other bits more...

Anyway thanks, looking forward to seeing the perf data on the swizzle
stuff.
Daniel Vetter March 9, 2014, 7:27 p.m. UTC | #3
On Sun, Mar 9, 2014 at 6:33 PM, Jesse Barnes <jbarnes@virtuousgeek.org> wrote:
> On Sat, 8 Mar 2014 11:33:15 +0100
> Daniel Vetter <daniel@ffwll.ch> wrote:
>
>> On Fri, Mar 07, 2014 at 08:57:55AM -0800, Jesse Barnes wrote:
>> > By stuffing the fb allocation into the crtc, we get mode set lifetime
>> > refcounting for free, but have to handle the initial pin & fence
>> > slightly differently.  It also means we can move the shared fb handling
>> > into the core rather than leaving it out in the fbdev code.
>> >
>> > v2: null out crtc->fb on error (Daniel)
>> >     take fbdev fb ref and remove unused error path (Daniel)
>> >
>> > Requested-by: Daniel Vetter <daniel@ffwll.ch>
>> > Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
>>
>> Ok, I've merged patches 1-4 and this one here from the series. Not
>> terribly happy that you didn't squash in this fixup, since now people
>> might stumble over the strange lifetime rules of the previous patches. But
>> meh ;-)
>
> Yeah even though there's a handoff from the core to the fbdev side, I
> still think they're correct as far as fbs go.  The underlying obj ref
> may not be correct though; I think that was papering over an earlier
> bug.
>
> Either way those should manifest as a leaked object (the stolen fb will
> stick around forever) rather than a crash or something.  Whereas this
> last patch is more likely to cause serious trouble I think since it's a
> bit more invasive and relies on some other bits more...

Yeah I agree that the refcounting before this patch was just a bit
leaky - otherwise I would have protested much louder ;-)

Cheers, Daniel
diff mbox

Patch

diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index d450ab6..718cc73 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -2068,7 +2068,7 @@  int intel_format_to_fourcc(int format)
 	}
 }
 
-static void intel_alloc_plane_obj(struct intel_crtc *crtc,
+static bool intel_alloc_plane_obj(struct intel_crtc *crtc,
 				  struct intel_plane_config *plane_config)
 {
 	struct drm_device *dev = crtc->base.dev;
@@ -2076,38 +2076,76 @@  static void intel_alloc_plane_obj(struct intel_crtc *crtc,
 	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
 	u32 base = plane_config->base;
 
-	if (!plane_config->fb)
-		return;
-
 	obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base,
 							     plane_config->size);
 	if (!obj)
-		return;
+		return false;
 
 	if (plane_config->tiled) {
 		obj->tiling_mode = I915_TILING_X;
-		obj->stride = plane_config->fb->base.pitches[0];
+		obj->stride = crtc->base.fb->pitches[0];
 	}
 
-	mode_cmd.pixel_format = plane_config->fb->base.pixel_format;
-	mode_cmd.width = plane_config->fb->base.width;
-	mode_cmd.height = plane_config->fb->base.height;
-	mode_cmd.pitches[0] = plane_config->fb->base.pitches[0];
+	mode_cmd.pixel_format = crtc->base.fb->pixel_format;
+	mode_cmd.width = crtc->base.fb->width;
+	mode_cmd.height = crtc->base.fb->height;
+	mode_cmd.pitches[0] = crtc->base.fb->pitches[0];
 
 	mutex_lock(&dev->struct_mutex);
 
-	if (intel_framebuffer_init(dev, plane_config->fb, &mode_cmd, obj)) {
+	if (intel_framebuffer_init(dev, to_intel_framebuffer(crtc->base.fb),
+				   &mode_cmd, obj)) {
 		DRM_DEBUG_KMS("intel fb init failed\n");
 		goto out_unref_obj;
 	}
 
 	mutex_unlock(&dev->struct_mutex);
-	DRM_DEBUG_KMS("plane fb obj %p\n", plane_config->fb->obj);
-	return;
+
+	DRM_DEBUG_KMS("plane fb obj %p\n", obj);
+	return true;
 
 out_unref_obj:
 	drm_gem_object_unreference(&obj->base);
 	mutex_unlock(&dev->struct_mutex);
+	return false;
+}
+
+static void intel_find_plane_obj(struct intel_crtc *intel_crtc,
+				 struct intel_plane_config *plane_config)
+{
+	struct drm_device *dev = intel_crtc->base.dev;
+	struct drm_crtc *c;
+	struct intel_crtc *i;
+	struct intel_framebuffer *fb;
+
+	if (!intel_crtc->base.fb)
+		return;
+
+	if (intel_alloc_plane_obj(intel_crtc, plane_config))
+		return;
+
+	kfree(intel_crtc->base.fb);
+
+	/*
+	 * Failed to alloc the obj, check to see if we should share
+	 * an fb with another CRTC instead
+	 */
+	list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+		i = to_intel_crtc(c);
+
+		if (c == &intel_crtc->base)
+			continue;
+
+		if (!i->active || !c->fb)
+			continue;
+
+		fb = to_intel_framebuffer(c->fb);
+		if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) {
+			drm_framebuffer_reference(c->fb);
+			intel_crtc->base.fb = c->fb;
+			break;
+		}
+	}
 }
 
 static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
@@ -5636,8 +5674,8 @@  static void i9xx_get_plane_config(struct intel_crtc *crtc,
 	int fourcc, pixel_format;
 	int aligned_height;
 
-	plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL);
-	if (!plane_config->fb) {
+	crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
+	if (!crtc->base.fb) {
 		DRM_DEBUG_KMS("failed to alloc fb\n");
 		return;
 	}
@@ -5650,8 +5688,8 @@  static void i9xx_get_plane_config(struct intel_crtc *crtc,
 
 	pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
 	fourcc = intel_format_to_fourcc(pixel_format);
-	plane_config->fb->base.pixel_format = fourcc;
-	plane_config->fb->base.bits_per_pixel =
+	crtc->base.fb->pixel_format = fourcc;
+	crtc->base.fb->bits_per_pixel =
 		drm_format_plane_cpp(fourcc, 0) * 8;
 
 	if (INTEL_INFO(dev)->gen >= 4) {
@@ -5666,23 +5704,23 @@  static void i9xx_get_plane_config(struct intel_crtc *crtc,
 	plane_config->base = base;
 
 	val = I915_READ(PIPESRC(pipe));
-	plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1;
-	plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1;
+	crtc->base.fb->width = ((val >> 16) & 0xfff) + 1;
+	crtc->base.fb->height = ((val >> 0) & 0xfff) + 1;
 
 	val = I915_READ(DSPSTRIDE(pipe));
-	plane_config->fb->base.pitches[0] = val & 0xffffff80;
+	crtc->base.fb->pitches[0] = val & 0xffffff80;
 
-	aligned_height = intel_align_height(dev, plane_config->fb->base.height,
+	aligned_height = intel_align_height(dev, crtc->base.fb->height,
 					    plane_config->tiled);
 
-	plane_config->size = ALIGN(plane_config->fb->base.pitches[0] *
+	plane_config->size = ALIGN(crtc->base.fb->pitches[0] *
 				   aligned_height, PAGE_SIZE);
 
 	DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
-		      pipe, plane, plane_config->fb->base.width,
-		      plane_config->fb->base.height,
-		      plane_config->fb->base.bits_per_pixel, base,
-		      plane_config->fb->base.pitches[0],
+		      pipe, plane, crtc->base.fb->width,
+		      crtc->base.fb->height,
+		      crtc->base.fb->bits_per_pixel, base,
+		      crtc->base.fb->pitches[0],
 		      plane_config->size);
 
 }
@@ -6644,8 +6682,8 @@  static void ironlake_get_plane_config(struct intel_crtc *crtc,
 	int fourcc, pixel_format;
 	int aligned_height;
 
-	plane_config->fb = kzalloc(sizeof(*plane_config->fb), GFP_KERNEL);
-	if (!plane_config->fb) {
+	crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
+	if (!crtc->base.fb) {
 		DRM_DEBUG_KMS("failed to alloc fb\n");
 		return;
 	}
@@ -6658,8 +6696,8 @@  static void ironlake_get_plane_config(struct intel_crtc *crtc,
 
 	pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
 	fourcc = intel_format_to_fourcc(pixel_format);
-	plane_config->fb->base.pixel_format = fourcc;
-	plane_config->fb->base.bits_per_pixel =
+	crtc->base.fb->pixel_format = fourcc;
+	crtc->base.fb->bits_per_pixel =
 		drm_format_plane_cpp(fourcc, 0) * 8;
 
 	base = I915_READ(DSPSURF(plane)) & 0xfffff000;
@@ -6674,23 +6712,23 @@  static void ironlake_get_plane_config(struct intel_crtc *crtc,
 	plane_config->base = base;
 
 	val = I915_READ(PIPESRC(pipe));
-	plane_config->fb->base.width = ((val >> 16) & 0xfff) + 1;
-	plane_config->fb->base.height = ((val >> 0) & 0xfff) + 1;
+	crtc->base.fb->width = ((val >> 16) & 0xfff) + 1;
+	crtc->base.fb->height = ((val >> 0) & 0xfff) + 1;
 
 	val = I915_READ(DSPSTRIDE(pipe));
-	plane_config->fb->base.pitches[0] = val & 0xffffff80;
+	crtc->base.fb->pitches[0] = val & 0xffffff80;
 
-	aligned_height = intel_align_height(dev, plane_config->fb->base.height,
+	aligned_height = intel_align_height(dev, crtc->base.fb->height,
 					    plane_config->tiled);
 
-	plane_config->size = ALIGN(plane_config->fb->base.pitches[0] *
+	plane_config->size = ALIGN(crtc->base.fb->pitches[0] *
 				   aligned_height, PAGE_SIZE);
 
 	DRM_DEBUG_KMS("pipe/plane %d/%d with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
-		      pipe, plane, plane_config->fb->base.width,
-		      plane_config->fb->base.height,
-		      plane_config->fb->base.bits_per_pixel, base,
-		      plane_config->fb->base.pitches[0],
+		      pipe, plane, crtc->base.fb->width,
+		      crtc->base.fb->height,
+		      crtc->base.fb->bits_per_pixel, base,
+		      crtc->base.fb->pitches[0],
 		      plane_config->size);
 }
 
@@ -11258,10 +11296,7 @@  void intel_modeset_init(struct drm_device *dev)
 		if (!crtc->active)
 			continue;
 
-#if IS_ENABLED(CONFIG_FB)
 		/*
-		 * We don't have a good way of freeing the buffer w/o the FB
-		 * layer owning it...
 		 * Note that reserving the BIOS fb up front prevents us
 		 * from stuffing other stolen allocations like the ring
 		 * on top.  This prevents some ugliness at boot time, and
@@ -11275,9 +11310,8 @@  void intel_modeset_init(struct drm_device *dev)
 			 * If the fb is shared between multiple heads, we'll
 			 * just get the first one.
 			 */
-			intel_alloc_plane_obj(crtc, &crtc->plane_config);
+			intel_find_plane_obj(crtc, &crtc->plane_config);
 		}
-#endif
 	}
 }
 
@@ -11648,9 +11682,32 @@  void intel_modeset_setup_hw_state(struct drm_device *dev,
 
 void intel_modeset_gem_init(struct drm_device *dev)
 {
+	struct drm_crtc *c;
+	struct intel_framebuffer *fb;
+
 	intel_modeset_init_hw(dev);
 
 	intel_setup_overlay(dev);
+
+	/*
+	 * Make sure any fbs we allocated at startup are properly
+	 * pinned & fenced.  When we do the allocation it's too early
+	 * for this.
+	 */
+	mutex_lock(&dev->struct_mutex);
+	list_for_each_entry(c, &dev->mode_config.crtc_list, head) {
+		if (!c->fb)
+			continue;
+
+		fb = to_intel_framebuffer(c->fb);
+		if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL)) {
+			DRM_ERROR("failed to pin boot fb on pipe %d\n",
+				  to_intel_crtc(c)->pipe);
+			drm_framebuffer_unreference(c->fb);
+			c->fb = NULL;
+		}
+	}
+	mutex_unlock(&dev->struct_mutex);
 }
 
 void intel_connector_unregister(struct intel_connector *intel_connector)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 3d404ab..2ed72cf 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -220,7 +220,6 @@  typedef struct dpll {
 } intel_clock_t;
 
 struct intel_plane_config {
-	struct intel_framebuffer *fb; /* ends up managed by intel_fbdev.c */
 	bool tiled;
 	int size;
 	u32 base;
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index 950469c..f81e3db 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -480,7 +480,7 @@  static bool intel_fbdev_init_bios(struct drm_device *dev,
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		intel_crtc = to_intel_crtc(crtc);
 
-		if (!intel_crtc->active || !intel_crtc->plane_config.fb) {
+		if (!intel_crtc->active || !crtc->fb) {
 			DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n",
 				      pipe_name(intel_crtc->pipe));
 			continue;
@@ -490,7 +490,7 @@  static bool intel_fbdev_init_bios(struct drm_device *dev,
 			DRM_DEBUG_KMS("found possible fb from plane %c\n",
 				      pipe_name(intel_crtc->pipe));
 			plane_config = &intel_crtc->plane_config;
-			fb = plane_config->fb;
+			fb = to_intel_framebuffer(crtc->fb);
 			max_size = plane_config->size;
 		}
 	}
@@ -542,43 +542,15 @@  static bool intel_fbdev_init_bios(struct drm_device *dev,
 			      max_size, cur_size);
 	}
 
-	/* Free unused fbs */
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		struct intel_framebuffer *cur_fb;
-
-		intel_crtc = to_intel_crtc(crtc);
-		cur_fb = intel_crtc->plane_config.fb;
-
-		if (cur_fb && cur_fb != fb)
-			drm_framebuffer_unreference(&cur_fb->base);
-	}
-
 	if (!fb) {
 		DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n");
 		goto out;
 	}
 
-	ifbdev->preferred_bpp = plane_config->fb->base.bits_per_pixel;
+	ifbdev->preferred_bpp = fb->base.bits_per_pixel;
 	ifbdev->fb = fb;
 
-	/* Assuming a single fb across all pipes here */
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		intel_crtc = to_intel_crtc(crtc);
-
-		if (!intel_crtc->active)
-			continue;
-
-		/*
-		 * This should only fail on the first one so we don't need
-		 * to cleanup any secondary crtc->fbs
-		 */
-		if (intel_pin_and_fence_fb_obj(dev, fb->obj, NULL))
-			goto out_unref_obj;
-
-		crtc->fb = &fb->base;
-		drm_gem_object_reference(&fb->obj->base);
-		drm_framebuffer_reference(&fb->base);
-	}
+	drm_framebuffer_reference(&ifbdev->fb->base);
 
 	/* Final pass to check if any active pipes don't have fbs */
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -596,8 +568,6 @@  static bool intel_fbdev_init_bios(struct drm_device *dev,
 	DRM_DEBUG_KMS("using BIOS fb for initial console\n");
 	return true;
 
-out_unref_obj:
-	drm_framebuffer_unreference(&fb->base);
 out:
 
 	return false;