* We can ditch the adjusted_mode.crtc_clock check as soon
         * as Haswell has gained clock readout/fastboot support.
         *
--       * We can ditch the crtc->fb check as soon as we can
++       * We can ditch the crtc->primary->fb check as soon as we can
         * properly reconstruct framebuffers.
         */
 -      return intel_crtc->active && crtc->fb &&
 +      return intel_crtc->active && crtc->primary->fb &&
                intel_crtc->config.adjusted_mode.crtc_clock;
  }
  
        }
  }
  
- static int i9xx_update_plane(struct drm_crtc *crtc, struct drm_framebuffer *fb,
-                            int x, int y)
+ int intel_format_to_fourcc(int format)
+ {
+       switch (format) {
+       case DISPPLANE_8BPP:
+               return DRM_FORMAT_C8;
+       case DISPPLANE_BGRX555:
+               return DRM_FORMAT_XRGB1555;
+       case DISPPLANE_BGRX565:
+               return DRM_FORMAT_RGB565;
+       default:
+       case DISPPLANE_BGRX888:
+               return DRM_FORMAT_XRGB8888;
+       case DISPPLANE_RGBX888:
+               return DRM_FORMAT_XBGR8888;
+       case DISPPLANE_BGRX101010:
+               return DRM_FORMAT_XRGB2101010;
+       case DISPPLANE_RGBX101010:
+               return DRM_FORMAT_XBGR2101010;
+       }
+ }
+ 
+ static bool intel_alloc_plane_obj(struct intel_crtc *crtc,
+                                 struct intel_plane_config *plane_config)
+ {
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_gem_object *obj = NULL;
+       struct drm_mode_fb_cmd2 mode_cmd = { 0 };
+       u32 base = plane_config->base;
+ 
+       if (plane_config->size == 0)
+               return false;
+ 
+       obj = i915_gem_object_create_stolen_for_preallocated(dev, base, base,
+                                                            plane_config->size);
+       if (!obj)
+               return false;
+ 
+       if (plane_config->tiled) {
+               obj->tiling_mode = I915_TILING_X;
 -              obj->stride = crtc->base.fb->pitches[0];
++              obj->stride = crtc->base.primary->fb->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];
++      mode_cmd.pixel_format = crtc->base.primary->fb->pixel_format;
++      mode_cmd.width = crtc->base.primary->fb->width;
++      mode_cmd.height = crtc->base.primary->fb->height;
++      mode_cmd.pitches[0] = crtc->base.primary->fb->pitches[0];
+ 
+       mutex_lock(&dev->struct_mutex);
+ 
 -      if (intel_framebuffer_init(dev, to_intel_framebuffer(crtc->base.fb),
++      if (intel_framebuffer_init(dev, to_intel_framebuffer(crtc->base.primary->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", 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)
++      if (!intel_crtc->base.primary->fb)
+               return;
+ 
+       if (intel_alloc_plane_obj(intel_crtc, plane_config))
+               return;
+ 
 -      kfree(intel_crtc->base.fb);
 -      intel_crtc->base.fb = NULL;
++      kfree(intel_crtc->base.primary->fb);
++      intel_crtc->base.primary->fb = NULL;
+ 
+       /*
+        * 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)
++              if (!i->active || !c->primary->fb)
+                       continue;
+ 
 -              fb = to_intel_framebuffer(c->fb);
++              fb = to_intel_framebuffer(c->primary->fb);
+               if (i915_gem_obj_ggtt_offset(fb->obj) == plane_config->base) {
 -                      drm_framebuffer_reference(c->fb);
 -                      intel_crtc->base.fb = c->fb;
++                      drm_framebuffer_reference(c->primary->fb);
++                      intel_crtc->base.primary->fb = c->primary->fb;
+                       break;
+               }
+       }
+ }
+ 
+ static int i9xx_update_primary_plane(struct drm_crtc *crtc,
+                                    struct drm_framebuffer *fb,
+                                    int x, int y)
  {
        struct drm_device *dev = crtc->dev;
        struct drm_i915_private *dev_priv = dev->dev_private;
                /*
                 * FIXME: Once we have proper support for primary planes (and
                 * disabling them without disabling the entire crtc) allow again
--               * a NULL crtc->fb.
++               * a NULL crtc->primary->fb.
                 */
 -              if (intel_crtc->active && crtc->fb)
 +              if (intel_crtc->active && crtc->primary->fb)
-                       dev_priv->display.update_plane(crtc, crtc->primary->fb,
-                                                      crtc->x, crtc->y);
+                       dev_priv->display.update_primary_plane(crtc,
 -                                                             crtc->fb,
++                                                             crtc->primary->fb,
+                                                              crtc->x,
+                                                              crtc->y);
                mutex_unlock(&crtc->mutex);
        }
  }
        pipe_config->port_clock = clock.dot / 5;
  }
  
 -      crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
 -      if (!crtc->base.fb) {
+ static void i9xx_get_plane_config(struct intel_crtc *crtc,
+                                 struct intel_plane_config *plane_config)
+ {
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 val, base, offset;
+       int pipe = crtc->pipe, plane = crtc->plane;
+       int fourcc, pixel_format;
+       int aligned_height;
+ 
 -      crtc->base.fb->pixel_format = fourcc;
 -      crtc->base.fb->bits_per_pixel =
++      crtc->base.primary->fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
++      if (!crtc->base.primary->fb) {
+               DRM_DEBUG_KMS("failed to alloc fb\n");
+               return;
+       }
+ 
+       val = I915_READ(DSPCNTR(plane));
+ 
+       if (INTEL_INFO(dev)->gen >= 4)
+               if (val & DISPPLANE_TILED)
+                       plane_config->tiled = true;
+ 
+       pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
+       fourcc = intel_format_to_fourcc(pixel_format);
 -      crtc->base.fb->width = ((val >> 16) & 0xfff) + 1;
 -      crtc->base.fb->height = ((val >> 0) & 0xfff) + 1;
++      crtc->base.primary->fb->pixel_format = fourcc;
++      crtc->base.primary->fb->bits_per_pixel =
+               drm_format_plane_cpp(fourcc, 0) * 8;
+ 
+       if (INTEL_INFO(dev)->gen >= 4) {
+               if (plane_config->tiled)
+                       offset = I915_READ(DSPTILEOFF(plane));
+               else
+                       offset = I915_READ(DSPLINOFF(plane));
+               base = I915_READ(DSPSURF(plane)) & 0xfffff000;
+       } else {
+               base = I915_READ(DSPADDR(plane));
+       }
+       plane_config->base = base;
+ 
+       val = I915_READ(PIPESRC(pipe));
 -      crtc->base.fb->pitches[0] = val & 0xffffff80;
++      crtc->base.primary->fb->width = ((val >> 16) & 0xfff) + 1;
++      crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1;
+ 
+       val = I915_READ(DSPSTRIDE(pipe));
 -      aligned_height = intel_align_height(dev, crtc->base.fb->height,
++      crtc->base.primary->fb->pitches[0] = val & 0xffffff80;
+ 
 -      plane_config->size = ALIGN(crtc->base.fb->pitches[0] *
++      aligned_height = intel_align_height(dev, crtc->base.primary->fb->height,
+                                           plane_config->tiled);
+ 
 -                    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 = ALIGN(crtc->base.primary->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, crtc->base.primary->fb->width,
++                    crtc->base.primary->fb->height,
++                    crtc->base.primary->fb->bits_per_pixel, base,
++                    crtc->base.primary->fb->pitches[0],
+                     plane_config->size);
+ 
+ }
+ 
  static bool i9xx_get_pipe_config(struct intel_crtc *crtc,
                                 struct intel_crtc_config *pipe_config)
  {
        }
  }
  
 -      crtc->base.fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
 -      if (!crtc->base.fb) {
+ static void ironlake_get_plane_config(struct intel_crtc *crtc,
+                                     struct intel_plane_config *plane_config)
+ {
+       struct drm_device *dev = crtc->base.dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       u32 val, base, offset;
+       int pipe = crtc->pipe, plane = crtc->plane;
+       int fourcc, pixel_format;
+       int aligned_height;
+ 
 -      crtc->base.fb->pixel_format = fourcc;
 -      crtc->base.fb->bits_per_pixel =
++      crtc->base.primary->fb = kzalloc(sizeof(struct intel_framebuffer), GFP_KERNEL);
++      if (!crtc->base.primary->fb) {
+               DRM_DEBUG_KMS("failed to alloc fb\n");
+               return;
+       }
+ 
+       val = I915_READ(DSPCNTR(plane));
+ 
+       if (INTEL_INFO(dev)->gen >= 4)
+               if (val & DISPPLANE_TILED)
+                       plane_config->tiled = true;
+ 
+       pixel_format = val & DISPPLANE_PIXFORMAT_MASK;
+       fourcc = intel_format_to_fourcc(pixel_format);
 -      crtc->base.fb->width = ((val >> 16) & 0xfff) + 1;
 -      crtc->base.fb->height = ((val >> 0) & 0xfff) + 1;
++      crtc->base.primary->fb->pixel_format = fourcc;
++      crtc->base.primary->fb->bits_per_pixel =
+               drm_format_plane_cpp(fourcc, 0) * 8;
+ 
+       base = I915_READ(DSPSURF(plane)) & 0xfffff000;
+       if (IS_HASWELL(dev) || IS_BROADWELL(dev)) {
+               offset = I915_READ(DSPOFFSET(plane));
+       } else {
+               if (plane_config->tiled)
+                       offset = I915_READ(DSPTILEOFF(plane));
+               else
+                       offset = I915_READ(DSPLINOFF(plane));
+       }
+       plane_config->base = base;
+ 
+       val = I915_READ(PIPESRC(pipe));
 -      crtc->base.fb->pitches[0] = val & 0xffffff80;
++      crtc->base.primary->fb->width = ((val >> 16) & 0xfff) + 1;
++      crtc->base.primary->fb->height = ((val >> 0) & 0xfff) + 1;
+ 
+       val = I915_READ(DSPSTRIDE(pipe));
 -      aligned_height = intel_align_height(dev, crtc->base.fb->height,
++      crtc->base.primary->fb->pitches[0] = val & 0xffffff80;
+ 
 -      plane_config->size = ALIGN(crtc->base.fb->pitches[0] *
++      aligned_height = intel_align_height(dev, crtc->base.primary->fb->height,
+                                           plane_config->tiled);
+ 
 -                    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 = ALIGN(crtc->base.primary->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, crtc->base.primary->fb->width,
++                    crtc->base.primary->fb->height,
++                    crtc->base.primary->fb->bits_per_pixel, base,
++                    crtc->base.primary->fb->pitches[0],
+                     plane_config->size);
+ }
+ 
  static bool ironlake_get_pipe_config(struct intel_crtc *crtc,
                                     struct intel_crtc_config *pipe_config)
  {
        intel_modeset_init_hw(dev);
  
        intel_setup_overlay(dev);
 -              if (!c->fb)
+ 
+       /*
+        * 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) {
 -              fb = to_intel_framebuffer(c->fb);
++              if (!c->primary->fb)
+                       continue;
+ 
 -                      drm_framebuffer_unreference(c->fb);
 -                      c->fb = NULL;
++              fb = to_intel_framebuffer(c->primary->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->primary->fb);
++                      c->primary->fb = NULL;
+               }
+       }
+       mutex_unlock(&dev->struct_mutex);
  }
  
  void intel_connector_unregister(struct intel_connector *intel_connector)
 
        drm_framebuffer_remove(&ifbdev->fb->base);
  }
  
 -              if (!intel_crtc->active || !crtc->fb) {
+ /*
+  * Build an intel_fbdev struct using a BIOS allocated framebuffer, if possible.
+  * The core display code will have read out the current plane configuration,
+  * so we use that to figure out if there's an object for us to use as the
+  * fb, and if so, we re-use it for the fbdev configuration.
+  *
+  * Note we only support a single fb shared across pipes for boot (mostly for
+  * fbcon), so we just find the biggest and use that.
+  */
+ static bool intel_fbdev_init_bios(struct drm_device *dev,
+                                struct intel_fbdev *ifbdev)
+ {
+       struct intel_framebuffer *fb = NULL;
+       struct drm_crtc *crtc;
+       struct intel_crtc *intel_crtc;
+       struct intel_plane_config *plane_config = NULL;
+       unsigned int max_size = 0;
+ 
+       if (!i915.fastboot)
+               return false;
+ 
+       /* Find the largest fb */
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               intel_crtc = to_intel_crtc(crtc);
+ 
 -                      fb = to_intel_framebuffer(crtc->fb);
++              if (!intel_crtc->active || !crtc->primary->fb) {
+                       DRM_DEBUG_KMS("pipe %c not active or no fb, skipping\n",
+                                     pipe_name(intel_crtc->pipe));
+                       continue;
+               }
+ 
+               if (intel_crtc->plane_config.size > max_size) {
+                       DRM_DEBUG_KMS("found possible fb from plane %c\n",
+                                     pipe_name(intel_crtc->pipe));
+                       plane_config = &intel_crtc->plane_config;
 -              WARN(!crtc->fb,
++                      fb = to_intel_framebuffer(crtc->primary->fb);
+                       max_size = plane_config->size;
+               }
+       }
+ 
+       if (!fb) {
+               DRM_DEBUG_KMS("no active fbs found, not using BIOS config\n");
+               goto out;
+       }
+ 
+       /* Now make sure all the pipes will fit into it */
+       list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+               unsigned int cur_size;
+ 
+               intel_crtc = to_intel_crtc(crtc);
+ 
+               if (!intel_crtc->active) {
+                       DRM_DEBUG_KMS("pipe %c not active, skipping\n",
+                                     pipe_name(intel_crtc->pipe));
+                       continue;
+               }
+ 
+               DRM_DEBUG_KMS("checking plane %c for BIOS fb\n",
+                             pipe_name(intel_crtc->pipe));
+ 
+               /*
+                * See if the plane fb we found above will fit on this
+                * pipe.  Note we need to use the selected fb's bpp rather
+                * than the current pipe's, since they could be different.
+                */
+               cur_size = intel_crtc->config.adjusted_mode.crtc_hdisplay *
+                       intel_crtc->config.adjusted_mode.crtc_vdisplay;
+               DRM_DEBUG_KMS("pipe %c area: %d\n", pipe_name(intel_crtc->pipe),
+                             cur_size);
+               cur_size *= fb->base.bits_per_pixel / 8;
+               DRM_DEBUG_KMS("total size %d (bpp %d)\n", cur_size,
+                             fb->base.bits_per_pixel / 8);
+ 
+               if (cur_size > max_size) {
+                       DRM_DEBUG_KMS("fb not big enough for plane %c (%d vs %d)\n",
+                                     pipe_name(intel_crtc->pipe),
+                                     cur_size, max_size);
+                       plane_config = NULL;
+                       fb = NULL;
+                       break;
+               }
+ 
+               DRM_DEBUG_KMS("fb big enough for plane %c (%d >= %d)\n",
+                             pipe_name(intel_crtc->pipe),
+                             max_size, cur_size);
+       }
+ 
+       if (!fb) {
+               DRM_DEBUG_KMS("BIOS fb not suitable for all pipes, not using\n");
+               goto out;
+       }
+ 
+       ifbdev->preferred_bpp = fb->base.bits_per_pixel;
+       ifbdev->fb = fb;
+ 
+       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) {
+               intel_crtc = to_intel_crtc(crtc);
+ 
+               if (!intel_crtc->active)
+                       continue;
+ 
++              WARN(!crtc->primary->fb,
+                    "re-used BIOS config but lost an fb on crtc %d\n",
+                    crtc->base.id);
+       }
+ 
+ 
+       DRM_DEBUG_KMS("using BIOS fb for initial console\n");
+       return true;
+ 
+ out:
+ 
+       return false;
+ }
+ 
  int intel_fbdev_init(struct drm_device *dev)
  {
        struct intel_fbdev *ifbdev;