diff mbox

[1/6] drm/fb: fix fbdev object model + cleanup properly.

Message ID 1269927258-13498-2-git-send-email-airlied@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dave Airlie March 30, 2010, 5:34 a.m. UTC
None
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index d91fb8c..6a472d5 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -857,7 +857,6 @@  void drm_mode_config_init(struct drm_device *dev)
 	mutex_init(&dev->mode_config.mutex);
 	mutex_init(&dev->mode_config.idr_mutex);
 	INIT_LIST_HEAD(&dev->mode_config.fb_list);
-	INIT_LIST_HEAD(&dev->mode_config.fb_kernel_list);
 	INIT_LIST_HEAD(&dev->mode_config.crtc_list);
 	INIT_LIST_HEAD(&dev->mode_config.connector_list);
 	INIT_LIST_HEAD(&dev->mode_config.encoder_list);
diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 51103aa..9d23f54 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -55,7 +55,7 @@  static void drm_mode_validate_flag(struct drm_connector *connector,
 }
 
 /**
- * drm_helper_probe_connector_modes - get complete set of display modes
+ * drm_helper_probe_single_connector_modes - get complete set of display modes
  * @dev: DRM device
  * @maxX: max width for modes
  * @maxY: max height for modes
@@ -154,21 +154,6 @@  prune:
 }
 EXPORT_SYMBOL(drm_helper_probe_single_connector_modes);
 
-int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
-				      uint32_t maxY)
-{
-	struct drm_connector *connector;
-	int count = 0;
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		count += drm_helper_probe_single_connector_modes(connector,
-								 maxX, maxY);
-	}
-
-	return count;
-}
-EXPORT_SYMBOL(drm_helper_probe_connector_modes);
-
 /**
  * drm_helper_encoder_in_use - check if a given encoder is in use
  * @encoder: encoder to check
@@ -263,302 +248,6 @@  void drm_helper_disable_unused_functions(struct drm_device *dev)
 }
 EXPORT_SYMBOL(drm_helper_disable_unused_functions);
 
-static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height)
-{
-	struct drm_display_mode *mode;
-
-	list_for_each_entry(mode, &connector->modes, head) {
-		if (drm_mode_width(mode) > width ||
-		    drm_mode_height(mode) > height)
-			continue;
-		if (mode->type & DRM_MODE_TYPE_PREFERRED)
-			return mode;
-	}
-	return NULL;
-}
-
-static bool drm_has_cmdline_mode(struct drm_connector *connector)
-{
-	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
-	struct drm_fb_helper_cmdline_mode *cmdline_mode;
-
-	if (!fb_help_conn)
-		return false;
-
-	cmdline_mode = &fb_help_conn->cmdline_mode;
-	return cmdline_mode->specified;
-}
-
-static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height)
-{
-	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
-	struct drm_fb_helper_cmdline_mode *cmdline_mode;
-	struct drm_display_mode *mode = NULL;
-
-	if (!fb_help_conn)
-		return mode;
-
-	cmdline_mode = &fb_help_conn->cmdline_mode;
-	if (cmdline_mode->specified == false)
-		return mode;
-
-	/* attempt to find a matching mode in the list of modes
-	 *  we have gotten so far, if not add a CVT mode that conforms
-	 */
-	if (cmdline_mode->rb || cmdline_mode->margins)
-		goto create_mode;
-
-	list_for_each_entry(mode, &connector->modes, head) {
-		/* check width/height */
-		if (mode->hdisplay != cmdline_mode->xres ||
-		    mode->vdisplay != cmdline_mode->yres)
-			continue;
-
-		if (cmdline_mode->refresh_specified) {
-			if (mode->vrefresh != cmdline_mode->refresh)
-				continue;
-		}
-
-		if (cmdline_mode->interlace) {
-			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
-				continue;
-		}
-		return mode;
-	}
-
-create_mode:
-	mode = drm_cvt_mode(connector->dev, cmdline_mode->xres,
-			    cmdline_mode->yres,
-			    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
-			    cmdline_mode->rb, cmdline_mode->interlace,
-			    cmdline_mode->margins);
-	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-	list_add(&mode->head, &connector->modes);
-	return mode;
-}
-
-static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
-{
-	bool enable;
-
-	if (strict) {
-		enable = connector->status == connector_status_connected;
-	} else {
-		enable = connector->status != connector_status_disconnected;
-	}
-	return enable;
-}
-
-static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
-{
-	bool any_enabled = false;
-	struct drm_connector *connector;
-	int i = 0;
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		enabled[i] = drm_connector_enabled(connector, true);
-		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
-			  enabled[i] ? "yes" : "no");
-		any_enabled |= enabled[i];
-		i++;
-	}
-
-	if (any_enabled)
-		return;
-
-	i = 0;
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		enabled[i] = drm_connector_enabled(connector, false);
-		i++;
-	}
-}
-
-static bool drm_target_preferred(struct drm_device *dev,
-				 struct drm_display_mode **modes,
-				 bool *enabled, int width, int height)
-{
-	struct drm_connector *connector;
-	int i = 0;
-
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-
-		if (enabled[i] == false) {
-			i++;
-			continue;
-		}
-
-		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
-			      connector->base.id);
-
-		/* got for command line mode first */
-		modes[i] = drm_pick_cmdline_mode(connector, width, height);
-		if (!modes[i]) {
-			DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
-				      connector->base.id);
-			modes[i] = drm_has_preferred_mode(connector, width, height);
-		}
-		/* No preferred modes, pick one off the list */
-		if (!modes[i] && !list_empty(&connector->modes)) {
-			list_for_each_entry(modes[i], &connector->modes, head)
-				break;
-		}
-		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
-			  "none");
-		i++;
-	}
-	return true;
-}
-
-static int drm_pick_crtcs(struct drm_device *dev,
-			  struct drm_crtc **best_crtcs,
-			  struct drm_display_mode **modes,
-			  int n, int width, int height)
-{
-	int c, o;
-	struct drm_connector *connector;
-	struct drm_connector_helper_funcs *connector_funcs;
-	struct drm_encoder *encoder;
-	struct drm_crtc *best_crtc;
-	int my_score, best_score, score;
-	struct drm_crtc **crtcs, *crtc;
-
-	if (n == dev->mode_config.num_connector)
-		return 0;
-	c = 0;
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		if (c == n)
-			break;
-		c++;
-	}
-
-	best_crtcs[n] = NULL;
-	best_crtc = NULL;
-	best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height);
-	if (modes[n] == NULL)
-		return best_score;
-
-	crtcs = kmalloc(dev->mode_config.num_connector *
-			sizeof(struct drm_crtc *), GFP_KERNEL);
-	if (!crtcs)
-		return best_score;
-
-	my_score = 1;
-	if (connector->status == connector_status_connected)
-		my_score++;
-	if (drm_has_cmdline_mode(connector))
-		my_score++;
-	if (drm_has_preferred_mode(connector, width, height))
-		my_score++;
-
-	connector_funcs = connector->helper_private;
-	encoder = connector_funcs->best_encoder(connector);
-	if (!encoder)
-		goto out;
-
-	connector->encoder = encoder;
-
-	/* select a crtc for this connector and then attempt to configure
-	   remaining connectors */
-	c = 0;
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-
-		if ((encoder->possible_crtcs & (1 << c)) == 0) {
-			c++;
-			continue;
-		}
-
-		for (o = 0; o < n; o++)
-			if (best_crtcs[o] == crtc)
-				break;
-
-		if (o < n) {
-			/* ignore cloning for now */
-			c++;
-			continue;
-		}
-
-		crtcs[n] = crtc;
-		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *));
-		score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1,
-						  width, height);
-		if (score > best_score) {
-			best_crtc = crtc;
-			best_score = score;
-			memcpy(best_crtcs, crtcs,
-			       dev->mode_config.num_connector *
-			       sizeof(struct drm_crtc *));
-		}
-		c++;
-	}
-out:
-	kfree(crtcs);
-	return best_score;
-}
-
-static void drm_setup_crtcs(struct drm_device *dev)
-{
-	struct drm_crtc **crtcs;
-	struct drm_display_mode **modes;
-	struct drm_encoder *encoder;
-	struct drm_connector *connector;
-	bool *enabled;
-	int width, height;
-	int i, ret;
-
-	DRM_DEBUG_KMS("\n");
-
-	width = dev->mode_config.max_width;
-	height = dev->mode_config.max_height;
-
-	/* clean out all the encoder/crtc combos */
-	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-		encoder->crtc = NULL;
-	}
-
-	crtcs = kcalloc(dev->mode_config.num_connector,
-			sizeof(struct drm_crtc *), GFP_KERNEL);
-	modes = kcalloc(dev->mode_config.num_connector,
-			sizeof(struct drm_display_mode *), GFP_KERNEL);
-	enabled = kcalloc(dev->mode_config.num_connector,
-			  sizeof(bool), GFP_KERNEL);
-
-	drm_enable_connectors(dev, enabled);
-
-	ret = drm_target_preferred(dev, modes, enabled, width, height);
-	if (!ret)
-		DRM_ERROR("Unable to find initial modes\n");
-
-	DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
-
-	drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
-
-	i = 0;
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		struct drm_display_mode *mode = modes[i];
-		struct drm_crtc *crtc = crtcs[i];
-
-		if (connector->encoder == NULL) {
-			i++;
-			continue;
-		}
-
-		if (mode && crtc) {
-			DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
-				  mode->name, crtc->base.id);
-			crtc->desired_mode = mode;
-			connector->encoder->crtc = crtc;
-		} else {
-			connector->encoder->crtc = NULL;
-			connector->encoder = NULL;
-		}
-		i++;
-	}
-
-	kfree(crtcs);
-	kfree(modes);
-	kfree(enabled);
-}
-
 /**
  * drm_encoder_crtc_ok - can a given crtc drive a given encoder?
  * @encoder: encoder to test
@@ -984,63 +673,6 @@  fail:
 }
 EXPORT_SYMBOL(drm_crtc_helper_set_config);
 
-bool drm_helper_plugged_event(struct drm_device *dev)
-{
-	DRM_DEBUG_KMS("\n");
-
-	drm_helper_probe_connector_modes(dev, dev->mode_config.max_width,
-					 dev->mode_config.max_height);
-
-	drm_setup_crtcs(dev);
-
-	/* alert the driver fb layer */
-	dev->mode_config.funcs->fb_changed(dev);
-
-	/* FIXME: send hotplug event */
-	return true;
-}
-/**
- * drm_initial_config - setup a sane initial connector configuration
- * @dev: DRM device
- *
- * LOCKING:
- * Called at init time, must take mode config lock.
- *
- * Scan the CRTCs and connectors and try to put together an initial setup.
- * At the moment, this is a cloned configuration across all heads with
- * a new framebuffer object as the backing store.
- *
- * RETURNS:
- * Zero if everything went ok, nonzero otherwise.
- */
-bool drm_helper_initial_config(struct drm_device *dev)
-{
-	int count = 0;
-
-	/* disable all the possible outputs/crtcs before entering KMS mode */
-	drm_helper_disable_unused_functions(dev);
-
-	drm_fb_helper_parse_command_line(dev);
-
-	count = drm_helper_probe_connector_modes(dev,
-						 dev->mode_config.max_width,
-						 dev->mode_config.max_height);
-
-	/*
-	 * we shouldn't end up with no modes here.
-	 */
-	if (count == 0)
-		printk(KERN_INFO "No connectors reported connected with modes\n");
-
-	drm_setup_crtcs(dev);
-
-	/* alert the driver fb layer */
-	dev->mode_config.funcs->fb_changed(dev);
-
-	return 0;
-}
-EXPORT_SYMBOL(drm_helper_initial_config);
-
 static int drm_helper_choose_encoder_dpms(struct drm_encoder *encoder)
 {
 	int dpms = DRM_MODE_DPMS_OFF;
@@ -1123,27 +755,6 @@  void drm_helper_connector_dpms(struct drm_connector *connector, int mode)
 }
 EXPORT_SYMBOL(drm_helper_connector_dpms);
 
-/**
- * drm_hotplug_stage_two
- * @dev DRM device
- * @connector hotpluged connector
- *
- * LOCKING.
- * Caller must hold mode config lock, function might grab struct lock.
- *
- * Stage two of a hotplug.
- *
- * RETURNS:
- * Zero on success, errno on failure.
- */
-int drm_helper_hotplug_stage_two(struct drm_device *dev)
-{
-	drm_helper_plugged_event(dev);
-
-	return 0;
-}
-EXPORT_SYMBOL(drm_helper_hotplug_stage_two);
-
 int drm_helper_mode_fill_fb_struct(struct drm_framebuffer *fb,
 				   struct drm_mode_fb_cmd *mode_cmd)
 {
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 5054970..d6f9f94 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -745,32 +745,30 @@  EXPORT_SYMBOL(drm_fb_helper_pan_display);
 
 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 				  int preferred_bpp,
-				  int (*fb_create)(struct drm_device *dev,
-						   uint32_t fb_width,
-						   uint32_t fb_height,
-						   uint32_t surface_width,
-						   uint32_t surface_height,
-						   uint32_t surface_depth,
-						   uint32_t surface_bpp,
-						   struct drm_framebuffer **fb_ptr))
+				  int (*fb_find_or_create)(struct drm_device *dev,
+							   struct drm_fb_helper_surface_size *sizes,
+							   struct drm_fb_helper **fb_ptr))
 {
 	struct drm_crtc *crtc;
 	struct drm_connector *connector;
-	unsigned int fb_width = (unsigned)-1, fb_height = (unsigned)-1;
-	unsigned int surface_width = 0, surface_height = 0;
 	int new_fb = 0;
 	int crtc_count = 0;
 	int ret, i, conn_count = 0;
 	struct fb_info *info;
-	struct drm_framebuffer *fb;
 	struct drm_mode_set *modeset = NULL;
 	struct drm_fb_helper *fb_helper;
-	uint32_t surface_depth = 24, surface_bpp = 32;
+	struct drm_fb_helper_surface_size sizes;
+
+	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
+	sizes.surface_depth = 24;
+	sizes.surface_bpp = 32;
+	sizes.fb_width = (unsigned)-1;
+	sizes.fb_height = (unsigned)-1;
 
 	/* if driver picks 8 or 16 by default use that
 	   for both depth/bpp */
-	if (preferred_bpp != surface_bpp) {
-		surface_depth = surface_bpp = preferred_bpp;
+	if (preferred_bpp != sizes.surface_bpp) {
+		sizes.surface_depth = sizes.surface_bpp = preferred_bpp;
 	}
 	/* first up get a count of crtcs now in use and new min/maxes width/heights */
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -786,21 +784,21 @@  int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 		if (cmdline_mode->bpp_specified) {
 			switch (cmdline_mode->bpp) {
 			case 8:
-				surface_depth = surface_bpp = 8;
+				sizes.surface_depth = sizes.surface_bpp = 8;
 				break;
 			case 15:
-				surface_depth = 15;
-				surface_bpp = 16;
+				sizes.surface_depth = 15;
+				sizes.surface_bpp = 16;
 				break;
 			case 16:
-				surface_depth = surface_bpp = 16;
+				sizes.surface_depth = sizes.surface_bpp = 16;
 				break;
 			case 24:
-				surface_depth = surface_bpp = 24;
+				sizes.surface_depth = sizes.surface_bpp = 24;
 				break;
 			case 32:
-				surface_depth = 24;
-				surface_bpp = 32;
+				sizes.surface_depth = 24;
+				sizes.surface_bpp = 32;
 				break;
 			}
 			break;
@@ -810,59 +808,41 @@  int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		if (drm_helper_crtc_in_use(crtc)) {
 			if (crtc->desired_mode) {
-				if (crtc->desired_mode->hdisplay < fb_width)
-					fb_width = crtc->desired_mode->hdisplay;
+				if (crtc->desired_mode->hdisplay < sizes.fb_width)
+					sizes.fb_width = crtc->desired_mode->hdisplay;
 
-				if (crtc->desired_mode->vdisplay < fb_height)
-					fb_height = crtc->desired_mode->vdisplay;
+				if (crtc->desired_mode->vdisplay < sizes.fb_height)
+					sizes.fb_height = crtc->desired_mode->vdisplay;
 
-				if (crtc->desired_mode->hdisplay > surface_width)
-					surface_width = crtc->desired_mode->hdisplay;
+				if (crtc->desired_mode->hdisplay > sizes.surface_width)
+					sizes.surface_width = crtc->desired_mode->hdisplay;
 
-				if (crtc->desired_mode->vdisplay > surface_height)
-					surface_height = crtc->desired_mode->vdisplay;
+				if (crtc->desired_mode->vdisplay > sizes.surface_height)
+					sizes.surface_height = crtc->desired_mode->vdisplay;
 			}
 			crtc_count++;
 		}
 	}
 
-	if (crtc_count == 0 || fb_width == -1 || fb_height == -1) {
+	if (crtc_count == 0 || sizes.fb_width == -1 || sizes.fb_height == -1) {
 		/* hmm everyone went away - assume VGA cable just fell out
 		   and will come back later. */
 		return 0;
 	}
 
-	/* do we have an fb already? */
-	if (list_empty(&dev->mode_config.fb_kernel_list)) {
-		ret = (*fb_create)(dev, fb_width, fb_height, surface_width,
-				   surface_height, surface_depth, surface_bpp,
-				   &fb);
-		if (ret)
-			return -EINVAL;
-		new_fb = 1;
-	} else {
-		fb = list_first_entry(&dev->mode_config.fb_kernel_list,
-				      struct drm_framebuffer, filp_head);
-
-		/* if someone hotplugs something bigger than we have already allocated, we are pwned.
-		   As really we can't resize an fbdev that is in the wild currently due to fbdev
-		   not really being designed for the lower layers moving stuff around under it.
-		   - so in the grand style of things - punt. */
-		if ((fb->width < surface_width) ||
-		    (fb->height < surface_height)) {
-			DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
-			return -EINVAL;
-		}
-	}
+	/* push down into drivers */
+	new_fb = (*fb_find_or_create)(dev, &sizes,
+				      &fb_helper);
+	if (new_fb < 0)
+		return new_fb;
 
-	info = fb->fbdev;
-	fb_helper = info->par;
+	info = fb_helper->fbdev;
 
 	crtc_count = 0;
 	/* okay we need to setup new connector sets in the crtcs */
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		modeset = &fb_helper->crtc_info[crtc_count].mode_set;
-		modeset->fb = fb;
+		modeset->fb = fb_helper->fb;
 		conn_count = 0;
 		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 			if (connector->encoder)
@@ -889,7 +869,6 @@  int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 		}
 	}
 	fb_helper->crtc_count = crtc_count;
-	fb_helper->fb = fb;
 
 	if (new_fb) {
 		info->var.pixclock = 0;
@@ -900,11 +879,13 @@  int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 			fb_dealloc_cmap(&info->cmap);
 			return -EINVAL;
 		}
+
+		printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
+		       info->fix.id);
+
 	} else {
 		drm_fb_helper_set_par(info);
 	}
-	printk(KERN_INFO "fb%d: %s frame buffer device\n", info->node,
-	       info->fix.id);
 
 	/* Switch back to kernel console on panic */
 	/* multi card linked list maybe */
@@ -914,7 +895,9 @@  int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 					       &paniced);
 		register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
 	}
-	list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
+	if (new_fb)
+		list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
+
 	return 0;
 }
 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
@@ -929,7 +912,7 @@  void drm_fb_helper_free(struct drm_fb_helper *helper)
 		unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
 	}
 	drm_fb_helper_crtc_free(helper);
-	fb_dealloc_cmap(&helper->fb->fbdev->cmap);
+	fb_dealloc_cmap(&helper->fbdev->cmap);
 }
 EXPORT_SYMBOL(drm_fb_helper_free);
 
@@ -951,10 +934,11 @@  void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 }
 EXPORT_SYMBOL(drm_fb_helper_fill_fix);
 
-void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
+void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
 			    uint32_t fb_width, uint32_t fb_height)
 {
-	info->pseudo_palette = fb->pseudo_palette;
+	struct drm_framebuffer *fb = fb_helper->fb;
+	info->pseudo_palette = fb_helper->pseudo_palette;
 	info->var.xres_virtual = fb->width;
 	info->var.yres_virtual = fb->height;
 	info->var.bits_per_pixel = fb->bits_per_pixel;
@@ -1022,3 +1006,364 @@  void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
 	info->var.yres = fb_height;
 }
 EXPORT_SYMBOL(drm_fb_helper_fill_var);
+
+static int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
+					    uint32_t maxY)
+{
+	struct drm_connector *connector;
+	int count = 0;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		count += connector->funcs->fill_modes(connector, maxX, maxY);
+	}
+
+	return count;
+}
+
+static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *connector, int width, int height)
+{
+	struct drm_display_mode *mode;
+
+	list_for_each_entry(mode, &connector->modes, head) {
+		if (drm_mode_width(mode) > width ||
+		    drm_mode_height(mode) > height)
+			continue;
+		if (mode->type & DRM_MODE_TYPE_PREFERRED)
+			return mode;
+	}
+	return NULL;
+}
+
+static bool drm_has_cmdline_mode(struct drm_connector *connector)
+{
+	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
+	struct drm_fb_helper_cmdline_mode *cmdline_mode;
+
+	if (!fb_help_conn)
+		return false;
+
+	cmdline_mode = &fb_help_conn->cmdline_mode;
+	return cmdline_mode->specified;
+}
+
+static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height)
+{
+	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
+	struct drm_fb_helper_cmdline_mode *cmdline_mode;
+	struct drm_display_mode *mode = NULL;
+
+	if (!fb_help_conn)
+		return mode;
+
+	cmdline_mode = &fb_help_conn->cmdline_mode;
+	if (cmdline_mode->specified == false)
+		return mode;
+
+	/* attempt to find a matching mode in the list of modes
+	 *  we have gotten so far, if not add a CVT mode that conforms
+	 */
+	if (cmdline_mode->rb || cmdline_mode->margins)
+		goto create_mode;
+
+	list_for_each_entry(mode, &connector->modes, head) {
+		/* check width/height */
+		if (mode->hdisplay != cmdline_mode->xres ||
+		    mode->vdisplay != cmdline_mode->yres)
+			continue;
+
+		if (cmdline_mode->refresh_specified) {
+			if (mode->vrefresh != cmdline_mode->refresh)
+				continue;
+		}
+
+		if (cmdline_mode->interlace) {
+			if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
+				continue;
+		}
+		return mode;
+	}
+
+create_mode:
+	mode = drm_cvt_mode(connector->dev, cmdline_mode->xres,
+			    cmdline_mode->yres,
+			    cmdline_mode->refresh_specified ? cmdline_mode->refresh : 60,
+			    cmdline_mode->rb, cmdline_mode->interlace,
+			    cmdline_mode->margins);
+	drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
+	list_add(&mode->head, &connector->modes);
+	return mode;
+}
+
+static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
+{
+	bool enable;
+
+	if (strict) {
+		enable = connector->status == connector_status_connected;
+	} else {
+		enable = connector->status != connector_status_disconnected;
+	}
+	return enable;
+}
+
+static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
+{
+	bool any_enabled = false;
+	struct drm_connector *connector;
+	int i = 0;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		enabled[i] = drm_connector_enabled(connector, true);
+		DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id,
+			  enabled[i] ? "yes" : "no");
+		any_enabled |= enabled[i];
+		i++;
+	}
+
+	if (any_enabled)
+		return;
+
+	i = 0;
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		enabled[i] = drm_connector_enabled(connector, false);
+		i++;
+	}
+}
+
+static bool drm_target_preferred(struct drm_device *dev,
+				 struct drm_display_mode **modes,
+				 bool *enabled, int width, int height)
+{
+	struct drm_connector *connector;
+	int i = 0;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+
+		if (enabled[i] == false) {
+			i++;
+			continue;
+		}
+
+		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
+			      connector->base.id);
+
+		/* got for command line mode first */
+		modes[i] = drm_pick_cmdline_mode(connector, width, height);
+		if (!modes[i]) {
+			DRM_DEBUG_KMS("looking for preferred mode on connector %d\n",
+				      connector->base.id);
+			modes[i] = drm_has_preferred_mode(connector, width, height);
+		}
+		/* No preferred modes, pick one off the list */
+		if (!modes[i] && !list_empty(&connector->modes)) {
+			list_for_each_entry(modes[i], &connector->modes, head)
+				break;
+		}
+		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
+			  "none");
+		i++;
+	}
+	return true;
+}
+
+static int drm_pick_crtcs(struct drm_device *dev,
+			  struct drm_crtc **best_crtcs,
+			  struct drm_display_mode **modes,
+			  int n, int width, int height)
+{
+	int c, o;
+	struct drm_connector *connector;
+	struct drm_connector_helper_funcs *connector_funcs;
+	struct drm_encoder *encoder;
+	struct drm_crtc *best_crtc;
+	int my_score, best_score, score;
+	struct drm_crtc **crtcs, *crtc;
+
+	if (n == dev->mode_config.num_connector)
+		return 0;
+	c = 0;
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		if (c == n)
+			break;
+		c++;
+	}
+
+	best_crtcs[n] = NULL;
+	best_crtc = NULL;
+	best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height);
+	if (modes[n] == NULL)
+		return best_score;
+
+	crtcs = kmalloc(dev->mode_config.num_connector *
+			sizeof(struct drm_crtc *), GFP_KERNEL);
+	if (!crtcs)
+		return best_score;
+
+	my_score = 1;
+	if (connector->status == connector_status_connected)
+		my_score++;
+	if (drm_has_cmdline_mode(connector))
+		my_score++;
+	if (drm_has_preferred_mode(connector, width, height))
+		my_score++;
+
+	connector_funcs = connector->helper_private;
+	encoder = connector_funcs->best_encoder(connector);
+	if (!encoder)
+		goto out;
+
+	connector->encoder = encoder;
+
+	/* select a crtc for this connector and then attempt to configure
+	   remaining connectors */
+	c = 0;
+	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+
+		if ((encoder->possible_crtcs & (1 << c)) == 0) {
+			c++;
+			continue;
+		}
+
+		for (o = 0; o < n; o++)
+			if (best_crtcs[o] == crtc)
+				break;
+
+		if (o < n) {
+			/* ignore cloning for now */
+			c++;
+			continue;
+		}
+
+		crtcs[n] = crtc;
+		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_crtc *));
+		score = my_score + drm_pick_crtcs(dev, crtcs, modes, n + 1,
+						  width, height);
+		if (score > best_score) {
+			best_crtc = crtc;
+			best_score = score;
+			memcpy(best_crtcs, crtcs,
+			       dev->mode_config.num_connector *
+			       sizeof(struct drm_crtc *));
+		}
+		c++;
+	}
+out:
+	kfree(crtcs);
+	return best_score;
+}
+
+static void drm_setup_crtcs(struct drm_device *dev)
+{
+	struct drm_crtc **crtcs;
+	struct drm_display_mode **modes;
+	struct drm_encoder *encoder;
+	struct drm_connector *connector;
+	bool *enabled;
+	int width, height;
+	int i, ret;
+
+	DRM_DEBUG_KMS("\n");
+
+	width = dev->mode_config.max_width;
+	height = dev->mode_config.max_height;
+
+	/* clean out all the encoder/crtc combos */
+	list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+		encoder->crtc = NULL;
+	}
+
+	crtcs = kcalloc(dev->mode_config.num_connector,
+			sizeof(struct drm_crtc *), GFP_KERNEL);
+	modes = kcalloc(dev->mode_config.num_connector,
+			sizeof(struct drm_display_mode *), GFP_KERNEL);
+	enabled = kcalloc(dev->mode_config.num_connector,
+			  sizeof(bool), GFP_KERNEL);
+
+	drm_enable_connectors(dev, enabled);
+
+	ret = drm_target_preferred(dev, modes, enabled, width, height);
+	if (!ret)
+		DRM_ERROR("Unable to find initial modes\n");
+
+	DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
+
+	drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
+
+	i = 0;
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		struct drm_display_mode *mode = modes[i];
+		struct drm_crtc *crtc = crtcs[i];
+
+		if (connector->encoder == NULL) {
+			i++;
+			continue;
+		}
+
+		if (mode && crtc) {
+			DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
+				  mode->name, crtc->base.id);
+			crtc->desired_mode = mode;
+			connector->encoder->crtc = crtc;
+		} else {
+			connector->encoder->crtc = NULL;
+			connector->encoder = NULL;
+		}
+		i++;
+	}
+
+	kfree(crtcs);
+	kfree(modes);
+	kfree(enabled);
+}
+
+/**
+ * drm_helper_initial_config - setup a sane initial connector configuration
+ * @dev: DRM device
+ *
+ * LOCKING:
+ * Called at init time, must take mode config lock.
+ *
+ * Scan the CRTCs and connectors and try to put together an initial setup.
+ * At the moment, this is a cloned configuration across all heads with
+ * a new framebuffer object as the backing store.
+ *
+ * RETURNS:
+ * Zero if everything went ok, nonzero otherwise.
+ */
+bool drm_helper_initial_config(struct drm_device *dev)
+{
+	int count = 0;
+
+	/* disable all the possible outputs/crtcs before entering KMS mode */
+	drm_helper_disable_unused_functions(dev);
+
+	drm_fb_helper_parse_command_line(dev);
+
+	count = drm_helper_probe_connector_modes(dev,
+						 dev->mode_config.max_width,
+						 dev->mode_config.max_height);
+
+	/*
+	 * we shouldn't end up with no modes here.
+	 */
+	if (count == 0)
+		printk(KERN_INFO "No connectors reported connected with modes\n");
+
+	drm_setup_crtcs(dev);
+
+	return 0;
+}
+EXPORT_SYMBOL(drm_helper_initial_config);
+
+bool drm_helper_fb_hotplug_event(struct drm_device *dev)
+{
+	DRM_DEBUG_KMS("\n");
+
+	drm_helper_probe_connector_modes(dev, dev->mode_config.max_width,
+					 dev->mode_config.max_height);
+
+	drm_setup_crtcs(dev);
+
+	return true;
+}
+EXPORT_SYMBOL(drm_helper_fb_hotplug_event);
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 8bfc0bb..7c7d229 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1491,7 +1491,7 @@  static int i915_load_modeset_init(struct drm_device *dev,
 
 	I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
 
-	drm_helper_initial_config(dev);
+	intel_fbdev_init(dev);
 
 	return 0;
 
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 979439c..601f354 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -220,6 +220,8 @@  enum no_fbc_reason {
 	FBC_NOT_TILED, /* buffer not tiled */
 };
 
+struct intel_kernel_fbdev;
+
 typedef struct drm_i915_private {
 	struct drm_device *dev;
 
@@ -627,6 +629,8 @@  typedef struct drm_i915_private {
 	u8 max_delay;
 
 	enum no_fbc_reason no_fbc_reason;
+
+	struct intel_kernel_fbdev *fbdev;
 } drm_i915_private_t;
 
 /** driver private structure attached to each drm_gem_object */
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 9cd6de5..80577b8 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -4506,10 +4506,6 @@  static void intel_setup_outputs(struct drm_device *dev)
 static void intel_user_framebuffer_destroy(struct drm_framebuffer *fb)
 {
 	struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
-	struct drm_device *dev = fb->dev;
-
-	if (fb->fbdev)
-		intelfb_remove(dev, fb);
 
 	drm_framebuffer_cleanup(fb);
 	drm_gem_object_unreference_unlocked(intel_fb->obj);
@@ -4532,18 +4528,13 @@  static const struct drm_framebuffer_funcs intel_fb_funcs = {
 	.create_handle = intel_user_framebuffer_create_handle,
 };
 
-int intel_framebuffer_create(struct drm_device *dev,
-			     struct drm_mode_fb_cmd *mode_cmd,
-			     struct drm_framebuffer **fb,
-			     struct drm_gem_object *obj)
+int intel_framebuffer_init(struct drm_device *dev,
+			   struct intel_framebuffer *intel_fb,
+			   struct drm_mode_fb_cmd *mode_cmd,
+			   struct drm_gem_object *obj)
 {
-	struct intel_framebuffer *intel_fb;
 	int ret;
 
-	intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
-	if (!intel_fb)
-		return -ENOMEM;
-
 	ret = drm_framebuffer_init(dev, &intel_fb->base, &intel_fb_funcs);
 	if (ret) {
 		DRM_ERROR("framebuffer init failed %d\n", ret);
@@ -4551,40 +4542,40 @@  int intel_framebuffer_create(struct drm_device *dev,
 	}
 
 	drm_helper_mode_fill_fb_struct(&intel_fb->base, mode_cmd);
-
 	intel_fb->obj = obj;
-
-	*fb = &intel_fb->base;
-
 	return 0;
 }
 
-
 static struct drm_framebuffer *
 intel_user_framebuffer_create(struct drm_device *dev,
 			      struct drm_file *filp,
 			      struct drm_mode_fb_cmd *mode_cmd)
 {
 	struct drm_gem_object *obj;
-	struct drm_framebuffer *fb;
+	struct intel_framebuffer *intel_fb;
 	int ret;
 
 	obj = drm_gem_object_lookup(dev, filp, mode_cmd->handle);
 	if (!obj)
 		return NULL;
 
-	ret = intel_framebuffer_create(dev, mode_cmd, &fb, obj);
+	intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
+	if (!intel_fb)
+		return NULL;
+
+	ret = intel_framebuffer_init(dev, intel_fb,
+				     mode_cmd, obj);
 	if (ret) {
 		drm_gem_object_unreference_unlocked(obj);
+		kfree(intel_fb);
 		return NULL;
 	}
 
-	return fb;
+	return &intel_fb->base;
 }
 
 static const struct drm_mode_config_funcs intel_mode_funcs = {
 	.fb_create = intel_user_framebuffer_create,
-	.fb_changed = intelfb_probe,
 };
 
 static struct drm_gem_object *
@@ -4924,6 +4915,8 @@  void intel_modeset_cleanup(struct drm_device *dev)
 
 	mutex_lock(&dev->struct_mutex);
 
+	intel_fbdev_fini(dev);
+
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
 		/* Skip inactive CRTCs */
 		if (!crtc->fb)
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 3a467ca..9ffb9f2 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -200,9 +200,6 @@  extern void intel_release_load_detect_pipe(struct intel_output *intel_output,
 extern struct drm_connector* intel_sdvo_find(struct drm_device *dev, int sdvoB);
 extern int intel_sdvo_supports_hotplug(struct drm_connector *connector);
 extern void intel_sdvo_set_hotplug(struct drm_connector *connector, int enable);
-extern int intelfb_probe(struct drm_device *dev);
-extern int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
-extern int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc);
 extern void intelfb_restore(void);
 extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
 				    u16 blue, int regno);
@@ -212,10 +209,12 @@  extern void intel_init_clock_gating(struct drm_device *dev);
 extern void ironlake_enable_drps(struct drm_device *dev);
 extern void ironlake_disable_drps(struct drm_device *dev);
 
-extern int intel_framebuffer_create(struct drm_device *dev,
-				    struct drm_mode_fb_cmd *mode_cmd,
-				    struct drm_framebuffer **fb,
-				    struct drm_gem_object *obj);
+extern int intel_framebuffer_init(struct drm_device *dev,
+				  struct intel_framebuffer *ifb,
+				  struct drm_mode_fb_cmd *mode_cmd,
+				  struct drm_gem_object *obj);
+extern int intel_fbdev_init(struct drm_device *dev);
+extern void intel_fbdev_fini(struct drm_device *dev);
 
 extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
 extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 8cd791d..d76e28e 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -45,9 +45,10 @@ 
 #include "i915_drm.h"
 #include "i915_drv.h"
 
-struct intelfb_par {
+struct intel_kernel_fbdev {
 	struct drm_fb_helper helper;
-	struct intel_framebuffer *intel_fb;
+	struct intel_framebuffer ifb;
+	struct list_head fbdev_list;
 	struct drm_display_mode *our_mode;
 };
 
@@ -70,54 +71,12 @@  static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
 };
 
 
-/**
- * Currently it is assumed that the old framebuffer is reused.
- *
- * LOCKING
- * caller should hold the mode config lock.
- *
- */
-int intelfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
-{
-	struct fb_info *info;
-	struct drm_framebuffer *fb;
-	struct drm_display_mode *mode = crtc->desired_mode;
-
-	fb = crtc->fb;
-	if (!fb)
-		return 1;
-
-	info = fb->fbdev;
-	if (!info)
-		return 1;
-
-	if (!mode)
-		return 1;
-
-	info->var.xres = mode->hdisplay;
-	info->var.right_margin = mode->hsync_start - mode->hdisplay;
-	info->var.hsync_len = mode->hsync_end - mode->hsync_start;
-	info->var.left_margin = mode->htotal - mode->hsync_end;
-	info->var.yres = mode->vdisplay;
-	info->var.lower_margin = mode->vsync_start - mode->vdisplay;
-	info->var.vsync_len = mode->vsync_end - mode->vsync_start;
-	info->var.upper_margin = mode->vtotal - mode->vsync_end;
-	info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
-	/* avoid overflow */
-	info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
-
-	return 0;
-}
-EXPORT_SYMBOL(intelfb_resize);
-
-static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
-			  uint32_t fb_height, uint32_t surface_width,
-			  uint32_t surface_height,
-			  uint32_t surface_depth, uint32_t surface_bpp,
-			  struct drm_framebuffer **fb_p)
+static int intelfb_create(struct drm_device *dev,
+			  struct drm_fb_helper_surface_size *sizes,
+			  struct intel_kernel_fbdev **ifbdev_p)
 {
 	struct fb_info *info;
-	struct intelfb_par *par;
+	struct intel_kernel_fbdev *ifbdev;
 	struct drm_framebuffer *fb;
 	struct intel_framebuffer *intel_fb;
 	struct drm_mode_fb_cmd mode_cmd;
@@ -127,15 +86,15 @@  static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
 	int size, ret, mmio_bar = IS_I9XX(dev) ? 0 : 1;
 
 	/* we don't do packed 24bpp */
-	if (surface_bpp == 24)
-		surface_bpp = 32;
+	if (sizes->surface_bpp == 24)
+		sizes->surface_bpp = 32;
 
-	mode_cmd.width = surface_width;
-	mode_cmd.height = surface_height;
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
 
-	mode_cmd.bpp = surface_bpp;
+	mode_cmd.bpp = sizes->surface_bpp;
 	mode_cmd.pitch = ALIGN(mode_cmd.width * ((mode_cmd.bpp + 1) / 8), 64);
-	mode_cmd.depth = surface_depth;
+	mode_cmd.depth = sizes->surface_depth;
 
 	size = mode_cmd.pitch * mode_cmd.height;
 	size = ALIGN(size, PAGE_SIZE);
@@ -158,28 +117,25 @@  static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
 	/* Flush everything out, we'll be doing GTT only from now on */
 	i915_gem_object_set_to_gtt_domain(fbo, 1);
 
-	ret = intel_framebuffer_create(dev, &mode_cmd, &fb, fbo);
-	if (ret) {
-		DRM_ERROR("failed to allocate fb.\n");
+	info = framebuffer_alloc(sizeof(struct intel_kernel_fbdev), device);
+	if (!info) { 
+		ret = -ENOMEM;
 		goto out_unpin;
 	}
 
-	list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
+	ifbdev = info->par;
+	intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo);
 
-	intel_fb = to_intel_framebuffer(fb);
-	*fb_p = fb;
+	fb = &ifbdev->ifb.base;
 
-	info = framebuffer_alloc(sizeof(struct intelfb_par), device);
-	if (!info) {
-		ret = -ENOMEM;
-		goto out_unpin;
-	}
+	ifbdev->helper.fb = fb;
+	ifbdev->helper.fbdev = info;
+	ifbdev->helper.funcs = &intel_fb_helper_funcs;
+	ifbdev->helper.dev = dev;
 
-	par = info->par;
+	*ifbdev_p = ifbdev;
 
-	par->helper.funcs = &intel_fb_helper_funcs;
-	par->helper.dev = dev;
-	ret = drm_fb_helper_init_crtc_count(&par->helper, 2,
+	ret = drm_fb_helper_init_crtc_count(&ifbdev->helper, 2,
 					    INTELFB_CONN_LIMIT);
 	if (ret)
 		goto out_unref;
@@ -214,7 +170,7 @@  static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
 //	memset(info->screen_base, 0, size);
 
 	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
-	drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
+	drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height);
 
 	/* FIXME: we really shouldn't expose mmio space at all */
 	info->fix.mmio_start = pci_resource_start(dev->pdev, mmio_bar);
@@ -226,15 +182,11 @@  static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
 	info->pixmap.flags = FB_PIXMAP_SYSTEM;
 	info->pixmap.scan_align = 1;
 
-	fb->fbdev = info;
-
-	par->intel_fb = intel_fb;
-
-	/* To allow resizeing without swapping buffers */
 	DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n",
 			intel_fb->base.width, intel_fb->base.height,
 			obj_priv->gtt_offset, fbo);
 
+
 	mutex_unlock(&dev->struct_mutex);
 	vga_switcheroo_client_fb_set(dev->pdev, info);
 	return 0;
@@ -248,35 +200,76 @@  out:
 	return ret;
 }
 
-int intelfb_probe(struct drm_device *dev)
+static int intel_fb_find_or_create_single(struct drm_device *dev,
+					  struct drm_fb_helper_surface_size *sizes,
+					  struct drm_fb_helper **fb_ptr)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	struct intel_kernel_fbdev *ifbdev = NULL;
+	int new_fb = 0;
+	int ret;
+
+	if (!dev_priv->fbdev) {
+		ret = intelfb_create(dev, sizes, 
+				     &ifbdev);
+		if (ret)
+			return ret;
+
+		dev_priv->fbdev = ifbdev;
+		new_fb = 1;
+	} else {
+		ifbdev = dev_priv->fbdev;
+		if (ifbdev->ifb.base.width < sizes->surface_width ||
+		    ifbdev->ifb.base.height < sizes->surface_height) {
+			DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
+			return -EINVAL;
+		}
+	}
+
+	*fb_ptr = &ifbdev->helper;
+	return new_fb;
+}
+
+static int intelfb_probe(struct drm_device *dev)
 {
 	int ret;
 
 	DRM_DEBUG_KMS("\n");
-	ret = drm_fb_helper_single_fb_probe(dev, 32, intelfb_create);
+	ret = drm_fb_helper_single_fb_probe(dev, 32, intel_fb_find_or_create_single);
 	return ret;
 }
-EXPORT_SYMBOL(intelfb_probe);
 
-int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+int intel_fbdev_destroy(struct drm_device *dev,
+			struct intel_kernel_fbdev *ifbdev)
 {
 	struct fb_info *info;
+	struct intel_framebuffer *ifb = &ifbdev->ifb;
 
-	if (!fb)
-		return -EINVAL;
+	info = ifbdev->helper.fbdev;
 
-	info = fb->fbdev;
+	unregister_framebuffer(info);
+	iounmap(info->screen_base);
+	drm_fb_helper_free(&ifbdev->helper);
 
-	if (info) {
-		struct intelfb_par *par = info->par;
-		unregister_framebuffer(info);
-		iounmap(info->screen_base);
-		if (info->par)
-			drm_fb_helper_free(&par->helper);
-		framebuffer_release(info);
-	}
+	drm_framebuffer_cleanup(&ifb->base);
+	drm_gem_object_unreference_unlocked(ifb->obj);
 
+	framebuffer_release(info);
+
+	return 0;
+}
+
+int intel_fbdev_init(struct drm_device *dev)
+{
+	drm_helper_initial_config(dev);
+	intelfb_probe(dev);
 	return 0;
 }
-EXPORT_SYMBOL(intelfb_remove);
+
+void intel_fbdev_fini(struct drm_device *dev)
+{
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	intel_fbdev_destroy(dev, dev_priv->fbdev);
+	dev_priv->fbdev = NULL;
+}
 MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
index cf1c5c0..9d7928f 100644
--- a/drivers/gpu/drm/nouveau/nouveau_display.c
+++ b/drivers/gpu/drm/nouveau/nouveau_display.c
@@ -34,10 +34,6 @@  static void
 nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
 {
 	struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
-	struct drm_device *dev = drm_fb->dev;
-
-	if (drm_fb->fbdev)
-		nouveau_fbcon_remove(dev, drm_fb);
 
 	if (fb->nvbo)
 		drm_gem_object_unreference_unlocked(fb->nvbo->gem);
@@ -61,27 +57,20 @@  static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
 	.create_handle = nouveau_user_framebuffer_create_handle,
 };
 
-struct drm_framebuffer *
-nouveau_framebuffer_create(struct drm_device *dev, struct nouveau_bo *nvbo,
-			   struct drm_mode_fb_cmd *mode_cmd)
+int
+nouveau_framebuffer_init(struct drm_device *dev, struct nouveau_framebuffer *nouveau_fb,
+			 struct drm_mode_fb_cmd *mode_cmd, struct nouveau_bo *nvbo)
 {
-	struct nouveau_framebuffer *fb;
 	int ret;
 
-	fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
-	if (!fb)
-		return NULL;
-
-	ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs);
+	ret = drm_framebuffer_init(dev, &nouveau_fb->base, &nouveau_framebuffer_funcs);
 	if (ret) {
-		kfree(fb);
-		return NULL;
+		return ret;
 	}
 
-	drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
-
-	fb->nvbo = nvbo;
-	return &fb->base;
+	drm_helper_mode_fill_fb_struct(&nouveau_fb->base, mode_cmd);
+	nouveau_fb->nvbo = nvbo;
+	return 0;
 }
 
 static struct drm_framebuffer *
@@ -89,24 +78,28 @@  nouveau_user_framebuffer_create(struct drm_device *dev,
 				struct drm_file *file_priv,
 				struct drm_mode_fb_cmd *mode_cmd)
 {
-	struct drm_framebuffer *fb;
+	struct nouveau_framebuffer *nouveau_fb;
 	struct drm_gem_object *gem;
+	int ret;
 
 	gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
 	if (!gem)
 		return NULL;
 
-	fb = nouveau_framebuffer_create(dev, nouveau_gem_object(gem), mode_cmd);
-	if (!fb) {
+	nouveau_fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
+	if (!nouveau_fb)
+		return NULL;
+
+	ret = nouveau_framebuffer_init(dev, nouveau_fb, mode_cmd, nouveau_gem_object(gem));
+	if (ret) {
 		drm_gem_object_unreference(gem);
 		return NULL;
 	}
 
-	return fb;
+	return &nouveau_fb->base;
 }
 
 const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
 	.fb_create = nouveau_user_framebuffer_create,
-	.fb_changed = nouveau_fbcon_probe,
 };
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
index 1de974a..c6079e3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
@@ -153,7 +153,6 @@  nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
 	struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
 	struct nouveau_channel *chan;
 	struct drm_crtc *crtc;
-	uint32_t fbdev_flags;
 	int ret, i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
@@ -163,8 +162,7 @@  nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
 		return 0;
 
 	NV_INFO(dev, "Disabling fbcon acceleration...\n");
-	fbdev_flags = dev_priv->fbdev_info->flags;
-	dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
+	nouveau_fbcon_save_disable_accel(dev);
 
 	NV_INFO(dev, "Unpinning framebuffer(s)...\n");
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
@@ -230,9 +228,9 @@  nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
 	}
 
 	acquire_console_sem();
-	fb_set_suspend(dev_priv->fbdev_info, 1);
+	nouveau_fbcon_set_suspend(dev, 1);
 	release_console_sem();
-	dev_priv->fbdev_info->flags = fbdev_flags;
+	nouveau_fbcon_restore_accel(dev);
 	return 0;
 
 out_abort:
@@ -250,14 +248,12 @@  nouveau_pci_resume(struct pci_dev *pdev)
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_engine *engine = &dev_priv->engine;
 	struct drm_crtc *crtc;
-	uint32_t fbdev_flags;
 	int ret, i;
 
 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
 		return -ENODEV;
 
-	fbdev_flags = dev_priv->fbdev_info->flags;
-	dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
+	nouveau_fbcon_save_disable_accel(dev);
 
 	NV_INFO(dev, "We're back, enabling device...\n");
 	pci_set_power_state(pdev, PCI_D0);
@@ -332,13 +328,14 @@  nouveau_pci_resume(struct pci_dev *pdev)
 	}
 
 	acquire_console_sem();
-	fb_set_suspend(dev_priv->fbdev_info, 0);
+	nouveau_fbcon_set_suspend(dev, 0);
 	release_console_sem();
 
-	nouveau_fbcon_zfill(dev);
+	nouveau_fbcon_zfill_all(dev);
 
 	drm_helper_resume_force_mode(dev);
-	dev_priv->fbdev_info->flags = fbdev_flags;
+
+	nouveau_fbcon_restore_accel(dev);
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 5bd8071..a1cb2b1 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -531,7 +531,7 @@  struct drm_nouveau_private {
 		atomic_t validate_sequence;
 	} ttm;
 
-	struct fb_info *fbdev_info;
+  //	struct fb_info *fbdev_info;
 
 	int fifo_alloc_count;
 	struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
@@ -628,6 +628,8 @@  struct drm_nouveau_private {
 	struct {
 		struct dentry *channel_root;
 	} debugfs;
+
+	struct nouveau_fbcon_par *nfbdev;
 };
 
 static inline struct drm_nouveau_private *
diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h
index 4a3f31a..d432134 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fb.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fb.h
@@ -40,8 +40,6 @@  nouveau_framebuffer(struct drm_framebuffer *fb)
 
 extern const struct drm_mode_config_funcs nouveau_mode_config_funcs;
 
-struct drm_framebuffer *
-nouveau_framebuffer_create(struct drm_device *, struct nouveau_bo *,
-			   struct drm_mode_fb_cmd *);
-
+int nouveau_framebuffer_init(struct drm_device *dev, struct nouveau_framebuffer *nouveau_fb,
+			     struct drm_mode_fb_cmd *mode_cmd, struct nouveau_bo *nvbo);
 #endif /* __NOUVEAU_FB_H__ */
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 68cedd9..ccbd0e0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -199,11 +199,10 @@  not_fb:
 }
 #endif
 
-void
-nouveau_fbcon_zfill(struct drm_device *dev)
+static void
+nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbcon_par *fbpar)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct fb_info *info = dev_priv->fbdev_info;
+	struct fb_info *info = fbpar->helper.fbdev;
 	struct fb_fillrect rect;
 
 	/* Clear the entire fbcon.  The drm will program every connector
@@ -219,10 +218,9 @@  nouveau_fbcon_zfill(struct drm_device *dev)
 }
 
 static int
-nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
-		     uint32_t fb_height, uint32_t surface_width,
-		     uint32_t surface_height, uint32_t surface_depth,
-		     uint32_t surface_bpp, struct drm_framebuffer **pfb)
+nouveau_fbcon_create(struct drm_device *dev, 
+		     struct drm_fb_helper_surface_size *sizes,
+		     struct nouveau_fbcon_par **fbpar_p)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct fb_info *info;
@@ -234,13 +232,13 @@  nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
 	struct device *device = &dev->pdev->dev;
 	int size, ret;
 
-	mode_cmd.width = surface_width;
-	mode_cmd.height = surface_height;
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
 
-	mode_cmd.bpp = surface_bpp;
+	mode_cmd.bpp = sizes->surface_bpp;
 	mode_cmd.pitch = mode_cmd.width * (mode_cmd.bpp >> 3);
 	mode_cmd.pitch = roundup(mode_cmd.pitch, 256);
-	mode_cmd.depth = surface_depth;
+	mode_cmd.depth = sizes->surface_depth;
 
 	size = mode_cmd.pitch * mode_cmd.height;
 	size = roundup(size, PAGE_SIZE);
@@ -269,18 +267,6 @@  nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
 
 	mutex_lock(&dev->struct_mutex);
 
-	fb = nouveau_framebuffer_create(dev, nvbo, &mode_cmd);
-	if (!fb) {
-		ret = -ENOMEM;
-		NV_ERROR(dev, "failed to allocate fb.\n");
-		goto out_unref;
-	}
-
-	list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
-
-	nouveau_fb = nouveau_framebuffer(fb);
-	*pfb = fb;
-
 	info = framebuffer_alloc(sizeof(struct nouveau_fbcon_par), device);
 	if (!info) {
 		ret = -ENOMEM;
@@ -288,12 +274,20 @@  nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
 	}
 
 	par = info->par;
+	nouveau_framebuffer_init(dev, &par->nouveau_fb, &mode_cmd, nvbo);
+
+	fb = &par->nouveau_fb.base;
+	/* setup helper */
+	par->helper.fb = fb;
+	par->helper.fbdev = info;
 	par->helper.funcs = &nouveau_fbcon_helper_funcs;
 	par->helper.dev = dev;
+
+	*fbpar_p = par;
+
 	ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4);
 	if (ret)
 		goto out_unref;
-	dev_priv->fbdev_info = info;
 
 	strcpy(info->fix.id, "nouveaufb");
 	if (nouveau_nofbaccel)
@@ -311,7 +305,7 @@  nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
 	info->screen_size = size;
 
 	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
-	drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
+	drm_fb_helper_fill_var(info, &par->helper, sizes->fb_width, sizes->fb_height);
 
 	/* FIXME: we really shouldn't expose mmio space at all */
 	info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
@@ -344,9 +338,6 @@  nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
 	info->pixmap.flags = FB_PIXMAP_SYSTEM;
 	info->pixmap.scan_align = 1;
 
-	fb->fbdev = info;
-
-	par->nouveau_fb = nouveau_fb;
 	par->dev = dev;
 
 	if (dev_priv->channel && !nouveau_nofbaccel) {
@@ -362,7 +353,7 @@  nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
 		};
 	}
 
-	nouveau_fbcon_zfill(dev);
+	nouveau_fbcon_zfill(dev, par);
 
 	/* To allow resizeing without swapping buffers */
 	NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n",
@@ -380,35 +371,59 @@  out:
 	return ret;
 }
 
-int
+static int
+nouveau_fbcon_find_or_create_single(struct drm_device *dev,
+				    struct drm_fb_helper_surface_size *sizes,
+				    struct drm_fb_helper **fb_ptr)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fbcon_par *fbpar;
+	int new_fb = 0;
+	int ret;
+
+	if (!dev_priv->nfbdev) {
+		ret = nouveau_fbcon_create(dev, sizes,
+					   &fbpar);
+		if (ret)
+			return ret;
+		dev_priv->nfbdev = fbpar;
+		new_fb = 1;
+	} else {
+		fbpar = dev_priv->nfbdev;
+		if (fbpar->nouveau_fb.base.width < sizes->surface_width ||
+		    fbpar->nouveau_fb.base.height < sizes->surface_height) {
+			DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
+			return -EINVAL;
+		}
+	}
+	*fb_ptr = &fbpar->helper;
+	return new_fb;
+}
+
+static int
 nouveau_fbcon_probe(struct drm_device *dev)
 {
 	NV_DEBUG_KMS(dev, "\n");
 
-	return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_create);
+	return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_find_or_create_single);
 }
 
 int
-nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbcon_par *fbpar)
 {
-	struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fb);
+	struct nouveau_framebuffer *nouveau_fb = &fbpar->nouveau_fb;
 	struct fb_info *info;
 
-	if (!fb)
-		return -EINVAL;
+	info = fbpar->helper.fbdev;
 
-	info = fb->fbdev;
-	if (info) {
-		struct nouveau_fbcon_par *par = info->par;
+	unregister_framebuffer(info);
+	nouveau_bo_unmap(nouveau_fb->nvbo);
+	drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
+	nouveau_fb->nvbo = NULL;
+	drm_fb_helper_free(&fbpar->helper);
 
-		unregister_framebuffer(info);
-		nouveau_bo_unmap(nouveau_fb->nvbo);
-		drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
-		nouveau_fb->nvbo = NULL;
-		if (par)
-			drm_fb_helper_free(&par->helper);
-		framebuffer_release(info);
-	}
+	drm_framebuffer_cleanup(&nouveau_fb->base);
+	framebuffer_release(info);
 
 	return 0;
 }
@@ -421,3 +436,43 @@  void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 	NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
 	info->flags |= FBINFO_HWACCEL_DISABLED;
 }
+
+int nouveau_fbcon_init(struct drm_device *dev)
+{
+	drm_helper_initial_config(dev);
+	nouveau_fbcon_probe(dev);
+	return 0;
+}
+
+void nouveau_fbcon_fini(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	nouveau_fbcon_destroy(dev, dev_priv->nfbdev);
+	dev_priv->nfbdev = NULL;
+}
+
+void nouveau_fbcon_save_disable_accel(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	dev_priv->nfbdev->saved_flags = dev_priv->nfbdev->helper.fbdev->flags;
+	dev_priv->nfbdev->helper.fbdev->flags |= FBINFO_HWACCEL_DISABLED;
+}
+
+void nouveau_fbcon_restore_accel(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	dev_priv->nfbdev->helper.fbdev->flags = dev_priv->nfbdev->saved_flags;
+}
+
+void nouveau_fbcon_set_suspend(struct drm_device *dev, int state)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	fb_set_suspend(dev_priv->nfbdev->helper.fbdev, state);
+}
+
+void nouveau_fbcon_zfill_all(struct drm_device *dev)
+{
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	nouveau_fbcon_zfill(dev, dev_priv->nfbdev);
+}
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
index f9c34e1..fa66cb9 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
@@ -29,16 +29,16 @@ 
 
 #include "drm_fb_helper.h"
 
+#include "nouveau_fb.h"
 struct nouveau_fbcon_par {
 	struct drm_fb_helper helper;
+	struct nouveau_framebuffer nouveau_fb;
+	struct list_head fbdev_list;
 	struct drm_device *dev;
-	struct nouveau_framebuffer *nouveau_fb;
+	unsigned int saved_flags;
 };
 
-int nouveau_fbcon_probe(struct drm_device *dev);
-int nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb);
 void nouveau_fbcon_restore(void);
-void nouveau_fbcon_zfill(struct drm_device *dev);
 
 void nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
 void nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
@@ -50,5 +50,12 @@  void nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
 int nv50_fbcon_accel_init(struct fb_info *info);
 
 void nouveau_fbcon_gpu_lockup(struct fb_info *info);
+
+int nouveau_fbcon_init(struct drm_device *dev);
+void nouveau_fbcon_fini(struct drm_device *dev);
+void nouveau_fbcon_set_suspend(struct drm_device *dev, int state);
+void nouveau_fbcon_zfill_all(struct drm_device *dev);
+void nouveau_fbcon_save_disable_accel(struct drm_device *dev);
+void nouveau_fbcon_restore_accel(struct drm_device *dev);
 #endif /* __NV50_FBCON_H__ */
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
index 2bd59a9..0aa08b3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_irq.c
+++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
@@ -1203,7 +1203,7 @@  nouveau_irq_handler(DRM_IRQ_ARGS)
 {
 	struct drm_device *dev = (struct drm_device *)arg;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	uint32_t status, fbdev_flags = 0;
+	uint32_t status;
 	unsigned long flags;
 
 	status = nv_rd32(dev, NV03_PMC_INTR_0);
@@ -1212,11 +1212,6 @@  nouveau_irq_handler(DRM_IRQ_ARGS)
 
 	spin_lock_irqsave(&dev_priv->context_switch_lock, flags);
 
-	if (dev_priv->fbdev_info) {
-		fbdev_flags = dev_priv->fbdev_info->flags;
-		dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
-	}
-
 	if (status & NV_PMC_INTR_0_PFIFO_PENDING) {
 		nouveau_fifo_irq_handler(dev);
 		status &= ~NV_PMC_INTR_0_PFIFO_PENDING;
@@ -1246,9 +1241,6 @@  nouveau_irq_handler(DRM_IRQ_ARGS)
 	if (status)
 		NV_ERROR(dev, "Unhandled PMC INTR status bits 0x%08x\n", status);
 
-	if (dev_priv->fbdev_info)
-		dev_priv->fbdev_info->flags = fbdev_flags;
-
 	spin_unlock_irqrestore(&dev_priv->context_switch_lock, flags);
 
 	return IRQ_HANDLED;
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index 58b4680..23e67bf 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -33,6 +33,7 @@ 
 
 #include "nouveau_drv.h"
 #include "nouveau_drm.h"
+#include "nouveau_fbcon.h"
 #include "nv50_display.h"
 
 static void nouveau_stub_takedown(struct drm_device *dev) {}
@@ -511,7 +512,7 @@  nouveau_card_init(struct drm_device *dev)
 	dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
 
 	if (drm_core_check_feature(dev, DRIVER_MODESET))
-		drm_helper_initial_config(dev);
+		nouveau_fbcon_init(dev);
 
 	return 0;
 
@@ -552,6 +553,7 @@  static void nouveau_card_takedown(struct drm_device *dev)
 	NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
 
 	if (dev_priv->init_state != NOUVEAU_CARD_INIT_DOWN) {
+
 		nouveau_backlight_exit(dev);
 
 		if (dev_priv->channel) {
@@ -783,6 +785,7 @@  int nouveau_unload(struct drm_device *dev)
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 
 	if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+		nouveau_fbcon_fini(dev);
 		if (dev_priv->card_type >= NV_50)
 			nv50_display_destroy(dev);
 		else
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index 72ed251..6a2d327 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -936,9 +936,6 @@  struct radeon_device {
 	bool				is_atom_bios;
 	uint16_t			bios_header_start;
 	struct radeon_bo		*stollen_vga_memory;
-	struct fb_info			*fbdev_info;
-	struct radeon_bo		*fbdev_rbo;
-	struct radeon_framebuffer	*fbdev_rfb;
 	/* Register mmio */
 	resource_size_t			rmmio_base;
 	resource_size_t			rmmio_size;
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 153615d..b3deb48 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -670,9 +670,10 @@  int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
 			continue;
 		}
 		robj = rfb->obj->driver_private;
-		if (robj != rdev->fbdev_rbo) {
+		/* don't unpin kernel fb objects */
+		if (!radeon_fbdev_robj_is_fb(rdev, robj)) {
 			r = radeon_bo_reserve(robj, false);
-			if (unlikely(r == 0)) {
+			if (r == 0) {
 				radeon_bo_unpin(robj);
 				radeon_bo_unreserve(robj);
 			}
@@ -697,7 +698,7 @@  int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
 		pci_set_power_state(dev->pdev, PCI_D3hot);
 	}
 	acquire_console_sem();
-	fb_set_suspend(rdev->fbdev_info, 1);
+	radeon_fbdev_set_suspend(rdev, 1);
 	release_console_sem();
 	return 0;
 }
@@ -721,7 +722,7 @@  int radeon_resume_kms(struct drm_device *dev)
 	radeon_agp_resume(rdev);
 	radeon_resume(rdev);
 	radeon_restore_bios_scratch_regs(rdev);
-	fb_set_suspend(rdev->fbdev_info, 0);
+	radeon_fbdev_set_suspend(rdev, 0);
 	release_console_sem();
 
 	/* reset hpd state */
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index d65931d..84aba8a 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -832,10 +832,6 @@  void radeon_compute_pll(struct radeon_pll *pll,
 static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
 {
 	struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb);
-	struct drm_device *dev = fb->dev;
-
-	if (fb->fbdev)
-		radeonfb_remove(dev, fb);
 
 	if (radeon_fb->obj)
 		drm_gem_object_unreference_unlocked(radeon_fb->obj);
@@ -857,21 +853,15 @@  static const struct drm_framebuffer_funcs radeon_fb_funcs = {
 	.create_handle = radeon_user_framebuffer_create_handle,
 };
 
-struct drm_framebuffer *
-radeon_framebuffer_create(struct drm_device *dev,
-			  struct drm_mode_fb_cmd *mode_cmd,
-			  struct drm_gem_object *obj)
+void
+radeon_framebuffer_init(struct drm_device *dev,
+			struct radeon_framebuffer *rfb,
+			struct drm_mode_fb_cmd *mode_cmd,
+			struct drm_gem_object *obj)
 {
-	struct radeon_framebuffer *radeon_fb;
-
-	radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL);
-	if (radeon_fb == NULL) {
-		return NULL;
-	}
-	drm_framebuffer_init(dev, &radeon_fb->base, &radeon_fb_funcs);
-	drm_helper_mode_fill_fb_struct(&radeon_fb->base, mode_cmd);
-	radeon_fb->obj = obj;
-	return &radeon_fb->base;
+	rfb->obj = obj;
+	drm_framebuffer_init(dev, &rfb->base, &radeon_fb_funcs);
+	drm_helper_mode_fill_fb_struct(&rfb->base, mode_cmd);
 }
 
 static struct drm_framebuffer *
@@ -880,6 +870,7 @@  radeon_user_framebuffer_create(struct drm_device *dev,
 			       struct drm_mode_fb_cmd *mode_cmd)
 {
 	struct drm_gem_object *obj;
+	struct radeon_framebuffer *radeon_fb;
 
 	obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
 	if (obj ==  NULL) {
@@ -887,12 +878,19 @@  radeon_user_framebuffer_create(struct drm_device *dev,
 			"can't create framebuffer\n", mode_cmd->handle);
 		return NULL;
 	}
-	return radeon_framebuffer_create(dev, mode_cmd, obj);
+
+	radeon_fb = kzalloc(sizeof(*radeon_fb), GFP_KERNEL);
+	if (radeon_fb == NULL) {
+		return NULL;
+	}
+
+	radeon_framebuffer_init(dev, radeon_fb, mode_cmd, obj);
+
+	return &radeon_fb->base;
 }
 
 static const struct drm_mode_config_funcs radeon_mode_funcs = {
 	.fb_create = radeon_user_framebuffer_create,
-	.fb_changed = radeonfb_probe,
 };
 
 struct drm_prop_enum_list {
@@ -1015,12 +1013,14 @@  int radeon_modeset_init(struct radeon_device *rdev)
 	}
 	/* initialize hpd */
 	radeon_hpd_init(rdev);
-	drm_helper_initial_config(rdev->ddev);
+
+	radeon_fbdev_init(rdev);
 	return 0;
 }
 
 void radeon_modeset_fini(struct radeon_device *rdev)
 {
+	radeon_fbdev_fini(rdev);
 	kfree(rdev->mode_info.bios_hardcoded_edid);
 
 	if (rdev->mode_info.mode_config_initialized) {
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 8fccbf2..06955ef 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -41,10 +41,15 @@ 
 
 #include <linux/vga_switcheroo.h>
 
-struct radeon_fb_device {
+/* object hierarchy - 
+   this contains a helper + a radeon fb
+   the helper contains a pointer to radeon framebuffer baseclass.
+*/
+struct radeon_kernel_fbdev {
 	struct drm_fb_helper helper;
-	struct radeon_framebuffer	*rfb;
-	struct radeon_device		*rdev;
+	struct radeon_framebuffer rfb;
+	struct list_head fbdev_list;
+	struct radeon_device *rdev;
 };
 
 static struct fb_ops radeonfb_ops = {
@@ -60,45 +65,6 @@  static struct fb_ops radeonfb_ops = {
 	.fb_setcmap = drm_fb_helper_setcmap,
 };
 
-/**
- * Currently it is assumed that the old framebuffer is reused.
- *
- * LOCKING
- * caller should hold the mode config lock.
- *
- */
-int radeonfb_resize(struct drm_device *dev, struct drm_crtc *crtc)
-{
-	struct fb_info *info;
-	struct drm_framebuffer *fb;
-	struct drm_display_mode *mode = crtc->desired_mode;
-
-	fb = crtc->fb;
-	if (fb == NULL) {
-		return 1;
-	}
-	info = fb->fbdev;
-	if (info == NULL) {
-		return 1;
-	}
-	if (mode == NULL) {
-		return 1;
-	}
-	info->var.xres = mode->hdisplay;
-	info->var.right_margin = mode->hsync_start - mode->hdisplay;
-	info->var.hsync_len = mode->hsync_end - mode->hsync_start;
-	info->var.left_margin = mode->htotal - mode->hsync_end;
-	info->var.yres = mode->vdisplay;
-	info->var.lower_margin = mode->vsync_start - mode->vdisplay;
-	info->var.vsync_len = mode->vsync_end - mode->vsync_start;
-	info->var.upper_margin = mode->vtotal - mode->vsync_end;
-	info->var.pixclock = 10000000 / mode->htotal * 1000 / mode->vtotal * 100;
-	/* avoid overflow */
-	info->var.pixclock = info->var.pixclock * 1000 / mode->vrefresh;
-
-	return 0;
-}
-EXPORT_SYMBOL(radeonfb_resize);
 
 static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bool tiled)
 {
@@ -129,17 +95,14 @@  static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
 	.gamma_get = radeon_crtc_fb_gamma_get,
 };
 
-int radeonfb_create(struct drm_device *dev,
-		    uint32_t fb_width, uint32_t fb_height,
-		    uint32_t surface_width, uint32_t surface_height,
-		    uint32_t surface_depth, uint32_t surface_bpp,
-		    struct drm_framebuffer **fb_p)
+static int radeonfb_create(struct drm_device *dev,
+			   struct drm_fb_helper_surface_size *sizes,
+			   struct radeon_kernel_fbdev **rfbdev_p)
 {
 	struct radeon_device *rdev = dev->dev_private;
 	struct fb_info *info;
-	struct radeon_fb_device *rfbdev;
+	struct radeon_kernel_fbdev *rfbdev;
 	struct drm_framebuffer *fb = NULL;
-	struct radeon_framebuffer *rfb;
 	struct drm_mode_fb_cmd mode_cmd;
 	struct drm_gem_object *gobj = NULL;
 	struct radeon_bo *rbo = NULL;
@@ -151,17 +114,17 @@  int radeonfb_create(struct drm_device *dev,
 	bool fb_tiled = false; /* useful for testing */
 	u32 tiling_flags = 0;
 
-	mode_cmd.width = surface_width;
-	mode_cmd.height = surface_height;
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
 
 	/* avivo can't scanout real 24bpp */
-	if ((surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
-		surface_bpp = 32;
+	if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
+		sizes->surface_bpp = 32;
 
-	mode_cmd.bpp = surface_bpp;
+	mode_cmd.bpp = sizes->surface_bpp;
 	/* need to align pitch with crtc limits */
 	mode_cmd.pitch = radeon_align_pitch(rdev, mode_cmd.width, mode_cmd.bpp, fb_tiled) * ((mode_cmd.bpp + 1) / 8);
-	mode_cmd.depth = surface_depth;
+	mode_cmd.depth = sizes->surface_depth;
 
 	size = mode_cmd.pitch * mode_cmd.height;
 	aligned_size = ALIGN(size, PAGE_SIZE);
@@ -172,7 +135,7 @@  int radeonfb_create(struct drm_device *dev,
 			&gobj);
 	if (ret) {
 		printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
-		       surface_width, surface_height);
+		       sizes->surface_width, sizes->surface_height);
 		ret = -ENOMEM;
 		goto out;
 	}
@@ -201,12 +164,7 @@  int radeonfb_create(struct drm_device *dev,
 			dev_err(rdev->dev, "FB failed to set tiling flags\n");
 	}
 	mutex_lock(&rdev->ddev->struct_mutex);
-	fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj);
-	if (fb == NULL) {
-		DRM_ERROR("failed to allocate fb.\n");
-		ret = -ENOMEM;
-		goto out_unref;
-	}
+
 	ret = radeon_bo_reserve(rbo, false);
 	if (unlikely(ret != 0))
 		goto out_unref;
@@ -223,23 +181,25 @@  int radeonfb_create(struct drm_device *dev,
 		goto out_unref;
 	}
 
-	list_add(&fb->filp_head, &rdev->ddev->mode_config.fb_kernel_list);
-
-	*fb_p = fb;
-	rfb = to_radeon_framebuffer(fb);
-	rdev->fbdev_rfb = rfb;
-	rdev->fbdev_rbo = rbo;
-
-	info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
+	info = framebuffer_alloc(sizeof(struct radeon_kernel_fbdev), device);
 	if (info == NULL) {
 		ret = -ENOMEM;
 		goto out_unref;
 	}
-
-	rdev->fbdev_info = info;
+	
 	rfbdev = info->par;
+	rfbdev->rdev = rdev;
+	radeon_framebuffer_init(dev, &rfbdev->rfb, &mode_cmd, gobj);
+	fb = &rfbdev->rfb.base;
+
+	/* setup helper */
+	rfbdev->helper.fb = fb;
+	rfbdev->helper.fbdev = info;
 	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
 	rfbdev->helper.dev = dev;
+
+	*rfbdev_p = rfbdev;
+
 	ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, rdev->num_crtc,
 					    RADEONFB_CONN_LIMIT);
 	if (ret)
@@ -260,7 +220,7 @@  int radeonfb_create(struct drm_device *dev,
 	info->screen_base = fbptr;
 	info->screen_size = size;
 
-	drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
+	drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
 
 	/* setup aperture base/size for vesafb takeover */
 	info->aperture_base = rdev->ddev->mode_config.fb_base;
@@ -283,9 +243,6 @@  int radeonfb_create(struct drm_device *dev,
 	DRM_INFO("fb depth is %d\n", fb->depth);
 	DRM_INFO("   pitch is %d\n", fb->pitch);
 
-	fb->fbdev = info;
-	rfbdev->rfb = rfb;
-	rfbdev->rdev = rdev;
 
 	mutex_unlock(&rdev->ddev->struct_mutex);
 	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
@@ -300,7 +257,6 @@  out_unref:
 		}
 	}
 	if (fb && ret) {
-		list_del(&fb->filp_head);
 		drm_gem_object_unreference(gobj);
 		drm_framebuffer_cleanup(fb);
 		kfree(fb);
@@ -311,6 +267,35 @@  out:
 	return ret;
 }
 
+static int radeon_fb_find_or_create_single(struct drm_device *dev,
+					   struct drm_fb_helper_surface_size *sizes,
+					   struct drm_fb_helper **fb_ptr)
+{
+	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_kernel_fbdev *rfbdev = NULL;
+	int new_fb = 0;
+	int ret;
+
+	if (!rdev->mode_info.rfbdev) {
+		ret = radeonfb_create(dev, sizes,
+				      &rfbdev);
+		if (ret)
+			return ret;
+		rdev->mode_info.rfbdev = rfbdev;
+		new_fb = 1;
+	} else {
+		rfbdev = rdev->mode_info.rfbdev;
+		if (rfbdev->rfb.base.width < sizes->surface_width ||
+		    rfbdev->rfb.base.height < sizes->surface_height) {
+			DRM_ERROR("Framebuffer not large enough to scale console onto.\n");
+			return -EINVAL;
+		}
+	}
+
+	*fb_ptr = &rfbdev->helper;
+	return new_fb;
+}
+
 static char *mode_option;
 int radeon_parse_options(char *options)
 {
@@ -327,7 +312,7 @@  int radeon_parse_options(char *options)
 	return 0;
 }
 
-int radeonfb_probe(struct drm_device *dev)
+static int radeonfb_probe(struct drm_device *dev)
 {
 	struct radeon_device *rdev = dev->dev_private;
 	int bpp_sel = 32;
@@ -336,37 +321,76 @@  int radeonfb_probe(struct drm_device *dev)
 	if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
 		bpp_sel = 8;
 
-	return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeonfb_create);
+	return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeon_fb_find_or_create_single);
+}
+
+void radeonfb_hotplug(struct drm_device *dev)
+{
+	drm_helper_fb_hotplug_event(dev);
+
+	radeonfb_probe(dev);
 }
 
-int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_kernel_fbdev *rfbdev)
 {
 	struct fb_info *info;
-	struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb);
+	struct radeon_framebuffer *rfb = &rfbdev->rfb;
 	struct radeon_bo *rbo;
 	int r;
 
-	if (!fb) {
-		return -EINVAL;
-	}
-	info = fb->fbdev;
-	if (info) {
-		struct radeon_fb_device *rfbdev = info->par;
-		rbo = rfb->obj->driver_private;
-		unregister_framebuffer(info);
-		r = radeon_bo_reserve(rbo, false);
-		if (likely(r == 0)) {
-			radeon_bo_kunmap(rbo);
-			radeon_bo_unpin(rbo);
-			radeon_bo_unreserve(rbo);
-		}
-		drm_fb_helper_free(&rfbdev->helper);
-		framebuffer_release(info);
+	rbo = rfb->obj->driver_private;
+	info = rfbdev->helper.fbdev;
+	unregister_framebuffer(info);
+	r = radeon_bo_reserve(rbo, false);
+	if (likely(r == 0)) {
+		radeon_bo_kunmap(rbo);
+		radeon_bo_unpin(rbo);
+		radeon_bo_unreserve(rbo);
 	}
 
-	printk(KERN_INFO "unregistered panic notifier\n");
+	drm_fb_helper_free(&rfbdev->helper);
+	drm_framebuffer_cleanup(&rfb->base);
+	if (rfb->obj)
+		drm_gem_object_unreference_unlocked(rfb->obj);
+
+	framebuffer_release(info);
 
 	return 0;
 }
-EXPORT_SYMBOL(radeonfb_remove);
 MODULE_LICENSE("GPL");
+
+int radeon_fbdev_init(struct radeon_device *rdev)
+{
+	drm_helper_initial_config(rdev->ddev);
+	radeonfb_probe(rdev->ddev);
+	return 0;
+}
+
+void radeon_fbdev_fini(struct radeon_device *rdev)
+{
+	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
+	rdev->mode_info.rfbdev = NULL;
+}
+
+void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state)
+{
+	fb_set_suspend(rdev->mode_info.rfbdev->helper.fbdev, state);
+}
+
+int radeon_fbdev_total_size(struct radeon_device *rdev)
+{
+	struct radeon_bo *robj;
+	int size = 0;
+
+	robj = rdev->mode_info.rfbdev->rfb.obj->driver_private;
+	size += radeon_bo_size(robj);
+	return size;
+}
+
+bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
+{
+	if (robj == rdev->mode_info.rfbdev->rfb.obj->driver_private)
+		return true;
+	return false;
+}
+
diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
index ef92d14..28dd3e1 100644
--- a/drivers/gpu/drm/radeon/radeon_gem.c
+++ b/drivers/gpu/drm/radeon/radeon_gem.c
@@ -158,8 +158,7 @@  int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
 	args->vram_visible = rdev->mc.real_vram_size;
 	if (rdev->stollen_vga_memory)
 		args->vram_visible -= radeon_bo_size(rdev->stollen_vga_memory);
-	if (rdev->fbdev_rbo)
-		args->vram_visible -= radeon_bo_size(rdev->fbdev_rbo);
+	args->vram_visible -= radeon_fbdev_total_size(rdev);
 	args->gart_size = rdev->mc.gtt_size - rdev->cp.ring_size - 4096 -
 		RADEON_IB_POOL_SIZE*64*1024;
 	return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 68519b4..19ed835 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -55,6 +55,8 @@  static void radeon_hotplug_work_func(struct work_struct *work)
 			radeon_connector_hotplug(connector);
 	}
 	/* Just fire off a uevent and let userspace tell us what to do */
+	radeonfb_hotplug(dev);
+
 	drm_sysfs_hotplug_event(dev);
 }
 
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 55a4175..9f088c6 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -39,6 +39,7 @@ 
 #include <linux/i2c-algo-bit.h>
 #include "radeon_fixed.h"
 
+struct radeon_bo;
 struct radeon_device;
 
 #define to_radeon_crtc(x) container_of(x, struct radeon_crtc, base)
@@ -207,6 +208,8 @@  enum radeon_dvo_chip {
 	DVO_SIL1178,
 };
 
+struct radeon_kernel_fbdev;
+
 struct radeon_mode_info {
 	struct atom_context *atom_context;
 	struct card_info *atom_card_info;
@@ -223,6 +226,9 @@  struct radeon_mode_info {
 	struct drm_property *tmds_pll_property;
 	/* hardcoded DFP edid from BIOS */
 	struct edid *bios_hardcoded_edid;
+
+	/* pointer to fbdev info structure */
+	struct radeon_kernel_fbdev *rfbdev;
 };
 
 #define MAX_H_CODE_TIMING_LEN 32
@@ -538,11 +544,10 @@  extern void radeon_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
 				     u16 blue, int regno);
 extern void radeon_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 				     u16 *blue, int regno);
-struct drm_framebuffer *radeon_framebuffer_create(struct drm_device *dev,
-						  struct drm_mode_fb_cmd *mode_cmd,
-						  struct drm_gem_object *obj);
-
-int radeonfb_probe(struct drm_device *dev);
+void radeon_framebuffer_init(struct drm_device *dev,
+			     struct radeon_framebuffer *rfb,
+			     struct drm_mode_fb_cmd *mode_cmd,
+			     struct drm_gem_object *obj);
 
 int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb);
 bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev);
@@ -579,4 +584,12 @@  void radeon_legacy_tv_adjust_pll2(struct drm_encoder *encoder,
 void radeon_legacy_tv_mode_set(struct drm_encoder *encoder,
 			       struct drm_display_mode *mode,
 			       struct drm_display_mode *adjusted_mode);
+
+/* fbdev layer */
+int radeon_fbdev_init(struct radeon_device *rdev);
+void radeon_fbdev_fini(struct radeon_device *rdev);
+void radeon_fbdev_set_suspend(struct radeon_device *rdev, int state);
+int radeon_fbdev_total_size(struct radeon_device *rdev);
+bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj);
+void radeonfb_hotplug(struct drm_device *dev);
 #endif
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
index 31f9afe..bbc7c4c 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
@@ -752,14 +752,8 @@  err_not_scanout:
 	return NULL;
 }
 
-static int vmw_kms_fb_changed(struct drm_device *dev)
-{
-	return 0;
-}
-
 static struct drm_mode_config_funcs vmw_kms_funcs = {
 	.fb_create = vmw_kms_fb_create,
-	.fb_changed = vmw_kms_fb_changed,
 };
 
 int vmw_kms_init(struct vmw_private *dev_priv)
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 1347524..c70814b 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -271,8 +271,6 @@  struct drm_framebuffer {
 	unsigned int depth;
 	int bits_per_pixel;
 	int flags;
-	struct fb_info *fbdev;
-	u32 pseudo_palette[17];
 	struct list_head filp_head;
 	/* if you are using the helper */
 	void *helper_private;
@@ -548,16 +546,9 @@  struct drm_mode_set {
 
 /**
  * struct drm_mode_config_funcs - configure CRTCs for a given screen layout
- * @resize: adjust CRTCs as necessary for the proposed layout
- *
- * Currently only a resize hook is available.  DRM will call back into the
- * driver with a new screen width and height.  If the driver can't support
- * the proposed size, it can return false.  Otherwise it should adjust
- * the CRTC<->connector mappings as needed and update its view of the screen.
  */
 struct drm_mode_config_funcs {
 	struct drm_framebuffer *(*fb_create)(struct drm_device *dev, struct drm_file *file_priv, struct drm_mode_fb_cmd *mode_cmd);
-	int (*fb_changed)(struct drm_device *dev);
 };
 
 struct drm_mode_group {
@@ -590,9 +581,6 @@  struct drm_mode_config {
 
 	struct list_head property_list;
 
-	/* in-kernel framebuffers - hung of filp_head in drm_framebuffer */
-	struct list_head fb_kernel_list;
-
 	int min_width, min_height;
 	int max_width, max_height;
 	struct drm_mode_config_funcs *funcs;
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index b29e201..ce7aab7 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -97,7 +97,6 @@  struct drm_connector_helper_funcs {
 extern int drm_helper_probe_single_connector_modes(struct drm_connector *connector, uint32_t maxX, uint32_t maxY);
 extern void drm_helper_disable_unused_functions(struct drm_device *dev);
 extern int drm_helper_hotplug_stage_two(struct drm_device *dev);
-extern bool drm_helper_initial_config(struct drm_device *dev);
 extern int drm_crtc_helper_set_config(struct drm_mode_set *set);
 extern bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
 				     struct drm_display_mode *mode,
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 58c892a..38ab0da 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -57,6 +57,15 @@  struct drm_fb_helper_cmdline_mode {
 	bool margins;
 };
 
+struct drm_fb_helper_surface_size {
+	u32 fb_width;
+	u32 fb_height;
+	u32 surface_width;
+	u32 surface_height;
+	u32 surface_bpp;
+	u32 surface_depth;
+};
+
 struct drm_fb_helper_connector {
 	struct drm_fb_helper_cmdline_mode cmdline_mode;
 };
@@ -69,19 +78,16 @@  struct drm_fb_helper {
 	struct drm_fb_helper_crtc *crtc_info;
 	struct drm_fb_helper_funcs *funcs;
 	int conn_limit;
+	struct fb_info *fbdev;
+	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
 };
 
 int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 				  int preferred_bpp,
 				  int (*fb_create)(struct drm_device *dev,
-						   uint32_t fb_width,
-						   uint32_t fb_height,
-						   uint32_t surface_width,
-						   uint32_t surface_height,
-						   uint32_t surface_depth,
-						   uint32_t surface_bpp,
-						   struct drm_framebuffer **fb_ptr));
+						   struct drm_fb_helper_surface_size *sizes,
+						   struct drm_fb_helper **fb_ptr));
 int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count,
 				  int max_conn);
 void drm_fb_helper_free(struct drm_fb_helper *helper);
@@ -99,7 +105,7 @@  int drm_fb_helper_setcolreg(unsigned regno,
 			    struct fb_info *info);
 
 void drm_fb_helper_restore(void);
-void drm_fb_helper_fill_var(struct fb_info *info, struct drm_framebuffer *fb,
+void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helper,
 			    uint32_t fb_width, uint32_t fb_height);
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 			    uint32_t depth);
@@ -108,4 +114,6 @@  int drm_fb_helper_add_connector(struct drm_connector *connector);
 int drm_fb_helper_parse_command_line(struct drm_device *dev);
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
 
+bool drm_helper_fb_hotplug_event(struct drm_device *dev);
+bool drm_helper_initial_config(struct drm_device *dev);
 #endif