All of lore.kernel.org
 help / color / mirror / Atom feed
* drm/kms/fb: clean up fbdev integration + add fbcon polling/hotplug
@ 2010-03-30  5:34 Dave Airlie
  2010-03-30  5:34 ` [PATCH 1/6] drm/fb: fix fbdev object model + cleanup properly Dave Airlie
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Airlie @ 2010-03-30  5:34 UTC (permalink / raw)
  To: dri-devel

This series of 6 patches attempts to clean up the KMS fbdev helper layer,
and add support for two features I'd like to see for some use cases.

The first 3 patches are mainly cleanup and moving code around, the main
idea being to better abstract the fbdev helper layer from the main kms
core. This is done by trying to make fbdev into a kms user not reliant
on any information stored in kms especially for it, it also makes the fbdev
layer own the initial crtc/mode programming.

The second set of 3 patches add the features:
a) default to making 1024x768 framebuffer if we detect nothing plugged in
b) if nothing is plugged in and the driver wants it, poll ever 5 seconds
for something, until you find something plugged in, stop polling then.
c) fbcon hotplug, if you have hot plug detect on irqs and you are at the
console, it will redetect when you hotplug something. It also deals
properly when X or something else is running and delays reprobing until
next set_par.

Dave.


------------------------------------------------------------------------------
Download Intel® Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [PATCH 1/6] drm/fb: fix fbdev object model + cleanup properly.
  2010-03-30  5:34 drm/kms/fb: clean up fbdev integration + add fbcon polling/hotplug Dave Airlie
@ 2010-03-30  5:34 ` Dave Airlie
  2010-03-30  5:34   ` [PATCH 2/6] drm/kms/fb: move to using fb helper crtc grouping instead of core crtc list Dave Airlie
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Airlie @ 2010-03-30  5:34 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

The fbdev layer in the kms code should act like a consumer of the kms services and avoid having relying on information being store in the kms core structures in order for it to work.

This patch

a) removes the info pointer/psuedo palette from the core drm_framebuffer structure and moves it to the fbdev helper layer, it also removes the core drm keeping a list of kernel kms fbdevs.
b) migrated all the fb helper functions out of the crtc helper file into the fb helper file.
c) pushed the fb probing/hotplug control into the driver
d) makes the surface sizes into a structure for ease of passing
This changes the intel/radeon/nouveau drivers to use the new helper.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/drm_crtc.c                |    1 -
 drivers/gpu/drm/drm_crtc_helper.c         |  391 +------------------------
 drivers/gpu/drm/drm_fb_helper.c           |  469 +++++++++++++++++++++++++----
 drivers/gpu/drm/i915/i915_dma.c           |    2 +-
 drivers/gpu/drm/i915/i915_drv.h           |    4 +
 drivers/gpu/drm/i915/intel_display.c      |   37 +--
 drivers/gpu/drm/i915/intel_drv.h          |   13 +-
 drivers/gpu/drm/i915/intel_fb.c           |  175 +++++------
 drivers/gpu/drm/nouveau/nouveau_display.c |   41 +--
 drivers/gpu/drm/nouveau/nouveau_drv.c     |   19 +-
 drivers/gpu/drm/nouveau/nouveau_drv.h     |    4 +-
 drivers/gpu/drm/nouveau/nouveau_fb.h      |    6 +-
 drivers/gpu/drm/nouveau/nouveau_fbcon.c   |  149 +++++++---
 drivers/gpu/drm/nouveau/nouveau_fbcon.h   |   15 +-
 drivers/gpu/drm/nouveau/nouveau_irq.c     |   10 +-
 drivers/gpu/drm/nouveau/nouveau_state.c   |    5 +-
 drivers/gpu/drm/radeon/radeon.h           |    3 -
 drivers/gpu/drm/radeon/radeon_device.c    |    9 +-
 drivers/gpu/drm/radeon/radeon_display.c   |   42 ++--
 drivers/gpu/drm/radeon/radeon_fb.c        |  222 ++++++++------
 drivers/gpu/drm/radeon/radeon_gem.c       |    3 +-
 drivers/gpu/drm/radeon/radeon_irq_kms.c   |    2 +
 drivers/gpu/drm/radeon/radeon_mode.h      |   23 ++-
 drivers/gpu/drm/vmwgfx/vmwgfx_kms.c       |    6 -
 include/drm/drm_crtc.h                    |   12 -
 include/drm/drm_crtc_helper.h             |    1 -
 include/drm/drm_fb_helper.h               |   24 +-
 27 files changed, 852 insertions(+), 836 deletions(-)

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
-- 
1.6.5.2


------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 2/6] drm/kms/fb: move to using fb helper crtc grouping instead of core crtc list
  2010-03-30  5:34 ` [PATCH 1/6] drm/fb: fix fbdev object model + cleanup properly Dave Airlie
@ 2010-03-30  5:34   ` Dave Airlie
  2010-03-30  5:34     ` [PATCH 3/6] drm/kms/fb: separate fbdev connector list from core drm connectors Dave Airlie
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Airlie @ 2010-03-30  5:34 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

This move to using the list of crtcs in the fb helper and cleans up the
whole picking code, now we store the crtc/connectors we want directly
into the modeset and we use the modeset directly to set the mode.

Fixes from James Simmons and Ben Skeggs.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/drm_crtc_helper.c       |    4 -
 drivers/gpu/drm/drm_fb_helper.c         |  329 +++++++++++++------------------
 drivers/gpu/drm/i915/i915_drv.h         |    5 +-
 drivers/gpu/drm/i915/intel_fb.c         |   93 ++++-----
 drivers/gpu/drm/nouveau/nouveau_drv.h   |    4 +-
 drivers/gpu/drm/nouveau/nouveau_fbcon.c |  121 ++++++------
 drivers/gpu/drm/nouveau/nouveau_fbcon.h |    2 +-
 drivers/gpu/drm/nouveau/nv04_fbcon.c    |   16 +-
 drivers/gpu/drm/nouveau/nv50_fbcon.c    |   16 +-
 drivers/gpu/drm/radeon/radeon_fb.c      |  240 ++++++++++++----------
 drivers/gpu/drm/radeon/radeon_mode.h    |    4 +-
 include/drm/drm_crtc.h                  |    5 -
 include/drm/drm_fb_helper.h             |   21 ++-
 13 files changed, 411 insertions(+), 449 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
index 9d23f54..b142ac2 100644
--- a/drivers/gpu/drm/drm_crtc_helper.c
+++ b/drivers/gpu/drm/drm_crtc_helper.c
@@ -625,10 +625,6 @@ int drm_crtc_helper_set_config(struct drm_mode_set *set)
 				ret = -EINVAL;
 				goto fail;
 			}
-			/* TODO are these needed? */
-			set->crtc->desired_x = set->x;
-			set->crtc->desired_y = set->y;
-			set->crtc->desired_mode = set->mode;
 		}
 		drm_helper_disable_unused_functions(dev);
 	} else if (fb_changed) {
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index d6f9f94..3d6f417 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -290,6 +290,7 @@ static void drm_fb_helper_on(struct fb_info *info)
 	struct drm_fb_helper *fb_helper = info->par;
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_crtc *crtc;
+	struct drm_crtc_helper_funcs *crtc_funcs;
 	struct drm_encoder *encoder;
 	int i;
 
@@ -297,33 +298,28 @@ static void drm_fb_helper_on(struct fb_info *info)
 	 * For each CRTC in this fb, turn the crtc on then,
 	 * find all associated encoders and turn them on.
 	 */
+	mutex_lock(&dev->mode_config.mutex);
 	for (i = 0; i < fb_helper->crtc_count; i++) {
-		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-			struct drm_crtc_helper_funcs *crtc_funcs =
-				crtc->helper_private;
+		crtc = fb_helper->crtc_info[i].mode_set.crtc;
+		crtc_funcs = crtc->helper_private;
 
-			/* Only mess with CRTCs in this fb */
-			if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
-			    !crtc->enabled)
-				continue;
+		if (!crtc->enabled)
+			continue;
+
+		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
 
-			mutex_lock(&dev->mode_config.mutex);
-			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_ON);
-			mutex_unlock(&dev->mode_config.mutex);
 
-			/* Found a CRTC on this fb, now find encoders */
-			list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-				if (encoder->crtc == crtc) {
-					struct drm_encoder_helper_funcs *encoder_funcs;
+		/* Found a CRTC on this fb, now find encoders */
+		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+			if (encoder->crtc == crtc) {
+				struct drm_encoder_helper_funcs *encoder_funcs;
 
-					encoder_funcs = encoder->helper_private;
-					mutex_lock(&dev->mode_config.mutex);
-					encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
-					mutex_unlock(&dev->mode_config.mutex);
-				}
+				encoder_funcs = encoder->helper_private;
+				encoder_funcs->dpms(encoder, DRM_MODE_DPMS_ON);
 			}
 		}
 	}
+	mutex_unlock(&dev->mode_config.mutex);
 }
 
 static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
@@ -331,6 +327,7 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
 	struct drm_fb_helper *fb_helper = info->par;
 	struct drm_device *dev = fb_helper->dev;
 	struct drm_crtc *crtc;
+	struct drm_crtc_helper_funcs *crtc_funcs;
 	struct drm_encoder *encoder;
 	int i;
 
@@ -338,32 +335,26 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
 	 * For each CRTC in this fb, find all associated encoders
 	 * and turn them off, then turn off the CRTC.
 	 */
+	mutex_lock(&dev->mode_config.mutex);
 	for (i = 0; i < fb_helper->crtc_count; i++) {
-		list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-			struct drm_crtc_helper_funcs *crtc_funcs =
-				crtc->helper_private;
+		crtc = fb_helper->crtc_info[i].mode_set.crtc;
+		crtc_funcs = crtc->helper_private;
 
-			/* Only mess with CRTCs in this fb */
-			if (crtc->base.id != fb_helper->crtc_info[i].crtc_id ||
-			    !crtc->enabled)
-				continue;
+		if (!crtc->enabled)
+			continue;
 
-			/* Found a CRTC on this fb, now find encoders */
-			list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
-				if (encoder->crtc == crtc) {
-					struct drm_encoder_helper_funcs *encoder_funcs;
+		/* Found a CRTC on this fb, now find encoders */
+		list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+			if (encoder->crtc == crtc) {
+				struct drm_encoder_helper_funcs *encoder_funcs;
 
-					encoder_funcs = encoder->helper_private;
-					mutex_lock(&dev->mode_config.mutex);
-					encoder_funcs->dpms(encoder, dpms_mode);
-					mutex_unlock(&dev->mode_config.mutex);
-				}
+				encoder_funcs = encoder->helper_private;
+				encoder_funcs->dpms(encoder, dpms_mode);
 			}
-			mutex_lock(&dev->mode_config.mutex);
-			crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
-			mutex_unlock(&dev->mode_config.mutex);
 		}
+		crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
 	}
+	mutex_unlock(&dev->mode_config.mutex);
 }
 
 int drm_fb_helper_blank(int blank, struct fb_info *info)
@@ -403,17 +394,19 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 	kfree(helper->crtc_info);
 }
 
-int drm_fb_helper_init_crtc_count(struct drm_fb_helper *helper, int crtc_count, int max_conn_count)
+int drm_fb_helper_init_crtc_count(struct drm_device *dev,
+				  struct drm_fb_helper *helper,
+				  int crtc_count, int max_conn_count)
 {
-	struct drm_device *dev = helper->dev;
 	struct drm_crtc *crtc;
 	int ret = 0;
 	int i;
 
+	INIT_LIST_HEAD(&helper->kernel_fb_list);
+	helper->dev = dev;
 	helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
 	if (!helper->crtc_info)
 		return -ENOMEM;
-
 	helper->crtc_count = crtc_count;
 
 	for (i = 0; i < crtc_count; i++) {
@@ -505,20 +498,15 @@ static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info)
 {
 	struct drm_fb_helper *fb_helper = info->par;
-	struct drm_device *dev = fb_helper->dev;
+	struct drm_crtc_helper_funcs *crtc_funcs;
 	u16 *red, *green, *blue, *transp;
 	struct drm_crtc *crtc;
 	int i, rc = 0;
 	int start;
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
-		for (i = 0; i < fb_helper->crtc_count; i++) {
-			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
-				break;
-		}
-		if (i == fb_helper->crtc_count)
-			continue;
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		crtc = fb_helper->crtc_info[i].mode_set.crtc;
+		crtc_funcs = crtc->helper_private;
 
 		red = cmap->red;
 		green = cmap->green;
@@ -554,22 +542,17 @@ int drm_fb_helper_setcolreg(unsigned regno,
 			    struct fb_info *info)
 {
 	struct drm_fb_helper *fb_helper = info->par;
-	struct drm_device *dev = fb_helper->dev;
 	struct drm_crtc *crtc;
+	struct drm_crtc_helper_funcs *crtc_funcs;
 	int i;
 	int ret;
 
 	if (regno > 255)
 		return 1;
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
-		for (i = 0; i < fb_helper->crtc_count; i++) {
-			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
-				break;
-		}
-		if (i == fb_helper->crtc_count)
-			continue;
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		crtc = fb_helper->crtc_info[i].mode_set.crtc;
+		crtc_funcs = crtc->helper_private;
 
 		ret = setcolreg(crtc, red, green, blue, regno, info);
 		if (ret)
@@ -684,23 +667,20 @@ int drm_fb_helper_set_par(struct fb_info *info)
 		return -EINVAL;
 	}
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-
-		for (i = 0; i < fb_helper->crtc_count; i++) {
-			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
-				break;
-		}
-		if (i == fb_helper->crtc_count)
-			continue;
+	mutex_lock(&dev->mode_config.mutex);
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		crtc = fb_helper->crtc_info[i].mode_set.crtc;
 
-		if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
-			mutex_lock(&dev->mode_config.mutex);
+		if (crtc->fb != fb_helper->crtc_info[i].mode_set.fb) {
 			ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
-			mutex_unlock(&dev->mode_config.mutex);
-			if (ret)
+
+			if (ret) {
+				mutex_unlock(&dev->mode_config.mutex);
 				return ret;
+			}
 		}
 	}
+	mutex_unlock(&dev->mode_config.mutex);
 	return 0;
 }
 EXPORT_SYMBOL(drm_fb_helper_set_par);
@@ -715,14 +695,9 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 	int ret = 0;
 	int i;
 
-	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		for (i = 0; i < fb_helper->crtc_count; i++) {
-			if (crtc->base.id == fb_helper->crtc_info[i].crtc_id)
-				break;
-		}
-
-		if (i == fb_helper->crtc_count)
-			continue;
+	mutex_lock(&dev->mode_config.mutex);
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		crtc = fb_helper->crtc_info[i].mode_set.crtc;
 
 		modeset = &fb_helper->crtc_info[i].mode_set;
 
@@ -730,34 +705,29 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 		modeset->y = var->yoffset;
 
 		if (modeset->num_connectors) {
-			mutex_lock(&dev->mode_config.mutex);
 			ret = crtc->funcs->set_config(modeset);
-			mutex_unlock(&dev->mode_config.mutex);
 			if (!ret) {
 				info->var.xoffset = var->xoffset;
 				info->var.yoffset = var->yoffset;
 			}
 		}
 	}
+	mutex_unlock(&dev->mode_config.mutex);
 	return ret;
 }
 EXPORT_SYMBOL(drm_fb_helper_pan_display);
 
-int drm_fb_helper_single_fb_probe(struct drm_device *dev,
-				  int preferred_bpp,
-				  int (*fb_find_or_create)(struct drm_device *dev,
-							   struct drm_fb_helper_surface_size *sizes,
-							   struct drm_fb_helper **fb_ptr))
+int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
+				  int preferred_bpp)
 {
-	struct drm_crtc *crtc;
+	struct drm_device *dev = fb_helper->dev;
 	struct drm_connector *connector;
 	int new_fb = 0;
 	int crtc_count = 0;
-	int ret, i, conn_count = 0;
+	int ret, i;
 	struct fb_info *info;
-	struct drm_mode_set *modeset = NULL;
-	struct drm_fb_helper *fb_helper;
 	struct drm_fb_helper_surface_size sizes;
+	int gamma_size = 0;
 
 	memset(&sizes, 0, sizeof(struct drm_fb_helper_surface_size));
 	sizes.surface_depth = 24;
@@ -773,7 +743,6 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 	/* 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) {
 		struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
-
 		struct drm_fb_helper_cmdline_mode *cmdline_mode;
 
 		if (!fb_help_conn)
@@ -805,21 +774,22 @@ 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 < sizes.fb_width)
-					sizes.fb_width = crtc->desired_mode->hdisplay;
-
-				if (crtc->desired_mode->vdisplay < sizes.fb_height)
-					sizes.fb_height = crtc->desired_mode->vdisplay;
-
-				if (crtc->desired_mode->hdisplay > sizes.surface_width)
-					sizes.surface_width = crtc->desired_mode->hdisplay;
-
-				if (crtc->desired_mode->vdisplay > sizes.surface_height)
-					sizes.surface_height = crtc->desired_mode->vdisplay;
-			}
+	crtc_count = 0;
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		struct drm_display_mode *desired_mode;
+		desired_mode = fb_helper->crtc_info[i].desired_mode;
+
+		if (desired_mode) {
+			if (gamma_size == 0)
+				gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
+			if (desired_mode->hdisplay < sizes.fb_width)
+				sizes.fb_width = desired_mode->hdisplay;
+			if (desired_mode->vdisplay < sizes.fb_height)
+				sizes.fb_height = desired_mode->vdisplay;
+			if (desired_mode->hdisplay > sizes.surface_width)
+				sizes.surface_width = desired_mode->hdisplay;
+			if (desired_mode->vdisplay > sizes.surface_height)
+				sizes.surface_height = desired_mode->vdisplay;
 			crtc_count++;
 		}
 	}
@@ -831,48 +801,20 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
 	}
 
 	/* push down into drivers */
-	new_fb = (*fb_find_or_create)(dev, &sizes,
-				      &fb_helper);
+	new_fb = (*fb_helper->fb_probe)(fb_helper, &sizes);
 	if (new_fb < 0)
 		return new_fb;
 
 	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_helper->fb;
-		conn_count = 0;
-		list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-			if (connector->encoder)
-				if (connector->encoder->crtc == modeset->crtc) {
-					modeset->connectors[conn_count] = connector;
-					conn_count++;
-					if (conn_count > fb_helper->conn_limit)
-						BUG();
-				}
-		}
-
-		for (i = conn_count; i < fb_helper->conn_limit; i++)
-			modeset->connectors[i] = NULL;
-
-		modeset->crtc = crtc;
-		crtc_count++;
-
-		modeset->num_connectors = conn_count;
-		if (modeset->crtc->desired_mode) {
-			if (modeset->mode)
-				drm_mode_destroy(dev, modeset->mode);
-			modeset->mode = drm_mode_duplicate(dev,
-							   modeset->crtc->desired_mode);
-		}
+	/* set the fb pointer */
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		fb_helper->crtc_info[i].mode_set.fb = fb_helper->fb;
 	}
-	fb_helper->crtc_count = crtc_count;
 
 	if (new_fb) {
 		info->var.pixclock = 0;
-		ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
+		ret = fb_alloc_cmap(&info->cmap, gamma_size, 0);
 		if (ret)
 			return ret;
 		if (register_framebuffer(info) < 0) {
@@ -904,15 +846,18 @@ EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
 
 void drm_fb_helper_free(struct drm_fb_helper *helper)
 {
-	list_del(&helper->kernel_fb_list);
-	if (list_empty(&kernel_fb_helper_list)) {
-		printk(KERN_INFO "unregistered panic notifier\n");
-		atomic_notifier_chain_unregister(&panic_notifier_list,
-						 &paniced);
-		unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
+	if (!list_empty(&helper->kernel_fb_list)) {
+		list_del(&helper->kernel_fb_list);
+		if (list_empty(&kernel_fb_helper_list)) {
+			printk(KERN_INFO "unregistered panic notifier\n");
+			atomic_notifier_chain_unregister(&panic_notifier_list,
+							 &paniced);
+			unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
+		}
 	}
 	drm_fb_helper_crtc_free(helper);
-	fb_dealloc_cmap(&helper->fbdev->cmap);
+	if (helper->fbdev->cmap.len)
+		fb_dealloc_cmap(&helper->fbdev->cmap);
 }
 EXPORT_SYMBOL(drm_fb_helper_free);
 
@@ -1166,20 +1111,21 @@ static bool drm_target_preferred(struct drm_device *dev,
 	return true;
 }
 
-static int drm_pick_crtcs(struct drm_device *dev,
-			  struct drm_crtc **best_crtcs,
+static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
+			  struct drm_fb_helper_crtc **best_crtcs,
 			  struct drm_display_mode **modes,
 			  int n, int width, int height)
 {
 	int c, o;
+	struct drm_device *dev = fb_helper->dev;
 	struct drm_connector *connector;
 	struct drm_connector_helper_funcs *connector_funcs;
 	struct drm_encoder *encoder;
-	struct drm_crtc *best_crtc;
+	struct drm_fb_helper_crtc *best_crtc;
 	int my_score, best_score, score;
-	struct drm_crtc **crtcs, *crtc;
+	struct drm_fb_helper_crtc **crtcs, *crtc;
 
-	if (n == dev->mode_config.num_connector)
+	if (n == fb_helper->dev->mode_config.num_connector)
 		return 0;
 	c = 0;
 	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
@@ -1190,12 +1136,12 @@ static int drm_pick_crtcs(struct drm_device *dev,
 
 	best_crtcs[n] = NULL;
 	best_crtc = NULL;
-	best_score = drm_pick_crtcs(dev, best_crtcs, modes, n+1, width, height);
+	best_score = drm_pick_crtcs(fb_helper, 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);
+	crtcs = kzalloc(dev->mode_config.num_connector *
+			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
 	if (!crtcs)
 		return best_score;
 
@@ -1212,15 +1158,12 @@ static int drm_pick_crtcs(struct drm_device *dev,
 	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) {
+	for (c = 0; c < fb_helper->crtc_count; c++) {
+		crtc = &fb_helper->crtc_info[c];
 
 		if ((encoder->possible_crtcs & (1 << c)) == 0) {
-			c++;
 			continue;
 		}
 
@@ -1230,34 +1173,34 @@ static int drm_pick_crtcs(struct drm_device *dev,
 
 		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,
+		memcpy(crtcs, best_crtcs, n * sizeof(struct drm_fb_helper_crtc *));
+		score = my_score + drm_pick_crtcs(fb_helper, 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 *));
+			       sizeof(struct drm_fb_helper_crtc *));
 		}
-		c++;
 	}
 out:
 	kfree(crtcs);
 	return best_score;
 }
 
-static void drm_setup_crtcs(struct drm_device *dev)
+static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 {
-	struct drm_crtc **crtcs;
+	struct drm_device *dev = fb_helper->dev;
+	struct drm_fb_helper_crtc **crtcs;
 	struct drm_display_mode **modes;
 	struct drm_encoder *encoder;
 	struct drm_connector *connector;
+	struct drm_mode_set *modeset;
 	bool *enabled;
 	int width, height;
 	int i, ret;
@@ -1273,7 +1216,7 @@ static void drm_setup_crtcs(struct drm_device *dev)
 	}
 
 	crtcs = kcalloc(dev->mode_config.num_connector,
-			sizeof(struct drm_crtc *), GFP_KERNEL);
+			sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
 	modes = kcalloc(dev->mode_config.num_connector,
 			sizeof(struct drm_display_mode *), GFP_KERNEL);
 	enabled = kcalloc(dev->mode_config.num_connector,
@@ -1287,26 +1230,30 @@ static void drm_setup_crtcs(struct drm_device *dev)
 
 	DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", width, height);
 
-	drm_pick_crtcs(dev, crtcs, modes, 0, width, height);
+	drm_pick_crtcs(fb_helper, crtcs, modes, 0, width, height);
+
+	/* need to set the modesets up here for use later */
+	/* fill out the connector<->crtc mappings into the modesets */
+	for (i = 0; i < fb_helper->crtc_count; i++) {
+		modeset = &fb_helper->crtc_info[i].mode_set;
+		modeset->num_connectors = 0;
+	}
 
 	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;
-		}
+		struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
+		modeset = &fb_crtc->mode_set;
 
-		if (mode && crtc) {
+		if (mode && fb_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;
+				      mode->name, fb_crtc->mode_set.crtc->base.id);
+			fb_crtc->desired_mode = mode;
+			if (modeset->mode)
+				drm_mode_destroy(dev, modeset->mode);
+			modeset->mode = drm_mode_duplicate(dev,
+							   fb_crtc->desired_mode);
+			modeset->connectors[modeset->num_connectors++] = connector;
 		}
 		i++;
 	}
@@ -1330,14 +1277,15 @@ static void drm_setup_crtcs(struct drm_device *dev)
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
  */
-bool drm_helper_initial_config(struct drm_device *dev)
+bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 {
+	struct drm_device *dev = fb_helper->dev;
 	int count = 0;
 
 	/* disable all the possible outputs/crtcs before entering KMS mode */
-	drm_helper_disable_unused_functions(dev);
+	drm_helper_disable_unused_functions(fb_helper->dev);
 
-	drm_fb_helper_parse_command_line(dev);
+	drm_fb_helper_parse_command_line(fb_helper->dev);
 
 	count = drm_helper_probe_connector_modes(dev,
 						 dev->mode_config.max_width,
@@ -1349,20 +1297,21 @@ bool drm_helper_initial_config(struct drm_device *dev)
 	if (count == 0)
 		printk(KERN_INFO "No connectors reported connected with modes\n");
 
-	drm_setup_crtcs(dev);
+	drm_setup_crtcs(fb_helper);
 
 	return 0;
 }
-EXPORT_SYMBOL(drm_helper_initial_config);
+EXPORT_SYMBOL(drm_fb_helper_initial_config);
 
-bool drm_helper_fb_hotplug_event(struct drm_device *dev)
+bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
+				 u32 max_width, u32 max_height)
 {
 	DRM_DEBUG_KMS("\n");
 
-	drm_helper_probe_connector_modes(dev, dev->mode_config.max_width,
-					 dev->mode_config.max_height);
+	drm_helper_probe_connector_modes(fb_helper->dev, max_width,
+					 max_height);
 
-	drm_setup_crtcs(dev);
+	drm_setup_crtcs(fb_helper);
 
 	return true;
 }
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
index 601f354..0405a74 100644
--- a/drivers/gpu/drm/i915/i915_drv.h
+++ b/drivers/gpu/drm/i915/i915_drv.h
@@ -220,7 +220,7 @@ enum no_fbc_reason {
 	FBC_NOT_TILED, /* buffer not tiled */
 };
 
-struct intel_kernel_fbdev;
+struct intel_fbdev;
 
 typedef struct drm_i915_private {
 	struct drm_device *dev;
@@ -630,7 +630,8 @@ typedef struct drm_i915_private {
 
 	enum no_fbc_reason no_fbc_reason;
 
-	struct intel_kernel_fbdev *fbdev;
+	/* list of fbdev register on this device */
+	struct intel_fbdev *fbdev;
 } drm_i915_private_t;
 
 /** driver private structure attached to each drm_gem_object */
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index d76e28e..0ffbcdd 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -45,7 +45,7 @@
 #include "i915_drm.h"
 #include "i915_drv.h"
 
-struct intel_kernel_fbdev {
+struct intel_fbdev {
 	struct drm_fb_helper helper;
 	struct intel_framebuffer ifb;
 	struct list_head fbdev_list;
@@ -71,14 +71,12 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
 };
 
 
-static int intelfb_create(struct drm_device *dev,
-			  struct drm_fb_helper_surface_size *sizes,
-			  struct intel_kernel_fbdev **ifbdev_p)
+static int intelfb_create(struct intel_fbdev *ifbdev,
+			  struct drm_fb_helper_surface_size *sizes)
 {
+	struct drm_device *dev = ifbdev->helper.dev;
 	struct fb_info *info;
-	struct intel_kernel_fbdev *ifbdev;
 	struct drm_framebuffer *fb;
-	struct intel_framebuffer *intel_fb;
 	struct drm_mode_fb_cmd mode_cmd;
 	struct drm_gem_object *fbo = NULL;
 	struct drm_i915_gem_object *obj_priv;
@@ -117,13 +115,14 @@ static int intelfb_create(struct drm_device *dev,
 	/* Flush everything out, we'll be doing GTT only from now on */
 	i915_gem_object_set_to_gtt_domain(fbo, 1);
 
-	info = framebuffer_alloc(sizeof(struct intel_kernel_fbdev), device);
+	info = framebuffer_alloc(0, device);
 	if (!info) { 
 		ret = -ENOMEM;
 		goto out_unpin;
 	}
 
-	ifbdev = info->par;
+	info->par = ifbdev;
+
 	intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo);
 
 	fb = &ifbdev->ifb.base;
@@ -131,22 +130,12 @@ static int intelfb_create(struct drm_device *dev,
 	ifbdev->helper.fb = fb;
 	ifbdev->helper.fbdev = info;
 	ifbdev->helper.funcs = &intel_fb_helper_funcs;
-	ifbdev->helper.dev = dev;
-
-	*ifbdev_p = ifbdev;
-
-	ret = drm_fb_helper_init_crtc_count(&ifbdev->helper, 2,
-					    INTELFB_CONN_LIMIT);
-	if (ret)
-		goto out_unref;
 
 	strcpy(info->fix.id, "inteldrmfb");
 
 	info->flags = FBINFO_DEFAULT;
-
 	info->fbops = &intelfb_ops;
 
-
 	/* setup aperture base/size for vesafb takeover */
 	info->aperture_base = dev->mode_config.fb_base;
 	if (IS_I9XX(dev))
@@ -183,8 +172,8 @@ static int intelfb_create(struct drm_device *dev,
 	info->pixmap.scan_align = 1;
 
 	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);
+		      fb->width, fb->height,
+		      obj_priv->gtt_offset, fbo);
 
 
 	mutex_unlock(&dev->struct_mutex);
@@ -200,76 +189,80 @@ out:
 	return ret;
 }
 
-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)
+static int intel_fb_find_or_create_single(struct drm_fb_helper *helper, 
+					  struct drm_fb_helper_surface_size *sizes)
 {
-	drm_i915_private_t *dev_priv = dev->dev_private;
-	struct intel_kernel_fbdev *ifbdev = NULL;
+	struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper;
 	int new_fb = 0;
 	int ret;
 
-	if (!dev_priv->fbdev) {
-		ret = intelfb_create(dev, sizes, 
-				     &ifbdev);
+	if (!helper->fb) {
+		ret = intelfb_create(ifbdev, sizes);
 		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)
+static int intelfb_probe(struct intel_fbdev *ifbdev)
 {
 	int ret;
 
 	DRM_DEBUG_KMS("\n");
-	ret = drm_fb_helper_single_fb_probe(dev, 32, intel_fb_find_or_create_single);
+	ret = drm_fb_helper_single_fb_probe(&ifbdev->helper, 32);
 	return ret;
 }
 
 int intel_fbdev_destroy(struct drm_device *dev,
-			struct intel_kernel_fbdev *ifbdev)
+			struct intel_fbdev *ifbdev)
 {
 	struct fb_info *info;
 	struct intel_framebuffer *ifb = &ifbdev->ifb;
 
-	info = ifbdev->helper.fbdev;
+	if (ifbdev->helper.fbdev) {
+		info = ifbdev->helper.fbdev;
+		unregister_framebuffer(info);
+		iounmap(info->screen_base);
+		framebuffer_release(info);
+	}
 
-	unregister_framebuffer(info);
-	iounmap(info->screen_base);
 	drm_fb_helper_free(&ifbdev->helper);
 
 	drm_framebuffer_cleanup(&ifb->base);
-	drm_gem_object_unreference_unlocked(ifb->obj);
-
-	framebuffer_release(info);
+	if (ifb->obj)
+		drm_gem_object_unreference_unlocked(ifb->obj);
 
 	return 0;
 }
 
 int intel_fbdev_init(struct drm_device *dev)
 {
-	drm_helper_initial_config(dev);
-	intelfb_probe(dev);
+	struct intel_fbdev *ifbdev;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+
+	ifbdev = kzalloc(sizeof(struct intel_fbdev), GFP_KERNEL);
+	if (!ifbdev)
+		return -ENOMEM;
+
+	dev_priv->fbdev = ifbdev;
+
+	drm_fb_helper_init_crtc_count(dev, &ifbdev->helper, 2,
+				      INTELFB_CONN_LIMIT);
+	ifbdev->helper.fb_probe = intel_fb_find_or_create_single;
+	drm_fb_helper_initial_config(&ifbdev->helper);
+	intelfb_probe(ifbdev);
 	return 0;
 }
 
 void intel_fbdev_fini(struct drm_device *dev)
 {
 	drm_i915_private_t *dev_priv = dev->dev_private;
+	if (!dev_priv->fbdev)
+		return;
+
 	intel_fbdev_destroy(dev, dev_priv->fbdev);
+	kfree(dev_priv->fbdev);
 	dev_priv->fbdev = NULL;
 }
 MODULE_LICENSE("GPL and additional rights");
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index a1cb2b1..62891c3 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -531,8 +531,6 @@ struct drm_nouveau_private {
 		atomic_t validate_sequence;
 	} ttm;
 
-  //	struct fb_info *fbdev_info;
-
 	int fifo_alloc_count;
 	struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
 
@@ -629,7 +627,7 @@ struct drm_nouveau_private {
 		struct dentry *channel_root;
 	} debugfs;
 
-	struct nouveau_fbcon_par *nfbdev;
+	struct nouveau_fbdev *nfbdev;
 };
 
 static inline struct drm_nouveau_private *
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index ccbd0e0..90843b6 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -53,8 +53,8 @@
 static int
 nouveau_fbcon_sync(struct fb_info *info)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 	int ret, i;
@@ -200,9 +200,9 @@ not_fb:
 #endif
 
 static void
-nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbcon_par *fbpar)
+nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
 {
-	struct fb_info *info = fbpar->helper.fbdev;
+	struct fb_info *info = nfbdev->helper.fbdev;
 	struct fb_fillrect rect;
 
 	/* Clear the entire fbcon.  The drm will program every connector
@@ -218,13 +218,12 @@ nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbcon_par *fbpar)
 }
 
 static int
-nouveau_fbcon_create(struct drm_device *dev, 
-		     struct drm_fb_helper_surface_size *sizes,
-		     struct nouveau_fbcon_par **fbpar_p)
+nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
+		     struct drm_fb_helper_surface_size *sizes)
 {
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct fb_info *info;
-	struct nouveau_fbcon_par *par;
 	struct drm_framebuffer *fb;
 	struct nouveau_framebuffer *nouveau_fb;
 	struct nouveau_bo *nvbo;
@@ -267,27 +266,23 @@ nouveau_fbcon_create(struct drm_device *dev,
 
 	mutex_lock(&dev->struct_mutex);
 
-	info = framebuffer_alloc(sizeof(struct nouveau_fbcon_par), device);
+	info = framebuffer_alloc(0, device);
 	if (!info) {
 		ret = -ENOMEM;
 		goto out_unref;
 	}
 
-	par = info->par;
-	nouveau_framebuffer_init(dev, &par->nouveau_fb, &mode_cmd, nvbo);
+	info->par = nfbdev;
 
-	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;
+	nouveau_framebuffer_init(dev, &nfbdev->nouveau_fb, &mode_cmd, nvbo);
 
-	*fbpar_p = par;
+	nouveau_fb = &nfbdev->nouveau_fb;
+	fb = &nouveau_fb->base;
 
-	ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4);
-	if (ret)
-		goto out_unref;
+	/* setup helper */
+	nfbdev->helper.fb = fb;
+	nfbdev->helper.fbdev = info;
+	nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs;
 
 	strcpy(info->fix.id, "nouveaufb");
 	if (nouveau_nofbaccel)
@@ -305,7 +300,7 @@ nouveau_fbcon_create(struct drm_device *dev,
 	info->screen_size = size;
 
 	drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
-	drm_fb_helper_fill_var(info, &par->helper, sizes->fb_width, sizes->fb_height);
+	drm_fb_helper_fill_var(info, &nfbdev->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);
@@ -338,8 +333,6 @@ nouveau_fbcon_create(struct drm_device *dev,
 	info->pixmap.flags = FB_PIXMAP_SYSTEM;
 	info->pixmap.scan_align = 1;
 
-	par->dev = dev;
-
 	if (dev_priv->channel && !nouveau_nofbaccel) {
 		switch (dev_priv->card_type) {
 		case NV_50:
@@ -353,7 +346,7 @@ nouveau_fbcon_create(struct drm_device *dev,
 		};
 	}
 
-	nouveau_fbcon_zfill(dev, par);
+	nouveau_fbcon_zfill(dev, nfbdev);
 
 	/* To allow resizeing without swapping buffers */
 	NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n",
@@ -372,66 +365,56 @@ out:
 }
 
 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)
+nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper,
+				    struct drm_fb_helper_surface_size *sizes)
 {
-	struct drm_nouveau_private *dev_priv = dev->dev_private;
-	struct nouveau_fbcon_par *fbpar;
+	struct nouveau_fbdev *nfbdev = (struct nouveau_fbdev *)helper;
 	int new_fb = 0;
 	int ret;
 
-	if (!dev_priv->nfbdev) {
-		ret = nouveau_fbcon_create(dev, sizes,
-					   &fbpar);
+	if (!helper->fb) {
+		ret = nouveau_fbcon_create(nfbdev, sizes);
 		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)
+nouveau_fbcon_probe(struct nouveau_fbdev *nfbdev)
 {
-	NV_DEBUG_KMS(dev, "\n");
+	NV_DEBUG_KMS(nfbdev->dev, "\n");
 
-	return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_find_or_create_single);
+	return drm_fb_helper_single_fb_probe(&nfbdev->helper, 32);
 }
 
 int
-nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbcon_par *fbpar)
+nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
 {
-	struct nouveau_framebuffer *nouveau_fb = &fbpar->nouveau_fb;
+	struct nouveau_framebuffer *nouveau_fb = &nfbdev->nouveau_fb;
 	struct fb_info *info;
 
-	info = fbpar->helper.fbdev;
-
-	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);
+	if (nfbdev->helper.fbdev) {
+		info = nfbdev->helper.fbdev;
+		unregister_framebuffer(info);
+		framebuffer_release(info);
+	}
 
+	if (nouveau_fb->nvbo) {
+		nouveau_bo_unmap(nouveau_fb->nvbo);
+		drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
+		nouveau_fb->nvbo = NULL;
+	}
+	drm_fb_helper_free(&nfbdev->helper);
 	drm_framebuffer_cleanup(&nouveau_fb->base);
-	framebuffer_release(info);
-
 	return 0;
 }
 
 void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 
 	NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
 	info->flags |= FBINFO_HWACCEL_DISABLED;
@@ -439,15 +422,33 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 
 int nouveau_fbcon_init(struct drm_device *dev)
 {
-	drm_helper_initial_config(dev);
-	nouveau_fbcon_probe(dev);
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	struct nouveau_fbdev *nfbdev;
+
+	nfbdev = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL);
+	if (!nfbdev)
+		return -ENOMEM;
+
+	nfbdev->dev = dev;
+	dev_priv->nfbdev = nfbdev;
+
+	drm_fb_helper_init_crtc_count(dev, &nfbdev->helper,
+				      2, 4);
+	nfbdev->helper.fb_probe = nouveau_fbcon_find_or_create_single;
+	drm_fb_helper_initial_config(&nfbdev->helper);
+	nouveau_fbcon_probe(nfbdev);
 	return 0;
 }
 
 void nouveau_fbcon_fini(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
+
+	if (!dev_priv->nfbdev)
+		return;
+
 	nouveau_fbcon_destroy(dev, dev_priv->nfbdev);
+	kfree(dev_priv->nfbdev);
 	dev_priv->nfbdev = NULL;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
index fa66cb9..7835b56 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
@@ -30,7 +30,7 @@
 #include "drm_fb_helper.h"
 
 #include "nouveau_fb.h"
-struct nouveau_fbcon_par {
+struct nouveau_fbdev {
 	struct drm_fb_helper helper;
 	struct nouveau_framebuffer nouveau_fb;
 	struct list_head fbdev_list;
diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
index 813b25c..603090e 100644
--- a/drivers/gpu/drm/nouveau/nv04_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
@@ -30,8 +30,8 @@
 void
 nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 
@@ -57,8 +57,8 @@ 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)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 
@@ -91,8 +91,8 @@ nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
 void
 nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 	uint32_t fg;
@@ -179,8 +179,8 @@ nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle)
 int
 nv04_fbcon_accel_init(struct fb_info *info)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 	const int sub = NvSubCtxSurf2D;
diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
index 25a3cd8..f241048 100644
--- a/drivers/gpu/drm/nouveau/nv50_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
@@ -6,8 +6,8 @@
 void
 nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 
@@ -49,8 +49,8 @@ nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
 void
 nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 
@@ -84,8 +84,8 @@ nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
 void
 nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 	uint32_t width, dwords, *data = (uint32_t *)image->data;
@@ -152,8 +152,8 @@ nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
 int
 nv50_fbcon_accel_init(struct fb_info *info)
 {
-	struct nouveau_fbcon_par *par = info->par;
-	struct drm_device *dev = par->dev;
+	struct nouveau_fbdev *nfbdev = info->par;
+	struct drm_device *dev = nfbdev->dev;
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
 	struct nouveau_channel *chan = dev_priv->channel;
 	struct nouveau_gpuobj *eng2d = NULL;
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 06955ef..e831aff 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -23,10 +23,6 @@
  * Authors:
  *     David Airlie
  */
-    /*
-     *  Modularization
-     */
-
 #include <linux/module.h>
 #include <linux/fb.h>
 
@@ -45,7 +41,7 @@
    this contains a helper + a radeon fb
    the helper contains a pointer to radeon framebuffer baseclass.
 */
-struct radeon_kernel_fbdev {
+struct radeon_fbdev {
 	struct drm_fb_helper helper;
 	struct radeon_framebuffer rfb;
 	struct list_head fbdev_list;
@@ -95,49 +91,44 @@ static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
 	.gamma_get = radeon_crtc_fb_gamma_get,
 };
 
-static int radeonfb_create(struct drm_device *dev,
-			   struct drm_fb_helper_surface_size *sizes,
-			   struct radeon_kernel_fbdev **rfbdev_p)
+static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
 {
-	struct radeon_device *rdev = dev->dev_private;
-	struct fb_info *info;
-	struct radeon_kernel_fbdev *rfbdev;
-	struct drm_framebuffer *fb = NULL;
-	struct drm_mode_fb_cmd mode_cmd;
+	struct radeon_bo *rbo = gobj->driver_private;
+	int ret;
+
+	ret = radeon_bo_reserve(rbo, false);
+	if (likely(ret == 0)) {
+		radeon_bo_kunmap(rbo);
+		radeon_bo_unreserve(rbo);
+	}
+	drm_gem_object_unreference_unlocked(gobj);
+}
+
+static int radeonfb_create_pinned_object(struct radeon_fbdev *rfbdev,
+					 struct drm_mode_fb_cmd *mode_cmd,
+					 struct drm_gem_object **gobj_p)
+{
+	struct radeon_device *rdev = rfbdev->rdev;
 	struct drm_gem_object *gobj = NULL;
 	struct radeon_bo *rbo = NULL;
-	struct device *device = &rdev->pdev->dev;
-	int size, aligned_size, ret;
-	u64 fb_gpuaddr;
-	void *fbptr = NULL;
-	unsigned long tmp;
 	bool fb_tiled = false; /* useful for testing */
 	u32 tiling_flags = 0;
+	int ret;
+	int aligned_size, size;
 
-	mode_cmd.width = sizes->surface_width;
-	mode_cmd.height = sizes->surface_height;
-
-	/* avivo can't scanout real 24bpp */
-	if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
-		sizes->surface_bpp = 32;
-
-	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 = sizes->surface_depth;
+	mode_cmd->pitch = radeon_align_pitch(rdev, mode_cmd->width, mode_cmd->bpp, fb_tiled) * ((mode_cmd->bpp + 1) / 8);
 
-	size = mode_cmd.pitch * mode_cmd.height;
+	size = mode_cmd->pitch * mode_cmd->height;
 	aligned_size = ALIGN(size, PAGE_SIZE);
-
 	ret = radeon_gem_object_create(rdev, aligned_size, 0,
-			RADEON_GEM_DOMAIN_VRAM,
-			false, ttm_bo_type_kernel,
-			&gobj);
+				       RADEON_GEM_DOMAIN_VRAM,
+				       false, ttm_bo_type_kernel,
+				       &gobj);
 	if (ret) {
-		printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
-		       sizes->surface_width, sizes->surface_height);
-		ret = -ENOMEM;
-		goto out;
+		printk(KERN_ERR "failed to allocate framebuffer (%d)\n",
+		       aligned_size);
+		return -ENOMEM;
 	}
 	rbo = gobj->driver_private;
 
@@ -145,7 +136,7 @@ static int radeonfb_create(struct drm_device *dev,
 		tiling_flags = RADEON_TILING_MACRO;
 
 #ifdef __BIG_ENDIAN
-	switch (mode_cmd.bpp) {
+	switch (mode_cmd->bpp) {
 	case 32:
 		tiling_flags |= RADEON_TILING_SWAP_32BIT;
 		break;
@@ -158,54 +149,82 @@ static int radeonfb_create(struct drm_device *dev,
 
 	if (tiling_flags) {
 		ret = radeon_bo_set_tiling_flags(rbo,
-					tiling_flags | RADEON_TILING_SURFACE,
-					mode_cmd.pitch);
+						 tiling_flags | RADEON_TILING_SURFACE,
+						 mode_cmd->pitch);
 		if (ret)
 			dev_err(rdev->dev, "FB failed to set tiling flags\n");
 	}
-	mutex_lock(&rdev->ddev->struct_mutex);
+
 
 	ret = radeon_bo_reserve(rbo, false);
 	if (unlikely(ret != 0))
 		goto out_unref;
-	ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
+	ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, NULL);
 	if (ret) {
 		radeon_bo_unreserve(rbo);
 		goto out_unref;
 	}
 	if (fb_tiled)
 		radeon_bo_check_tiling(rbo, 0, 0);
-	ret = radeon_bo_kmap(rbo, &fbptr);
+	ret = radeon_bo_kmap(rbo, NULL);
 	radeon_bo_unreserve(rbo);
 	if (ret) {
 		goto out_unref;
 	}
 
-	info = framebuffer_alloc(sizeof(struct radeon_kernel_fbdev), device);
+	*gobj_p = gobj;
+	return 0;
+out_unref:
+	radeonfb_destroy_pinned_object(gobj);
+	*gobj_p = NULL;
+	return ret;
+}
+
+static int radeonfb_create(struct radeon_fbdev *rfbdev,
+			   struct drm_fb_helper_surface_size *sizes)
+{
+	struct radeon_device *rdev = rfbdev->rdev;
+	struct fb_info *info;
+	struct drm_framebuffer *fb = NULL;
+	struct drm_mode_fb_cmd mode_cmd;
+	struct drm_gem_object *gobj = NULL;
+	struct radeon_bo *rbo = NULL;
+	struct device *device = &rdev->pdev->dev;
+	int ret;
+	unsigned long tmp;
+
+	mode_cmd.width = sizes->surface_width;
+	mode_cmd.height = sizes->surface_height;
+
+	/* avivo can't scanout real 24bpp */
+	if ((sizes->surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
+		sizes->surface_bpp = 32;
+
+	mode_cmd.bpp = sizes->surface_bpp;
+	mode_cmd.depth = sizes->surface_depth;
+
+	ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
+	rbo = gobj->driver_private;
+
+	/* okay we have an object now allocate the framebuffer */
+	info = framebuffer_alloc(0, device);
 	if (info == NULL) {
 		ret = -ENOMEM;
 		goto out_unref;
 	}
 	
-	rfbdev = info->par;
-	rfbdev->rdev = rdev;
-	radeon_framebuffer_init(dev, &rfbdev->rfb, &mode_cmd, gobj);
+	info->par = rfbdev;
+
+	radeon_framebuffer_init(rdev->ddev, &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)
-		goto out_unref;
-
-	memset_io(fbptr, 0x0, aligned_size);
+	memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
 
 	strcpy(info->fix.id, "radeondrmfb");
 
@@ -214,11 +233,11 @@ static int radeonfb_create(struct drm_device *dev,
 	info->flags = FBINFO_DEFAULT;
 	info->fbops = &radeonfb_ops;
 
-	tmp = fb_gpuaddr - rdev->mc.vram_start;
+	tmp = radeon_bo_gpu_offset(rbo) - rdev->mc.vram_start;
 	info->fix.smem_start = rdev->mc.aper_base + tmp;
-	info->fix.smem_len = size;
-	info->screen_base = fbptr;
-	info->screen_size = size;
+	info->fix.smem_len = radeon_bo_size(rbo);
+	info->screen_base = rbo->kptr;
+	info->screen_size = radeon_bo_size(rbo);
 
 	drm_fb_helper_fill_var(info, &rfbdev->helper, sizes->fb_width, sizes->fb_height);
 
@@ -239,60 +258,40 @@ static int radeonfb_create(struct drm_device *dev,
 	}
 	DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
 	DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
-	DRM_INFO("size %lu\n", (unsigned long)size);
+	DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
 	DRM_INFO("fb depth is %d\n", fb->depth);
 	DRM_INFO("   pitch is %d\n", fb->pitch);
 
-
-	mutex_unlock(&rdev->ddev->struct_mutex);
 	vga_switcheroo_client_fb_set(rdev->ddev->pdev, info);
 	return 0;
 
 out_unref:
 	if (rbo) {
-		ret = radeon_bo_reserve(rbo, false);
-		if (likely(ret == 0)) {
-			radeon_bo_kunmap(rbo);
-			radeon_bo_unreserve(rbo);
-		}
+
 	}
 	if (fb && ret) {
 		drm_gem_object_unreference(gobj);
 		drm_framebuffer_cleanup(fb);
 		kfree(fb);
 	}
-	drm_gem_object_unreference(gobj);
-	mutex_unlock(&rdev->ddev->struct_mutex);
+
 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)
+static int radeon_fb_find_or_create_single(struct drm_fb_helper *helper,
+					   struct drm_fb_helper_surface_size *sizes)
 {
-	struct radeon_device *rdev = dev->dev_private;
-	struct radeon_kernel_fbdev *rfbdev = NULL;
+	struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
 	int new_fb = 0;
 	int ret;
 
-	if (!rdev->mode_info.rfbdev) {
-		ret = radeonfb_create(dev, sizes,
-				      &rfbdev);
+	if (!helper->fb) {
+		ret = radeonfb_create(rfbdev, sizes);
 		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;
 }
 
@@ -312,48 +311,55 @@ int radeon_parse_options(char *options)
 	return 0;
 }
 
-static int radeonfb_probe(struct drm_device *dev)
+static int radeonfb_probe(struct radeon_fbdev *rfbdev)
 {
-	struct radeon_device *rdev = dev->dev_private;
+	struct radeon_device *rdev = rfbdev->rdev;
 	int bpp_sel = 32;
 
 	/* select 8 bpp console on RN50 or 16MB cards */
 	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, &radeon_fb_find_or_create_single);
+	return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel);
 }
 
 void radeonfb_hotplug(struct drm_device *dev)
 {
-	drm_helper_fb_hotplug_event(dev);
+	struct radeon_device *rdev = dev->dev_private;
+	int max_width, max_height;
+
+	max_width = rdev->mode_info.rfbdev->rfb.base.width;
+	max_height = rdev->mode_info.rfbdev->rfb.base.height;
+	drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height);
 
-	radeonfb_probe(dev);
+	radeonfb_probe(rdev->mode_info.rfbdev);
 }
 
-static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_kernel_fbdev *rfbdev)
+static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
 {
 	struct fb_info *info;
 	struct radeon_framebuffer *rfb = &rfbdev->rfb;
 	struct radeon_bo *rbo;
 	int r;
 
-	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);
+	if (rfbdev->helper.fbdev) {
+		info = rfbdev->helper.fbdev;
+		unregister_framebuffer(info);
+		framebuffer_release(info);
 	}
 
+	if (rfb->obj) {
+		rbo = rfb->obj->driver_private;
+		r = radeon_bo_reserve(rbo, false);
+		if (likely(r == 0)) {
+			radeon_bo_kunmap(rbo);
+			radeon_bo_unpin(rbo);
+			radeon_bo_unreserve(rbo);
+		}
+		drm_gem_object_unreference_unlocked(rfb->obj);
+	}
 	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;
 }
@@ -361,14 +367,32 @@ MODULE_LICENSE("GPL");
 
 int radeon_fbdev_init(struct radeon_device *rdev)
 {
-	drm_helper_initial_config(rdev->ddev);
-	radeonfb_probe(rdev->ddev);
+	struct radeon_fbdev *rfbdev;
+
+	rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL);
+	if (!rfbdev)
+		return -ENOMEM;
+
+	rfbdev->rdev = rdev;
+	rdev->mode_info.rfbdev = rfbdev;
+
+	drm_fb_helper_init_crtc_count(rdev->ddev, &rfbdev->helper,
+				      rdev->num_crtc,
+				      RADEONFB_CONN_LIMIT);
+	rfbdev->helper.fb_probe = radeon_fb_find_or_create_single;
+	drm_fb_helper_initial_config(&rfbdev->helper);
+	radeonfb_probe(rfbdev);
 	return 0;
+
 }
 
 void radeon_fbdev_fini(struct radeon_device *rdev)
 {
+	if (!rdev->mode_info.rfbdev)
+		return;
+
 	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
+	kfree(rdev->mode_info.rfbdev);
 	rdev->mode_info.rfbdev = NULL;
 }
 
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 9f088c6..847ddbb 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -208,7 +208,7 @@ enum radeon_dvo_chip {
 	DVO_SIL1178,
 };
 
-struct radeon_kernel_fbdev;
+struct radeon_fbdev;
 
 struct radeon_mode_info {
 	struct atom_context *atom_context;
@@ -228,7 +228,7 @@ struct radeon_mode_info {
 	struct edid *bios_hardcoded_edid;
 
 	/* pointer to fbdev info structure */
-	struct radeon_kernel_fbdev *rfbdev;
+	struct radeon_fbdev *rfbdev;
 };
 
 #define MAX_H_CODE_TIMING_LEN 32
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index c70814b..e4e34ba 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -367,9 +367,6 @@ struct drm_crtc_funcs {
  * @enabled: is this CRTC enabled?
  * @x: x position on screen
  * @y: y position on screen
- * @desired_mode: new desired mode
- * @desired_x: desired x for desired_mode
- * @desired_y: desired y for desired_mode
  * @funcs: CRTC control functions
  *
  * Each CRTC may have one or more connectors associated with it.  This structure
@@ -389,8 +386,6 @@ struct drm_crtc {
 	struct drm_display_mode mode;
 
 	int x, y;
-	struct drm_display_mode *desired_mode;
-	int desired_x, desired_y;
 	const struct drm_crtc_funcs *funcs;
 
 	/* CRTC gamma size for reporting to userspace */
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 38ab0da..b1ea66f 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -33,6 +33,7 @@
 struct drm_fb_helper_crtc {
 	uint32_t crtc_id;
 	struct drm_mode_set mode_set;
+	struct drm_display_mode *desired_mode;
 };
 
 
@@ -81,14 +82,16 @@ struct drm_fb_helper {
 	struct fb_info *fbdev;
 	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
+
+	int (*fb_probe)(struct drm_fb_helper *helper,
+			struct drm_fb_helper_surface_size *sizes);
 };
 
-int drm_fb_helper_single_fb_probe(struct drm_device *dev,
-				  int preferred_bpp,
-				  int (*fb_create)(struct drm_device *dev,
-						   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 drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper,
+				  int preferred_bpp);
+
+int drm_fb_helper_init_crtc_count(struct drm_device *dev,
+				  struct drm_fb_helper *helper, int crtc_count,
 				  int max_conn);
 void drm_fb_helper_free(struct drm_fb_helper *helper);
 int drm_fb_helper_blank(int blank, struct fb_info *info);
@@ -114,6 +117,8 @@ 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);
+bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, u32 max_width,
+				 u32 max_height);
+bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper);
+
 #endif
-- 
1.6.5.2


------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 3/6] drm/kms/fb: separate fbdev connector list from core drm connectors
  2010-03-30  5:34   ` [PATCH 2/6] drm/kms/fb: move to using fb helper crtc grouping instead of core crtc list Dave Airlie
@ 2010-03-30  5:34     ` Dave Airlie
  2010-03-30  5:34       ` [PATCH 4/6] drm/kms/fb: provide a 1024x768 fbcon if no outputs found Dave Airlie
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Airlie @ 2010-03-30  5:34 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

This breaks the connection between the core drm connector list
and the fbdev connector usage, and allows them to become disjoint
in the future. It also removes the untype void* that was in the
connector struct to support this.

All connectors are added to the fbdev now but this could be
changed in the future.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/drm_crtc.c                 |    1 -
 drivers/gpu/drm/drm_fb_helper.c            |  196 +++++++++++++++-------------
 drivers/gpu/drm/i915/intel_fb.c            |    1 +
 drivers/gpu/drm/nouveau/nouveau_fbcon.c    |    2 +
 drivers/gpu/drm/radeon/radeon_connectors.c |   50 ++------
 drivers/gpu/drm/radeon/radeon_fb.c         |    5 +-
 include/drm/drm_crtc.h                     |    1 -
 include/drm/drm_crtc_helper.h              |    5 +-
 include/drm/drm_fb_helper.h                |    6 +-
 9 files changed, 126 insertions(+), 141 deletions(-)

diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 6a472d5..e8cd683 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -493,7 +493,6 @@ void drm_connector_cleanup(struct drm_connector *connector)
 	list_for_each_entry_safe(mode, t, &connector->user_modes, head)
 		drm_mode_remove(connector, mode);
 
-	kfree(connector->fb_helper_private);
 	mutex_lock(&dev->mode_config.mutex);
 	drm_mode_object_put(dev, &connector->base);
 	list_del(&connector->head);
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 3d6f417..a1724af 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -41,15 +41,33 @@ MODULE_LICENSE("GPL and additional rights");
 
 static LIST_HEAD(kernel_fb_helper_list);
 
-int drm_fb_helper_add_connector(struct drm_connector *connector)
+/* simple single crtc case helper function */
+int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
 {
-	connector->fb_helper_private = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
-	if (!connector->fb_helper_private)
-		return -ENOMEM;
+	struct drm_device *dev = fb_helper->dev;
+	struct drm_connector *connector;
+	int i;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		struct drm_fb_helper_connector *fb_helper_connector;
+
+		fb_helper_connector = kzalloc(sizeof(struct drm_fb_helper_connector), GFP_KERNEL);
+		if (!fb_helper_connector)
+			goto fail;
 
+		fb_helper_connector->connector = connector;
+		fb_helper->connector_info[fb_helper->connector_count++] = fb_helper_connector;
+	}
 	return 0;
+fail:
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		kfree(fb_helper->connector_info[i]);
+		fb_helper->connector_info[i] = NULL;
+	}
+	fb_helper->connector_count = 0;
+	return -ENOMEM;
 }
-EXPORT_SYMBOL(drm_fb_helper_add_connector);
+EXPORT_SYMBOL(drm_fb_helper_single_add_all_connectors);
 
 /**
  * drm_fb_helper_connector_parse_command_line - parse command line for connector
@@ -64,7 +82,7 @@ EXPORT_SYMBOL(drm_fb_helper_add_connector);
  *
  * enable/enable Digital/disable bit at the end
  */
-static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *connector,
+static bool drm_fb_helper_connector_parse_command_line(struct drm_fb_helper_connector *fb_helper_conn,
 						       const char *mode_option)
 {
 	const char *name;
@@ -74,13 +92,13 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *con
 	int yres_specified = 0, cvt = 0, rb = 0, interlace = 0, margins = 0;
 	int i;
 	enum drm_connector_force force = DRM_FORCE_UNSPECIFIED;
-	struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
 	struct drm_fb_helper_cmdline_mode *cmdline_mode;
+	struct drm_connector *connector = fb_helper_conn->connector;
 
-	if (!fb_help_conn)
+	if (!fb_helper_conn)
 		return false;
 
-	cmdline_mode = &fb_help_conn->cmdline_mode;
+	cmdline_mode = &fb_helper_conn->cmdline_mode;
 	if (!mode_option)
 		mode_option = fb_mode_option;
 
@@ -203,18 +221,21 @@ done:
 	return true;
 }
 
-int drm_fb_helper_parse_command_line(struct drm_device *dev)
+static int drm_fb_helper_parse_command_line(struct drm_fb_helper *fb_helper)
 {
-	struct drm_connector *connector;
+	struct drm_fb_helper_connector *fb_helper_conn;
+	int i;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	for (i = 0; i < fb_helper->connector_count; i++) {
 		char *option = NULL;
 
+		fb_helper_conn = fb_helper->connector_info[i];
+
 		/* do something on return - turn off connector maybe */
-		if (fb_get_options(drm_get_connector_name(connector), &option))
+		if (fb_get_options(drm_get_connector_name(fb_helper_conn->connector), &option))
 			continue;
 
-		drm_fb_helper_connector_parse_command_line(connector, option);
+		drm_fb_helper_connector_parse_command_line(fb_helper_conn, option);
 	}
 	return 0;
 }
@@ -389,6 +410,9 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 {
 	int i;
 
+	for (i = 0; i < helper->connector_count; i++)
+		kfree(helper->connector_info[i]);
+	kfree(helper->connector_info);
 	for (i = 0; i < helper->crtc_count; i++)
 		kfree(helper->crtc_info[i].mode_set.connectors);
 	kfree(helper->crtc_info);
@@ -409,6 +433,13 @@ int drm_fb_helper_init_crtc_count(struct drm_device *dev,
 		return -ENOMEM;
 	helper->crtc_count = crtc_count;
 
+	helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
+	if (!helper->connector_info) {
+		kfree(helper->crtc_info);
+		return -ENOMEM;
+	}
+	helper->connector_count = 0;
+
 	for (i = 0; i < crtc_count; i++) {
 		helper->crtc_info[i].mode_set.connectors =
 			kcalloc(max_conn_count,
@@ -670,14 +701,10 @@ int drm_fb_helper_set_par(struct fb_info *info)
 	mutex_lock(&dev->mode_config.mutex);
 	for (i = 0; i < fb_helper->crtc_count; i++) {
 		crtc = fb_helper->crtc_info[i].mode_set.crtc;
-
-		if (crtc->fb != fb_helper->crtc_info[i].mode_set.fb) {
-			ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
-
-			if (ret) {
-				mutex_unlock(&dev->mode_config.mutex);
-				return ret;
-			}
+		ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
+		if (ret) {
+			mutex_unlock(&dev->mode_config.mutex);
+			return ret;
 		}
 	}
 	mutex_unlock(&dev->mode_config.mutex);
@@ -720,8 +747,6 @@ EXPORT_SYMBOL(drm_fb_helper_pan_display);
 int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 				  int preferred_bpp)
 {
-	struct drm_device *dev = fb_helper->dev;
-	struct drm_connector *connector;
 	int new_fb = 0;
 	int crtc_count = 0;
 	int ret, i;
@@ -741,14 +766,11 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 		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) {
-		struct drm_fb_helper_connector *fb_help_conn = connector->fb_helper_private;
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		struct drm_fb_helper_connector *fb_helper_conn = fb_helper->connector_info[i];
 		struct drm_fb_helper_cmdline_mode *cmdline_mode;
 
-		if (!fb_help_conn)
-			continue;
-		
-		cmdline_mode = &fb_help_conn->cmdline_mode;
+		cmdline_mode = &fb_helper_conn->cmdline_mode;
 
 		if (cmdline_mode->bpp_specified) {
 			switch (cmdline_mode->bpp) {
@@ -952,24 +974,27 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe
 }
 EXPORT_SYMBOL(drm_fb_helper_fill_var);
 
-static int drm_helper_probe_connector_modes(struct drm_device *dev, uint32_t maxX,
-					    uint32_t maxY)
+static int drm_fb_helper_probe_connector_modes(struct drm_fb_helper *fb_helper,
+					       uint32_t maxX,
+					       uint32_t maxY)
 {
 	struct drm_connector *connector;
 	int count = 0;
+	int i;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		connector = fb_helper->connector_info[i]->connector;
 		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)
+static struct drm_display_mode *drm_has_preferred_mode(struct drm_fb_helper_connector *fb_connector, int width, int height)
 {
 	struct drm_display_mode *mode;
 
-	list_for_each_entry(mode, &connector->modes, head) {
+	list_for_each_entry(mode, &fb_connector->connector->modes, head) {
 		if (drm_mode_width(mode) > width ||
 		    drm_mode_height(mode) > height)
 			continue;
@@ -979,28 +1004,20 @@ static struct drm_display_mode *drm_has_preferred_mode(struct drm_connector *con
 	return NULL;
 }
 
-static bool drm_has_cmdline_mode(struct drm_connector *connector)
+static bool drm_has_cmdline_mode(struct drm_fb_helper_connector *fb_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;
+	cmdline_mode = &fb_connector->cmdline_mode;
 	return cmdline_mode->specified;
 }
 
-static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *connector, int width, int height)
+static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_fb_helper_connector *fb_helper_conn,
+						      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;
+	cmdline_mode = &fb_helper_conn->cmdline_mode;
 	if (cmdline_mode->specified == false)
 		return mode;
 
@@ -1010,7 +1027,7 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *conn
 	if (cmdline_mode->rb || cmdline_mode->margins)
 		goto create_mode;
 
-	list_for_each_entry(mode, &connector->modes, head) {
+	list_for_each_entry(mode, &fb_helper_conn->connector->modes, head) {
 		/* check width/height */
 		if (mode->hdisplay != cmdline_mode->xres ||
 		    mode->vdisplay != cmdline_mode->yres)
@@ -1029,13 +1046,13 @@ static struct drm_display_mode *drm_pick_cmdline_mode(struct drm_connector *conn
 	}
 
 create_mode:
-	mode = drm_cvt_mode(connector->dev, cmdline_mode->xres,
+	mode = drm_cvt_mode(fb_helper_conn->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);
+	list_add(&mode->head, &fb_helper_conn->connector->modes);
 	return mode;
 }
 
@@ -1051,62 +1068,60 @@ static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
 	return enable;
 }
 
-static void drm_enable_connectors(struct drm_device *dev, bool *enabled)
+static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
+				  bool *enabled)
 {
 	bool any_enabled = false;
 	struct drm_connector *connector;
 	int i = 0;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		connector = fb_helper->connector_info[i]->connector;
 		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) {
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		connector = fb_helper->connector_info[i]->connector;
 		enabled[i] = drm_connector_enabled(connector, false);
-		i++;
 	}
 }
 
-static bool drm_target_preferred(struct drm_device *dev,
+static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
 				 struct drm_display_mode **modes,
 				 bool *enabled, int width, int height)
 {
-	struct drm_connector *connector;
-	int i = 0;
+	struct drm_fb_helper_connector *fb_helper_conn;
+	int i;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		fb_helper_conn = fb_helper->connector_info[i];
 
-		if (enabled[i] == false) {
-			i++;
+		if (enabled[i] == false)
 			continue;
-		}
 
 		DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
-			      connector->base.id);
+			      fb_helper_conn->connector->base.id);
 
 		/* got for command line mode first */
-		modes[i] = drm_pick_cmdline_mode(connector, width, height);
+		modes[i] = drm_pick_cmdline_mode(fb_helper_conn, 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);
+				      fb_helper_conn->connector->base.id);
+			modes[i] = drm_has_preferred_mode(fb_helper_conn, 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)
+		if (!modes[i] && !list_empty(&fb_helper_conn->connector->modes)) {
+			list_for_each_entry(modes[i], &fb_helper_conn->connector->modes, head)
 				break;
 		}
 		DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
 			  "none");
-		i++;
 	}
 	return true;
 }
@@ -1124,15 +1139,13 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 	struct drm_fb_helper_crtc *best_crtc;
 	int my_score, best_score, score;
 	struct drm_fb_helper_crtc **crtcs, *crtc;
+	struct drm_fb_helper_connector *fb_helper_conn;
 
-	if (n == fb_helper->dev->mode_config.num_connector)
+	if (n == fb_helper->connector_count)
 		return 0;
-	c = 0;
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
-		if (c == n)
-			break;
-		c++;
-	}
+
+	fb_helper_conn = fb_helper->connector_info[n];
+	connector = fb_helper_conn->connector;
 
 	best_crtcs[n] = NULL;
 	best_crtc = NULL;
@@ -1148,9 +1161,9 @@ static int drm_pick_crtcs(struct drm_fb_helper *fb_helper,
 	my_score = 1;
 	if (connector->status == connector_status_connected)
 		my_score++;
-	if (drm_has_cmdline_mode(connector))
+	if (drm_has_cmdline_mode(fb_helper_conn))
 		my_score++;
-	if (drm_has_preferred_mode(connector, width, height))
+	if (drm_has_preferred_mode(fb_helper_conn, width, height))
 		my_score++;
 
 	connector_funcs = connector->helper_private;
@@ -1199,7 +1212,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 	struct drm_fb_helper_crtc **crtcs;
 	struct drm_display_mode **modes;
 	struct drm_encoder *encoder;
-	struct drm_connector *connector;
 	struct drm_mode_set *modeset;
 	bool *enabled;
 	int width, height;
@@ -1222,9 +1234,9 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 	enabled = kcalloc(dev->mode_config.num_connector,
 			  sizeof(bool), GFP_KERNEL);
 
-	drm_enable_connectors(dev, enabled);
+	drm_enable_connectors(fb_helper, enabled);
 
-	ret = drm_target_preferred(dev, modes, enabled, width, height);
+	ret = drm_target_preferred(fb_helper, modes, enabled, width, height);
 	if (!ret)
 		DRM_ERROR("Unable to find initial modes\n");
 
@@ -1239,8 +1251,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 		modeset->num_connectors = 0;
 	}
 
-	i = 0;
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	for (i = 0; i < fb_helper->connector_count; i++) {
 		struct drm_display_mode *mode = modes[i];
 		struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
 		modeset = &fb_crtc->mode_set;
@@ -1253,9 +1264,8 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
 				drm_mode_destroy(dev, modeset->mode);
 			modeset->mode = drm_mode_duplicate(dev,
 							   fb_crtc->desired_mode);
-			modeset->connectors[modeset->num_connectors++] = connector;
+			modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
 		}
-		i++;
 	}
 
 	kfree(crtcs);
@@ -1285,11 +1295,11 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 	/* disable all the possible outputs/crtcs before entering KMS mode */
 	drm_helper_disable_unused_functions(fb_helper->dev);
 
-	drm_fb_helper_parse_command_line(fb_helper->dev);
+	drm_fb_helper_parse_command_line(fb_helper);
 
-	count = drm_helper_probe_connector_modes(dev,
-						 dev->mode_config.max_width,
-						 dev->mode_config.max_height);
+	count = drm_fb_helper_probe_connector_modes(fb_helper,
+						    dev->mode_config.max_width,
+						    dev->mode_config.max_height);
 
 	/*
 	 * we shouldn't end up with no modes here.
@@ -1308,8 +1318,8 @@ bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
 {
 	DRM_DEBUG_KMS("\n");
 
-	drm_helper_probe_connector_modes(fb_helper->dev, max_width,
-					 max_height);
+	drm_fb_helper_probe_connector_modes(fb_helper, max_width,
+						    max_height);
 
 	drm_setup_crtcs(fb_helper);
 
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index 0ffbcdd..dc126b8 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -249,6 +249,7 @@ int intel_fbdev_init(struct drm_device *dev)
 
 	drm_fb_helper_init_crtc_count(dev, &ifbdev->helper, 2,
 				      INTELFB_CONN_LIMIT);
+	drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
 	ifbdev->helper.fb_probe = intel_fb_find_or_create_single;
 	drm_fb_helper_initial_config(&ifbdev->helper);
 	intelfb_probe(ifbdev);
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 90843b6..fd5d3cd 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -435,6 +435,8 @@ int nouveau_fbcon_init(struct drm_device *dev)
 	drm_fb_helper_init_crtc_count(dev, &nfbdev->helper,
 				      2, 4);
 	nfbdev->helper.fb_probe = nouveau_fbcon_find_or_create_single;
+	drm_fb_helper_single_add_all_connectors(&nfbdev->helper);
+
 	drm_fb_helper_initial_config(&nfbdev->helper);
 	nouveau_fbcon_probe(nfbdev);
 	return 0;
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
index ee0083f..06e6367 100644
--- a/drivers/gpu/drm/radeon/radeon_connectors.c
+++ b/drivers/gpu/drm/radeon/radeon_connectors.c
@@ -1032,7 +1032,6 @@ radeon_add_atom_connector(struct drm_device *dev,
 	struct radeon_connector_atom_dig *radeon_dig_connector;
 	uint32_t subpixel_order = SubPixelNone;
 	bool shared_ddc = false;
-	int ret;
 
 	/* fixme - tv/cv/din */
 	if (connector_type == DRM_MODE_CONNECTOR_Unknown)
@@ -1067,9 +1066,7 @@ radeon_add_atom_connector(struct drm_device *dev,
 	switch (connector_type) {
 	case DRM_MODE_CONNECTOR_VGA:
 		drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA");
 			if (!radeon_connector->ddc_bus)
@@ -1082,9 +1079,7 @@ radeon_add_atom_connector(struct drm_device *dev,
 		break;
 	case DRM_MODE_CONNECTOR_DVIA:
 		drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
 			if (!radeon_connector->ddc_bus)
@@ -1104,9 +1099,7 @@ radeon_add_atom_connector(struct drm_device *dev,
 		radeon_dig_connector->igp_lane_info = igp_lane_info;
 		radeon_connector->con_priv = radeon_dig_connector;
 		drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
 			if (!radeon_connector->ddc_bus)
@@ -1132,9 +1125,7 @@ radeon_add_atom_connector(struct drm_device *dev,
 		radeon_dig_connector->igp_lane_info = igp_lane_info;
 		radeon_connector->con_priv = radeon_dig_connector;
 		drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "HDMI");
 			if (!radeon_connector->ddc_bus)
@@ -1154,9 +1145,7 @@ radeon_add_atom_connector(struct drm_device *dev,
 		radeon_dig_connector->igp_lane_info = igp_lane_info;
 		radeon_connector->con_priv = radeon_dig_connector;
 		drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			/* add DP i2c bus */
 			if (connector_type == DRM_MODE_CONNECTOR_eDP)
@@ -1182,9 +1171,7 @@ radeon_add_atom_connector(struct drm_device *dev,
 	case DRM_MODE_CONNECTOR_9PinDIN:
 		if (radeon_tv == 1) {
 			drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type);
-			ret = drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
-			if (ret)
-				goto failed;
+			drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
 			radeon_connector->dac_load_detect = true;
 			drm_connector_attach_property(&radeon_connector->base,
 						      rdev->mode_info.load_detect_property,
@@ -1202,9 +1189,7 @@ radeon_add_atom_connector(struct drm_device *dev,
 		radeon_dig_connector->igp_lane_info = igp_lane_info;
 		radeon_connector->con_priv = radeon_dig_connector;
 		drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS");
 			if (!radeon_connector->ddc_bus)
@@ -1241,7 +1226,6 @@ radeon_add_legacy_connector(struct drm_device *dev,
 	struct drm_connector *connector;
 	struct radeon_connector *radeon_connector;
 	uint32_t subpixel_order = SubPixelNone;
-	int ret;
 
 	/* fixme - tv/cv/din */
 	if (connector_type == DRM_MODE_CONNECTOR_Unknown)
@@ -1269,9 +1253,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
 	switch (connector_type) {
 	case DRM_MODE_CONNECTOR_VGA:
 		drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "VGA");
 			if (!radeon_connector->ddc_bus)
@@ -1284,9 +1266,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
 		break;
 	case DRM_MODE_CONNECTOR_DVIA:
 		drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_vga_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
 			if (!radeon_connector->ddc_bus)
@@ -1300,9 +1280,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
 	case DRM_MODE_CONNECTOR_DVII:
 	case DRM_MODE_CONNECTOR_DVID:
 		drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DVI");
 			if (!radeon_connector->ddc_bus)
@@ -1319,9 +1297,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
 	case DRM_MODE_CONNECTOR_9PinDIN:
 		if (radeon_tv == 1) {
 			drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type);
-			ret = drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
-			if (ret)
-				goto failed;
+			drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs);
 			radeon_connector->dac_load_detect = true;
 			/* RS400,RC410,RS480 chipset seems to report a lot
 			 * of false positive on load detect, we haven't yet
@@ -1340,9 +1316,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
 		break;
 	case DRM_MODE_CONNECTOR_LVDS:
 		drm_connector_init(dev, &radeon_connector->base, &radeon_lvds_connector_funcs, connector_type);
-		ret = drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
-		if (ret)
-			goto failed;
+		drm_connector_helper_add(&radeon_connector->base, &radeon_lvds_connector_helper_funcs);
 		if (i2c_bus->valid) {
 			radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "LVDS");
 			if (!radeon_connector->ddc_bus)
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index e831aff..01a9d3b 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -274,8 +274,6 @@ out_unref:
 		drm_framebuffer_cleanup(fb);
 		kfree(fb);
 	}
-
-out:
 	return ret;
 }
 
@@ -380,6 +378,9 @@ int radeon_fbdev_init(struct radeon_device *rdev)
 				      rdev->num_crtc,
 				      RADEONFB_CONN_LIMIT);
 	rfbdev->helper.fb_probe = radeon_fb_find_or_create_single;
+
+	drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
+
 	drm_fb_helper_initial_config(&rfbdev->helper);
 	radeonfb_probe(rfbdev);
 	return 0;
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index e4e34ba..fce2042 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -514,7 +514,6 @@ struct drm_connector {
 	uint32_t encoder_ids[DRM_CONNECTOR_MAX_ENCODER];
 	uint32_t force_encoder_id;
 	struct drm_encoder *encoder; /* currently active encoder */
-	void *fb_helper_private;
 };
 
 /**
diff --git a/include/drm/drm_crtc_helper.h b/include/drm/drm_crtc_helper.h
index ce7aab7..b1fa0f8 100644
--- a/include/drm/drm_crtc_helper.h
+++ b/include/drm/drm_crtc_helper.h
@@ -39,7 +39,6 @@
 
 #include <linux/fb.h>
 
-#include "drm_fb_helper.h"
 struct drm_crtc_helper_funcs {
 	/*
 	 * Control power levels on the CRTC.  If the mode passed in is
@@ -96,7 +95,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 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,
@@ -122,11 +120,10 @@ static inline void drm_encoder_helper_add(struct drm_encoder *encoder,
 	encoder->helper_private = (void *)funcs;
 }
 
-static inline int drm_connector_helper_add(struct drm_connector *connector,
+static inline void drm_connector_helper_add(struct drm_connector *connector,
 					    const struct drm_connector_helper_funcs *funcs)
 {
 	connector->helper_private = (void *)funcs;
-	return drm_fb_helper_add_connector(connector);
 }
 
 extern int drm_helper_resume_force_mode(struct drm_device *dev);
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index b1ea66f..50094f9 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -69,6 +69,7 @@ struct drm_fb_helper_surface_size {
 
 struct drm_fb_helper_connector {
 	struct drm_fb_helper_cmdline_mode cmdline_mode;
+	struct drm_connector *connector;
 };
 
 struct drm_fb_helper {
@@ -77,6 +78,8 @@ struct drm_fb_helper {
 	struct drm_display_mode *mode;
 	int crtc_count;
 	struct drm_fb_helper_crtc *crtc_info;
+	int connector_count;
+	struct drm_fb_helper_connector **connector_info;
 	struct drm_fb_helper_funcs *funcs;
 	int conn_limit;
 	struct fb_info *fbdev;
@@ -113,12 +116,11 @@ void drm_fb_helper_fill_var(struct fb_info *info, struct drm_fb_helper *fb_helpe
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 			    uint32_t depth);
 
-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_fb_helper *fb_helper, u32 max_width,
 				 u32 max_height);
 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper);
+int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
 
 #endif
-- 
1.6.5.2


------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 4/6] drm/kms/fb: provide a 1024x768 fbcon if no outputs found.
  2010-03-30  5:34     ` [PATCH 3/6] drm/kms/fb: separate fbdev connector list from core drm connectors Dave Airlie
@ 2010-03-30  5:34       ` Dave Airlie
  2010-03-30  5:34         ` [PATCH 5/6] drm/kms/fb: add polling support for when nothing is connected Dave Airlie
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Airlie @ 2010-03-30  5:34 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

If we get no outputs setup provide a 1024x768 fbcon, with
this + radeon hotplug stuff I can plug a monitor in after startup
and get to see stuff.

Last thing is to add some sort of timer for non-hpd outputs like
VGA etc.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/drm_fb_helper.c |    4 +++-
 1 files changed, 3 insertions(+), 1 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index a1724af..8192a56 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -819,7 +819,9 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 	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;
+		DRM_ERROR("Cannot find any crtc or sizes - going 1024x768\n");
+		sizes.fb_width = sizes.surface_width = 1024;
+		sizes.fb_height = sizes.surface_height = 768;
 	}
 
 	/* push down into drivers */
-- 
1.6.5.2


------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 5/6] drm/kms/fb: add polling support for when nothing is connected.
  2010-03-30  5:34       ` [PATCH 4/6] drm/kms/fb: provide a 1024x768 fbcon if no outputs found Dave Airlie
@ 2010-03-30  5:34         ` Dave Airlie
  2010-03-30  5:34           ` [PATCH 6/6] drm/kms/fb: use slow work mechanism for normal hotplug also Dave Airlie
  0 siblings, 1 reply; 7+ messages in thread
From: Dave Airlie @ 2010-03-30  5:34 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

When we are running in a headless environment we have no idea what
output the user might plug in later, we only have hotplug detect
from the digital outputs. So if we detect no connected outputs at
initialisation, start a slow work operation to poll every 5 seconds
for an output.

this is only hooked up for radeon so far, on hw where we have full
hotplug detection there is no need for this.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/Kconfig                 |    1 +
 drivers/gpu/drm/drm_fb_helper.c         |   82 ++++++++++++++++++++++++++++--
 drivers/gpu/drm/radeon/radeon_fb.c      |   14 +++++-
 drivers/gpu/drm/radeon/radeon_irq_kms.c |    2 +-
 drivers/gpu/drm/radeon/radeon_mode.h    |    2 +-
 include/drm/drm_fb_helper.h             |   12 ++++-
 6 files changed, 101 insertions(+), 12 deletions(-)

diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
index 305c590..be5aa7d 100644
--- a/drivers/gpu/drm/Kconfig
+++ b/drivers/gpu/drm/Kconfig
@@ -23,6 +23,7 @@ config DRM_KMS_HELPER
 	depends on DRM
 	select FB
 	select FRAMEBUFFER_CONSOLE if !EMBEDDED
+	select SLOW_WORK
 	help
 	  FB and CRTC helpers for KMS drivers.
 
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 8192a56..f879b3a 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -1306,9 +1306,14 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 	/*
 	 * we shouldn't end up with no modes here.
 	 */
-	if (count == 0)
-		printk(KERN_INFO "No connectors reported connected with modes\n");
-
+	if (count == 0) {
+		if (fb_helper->poll_enabled) {
+			delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work,
+						  5*HZ);
+			printk(KERN_INFO "No connectors reported connected with modes - started polling\n");
+		} else
+			printk(KERN_INFO "No connectors reported connected with modes\n");
+	}
 	drm_setup_crtcs(fb_helper);
 
 	return 0;
@@ -1316,15 +1321,80 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 EXPORT_SYMBOL(drm_fb_helper_initial_config);
 
 bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
-				 u32 max_width, u32 max_height)
+				 u32 max_width, u32 max_height, bool polled)
 {
+	int count = 0;
+	int ret;
 	DRM_DEBUG_KMS("\n");
 
-	drm_fb_helper_probe_connector_modes(fb_helper, max_width,
+	count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
 						    max_height);
-
+	if (fb_helper->poll_enabled && !polled) {
+		if (count) {
+			delayed_slow_work_cancel(&fb_helper->output_poll_slow_work);
+		} else {
+			ret = delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work, 5*HZ);
+		}
+	}
 	drm_setup_crtcs(fb_helper);
 
 	return true;
 }
 EXPORT_SYMBOL(drm_helper_fb_hotplug_event);
+
+static void output_poll_execute(struct slow_work *work)
+{
+	struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work);
+	struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_poll_slow_work);
+	struct drm_device *dev = fb_helper->dev;
+	struct drm_connector *connector;
+	enum drm_connector_status old_status, status;
+	bool repoll = true, changed = false;
+	int ret;
+
+	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+		old_status = connector->status;
+		status = connector->funcs->detect(connector);
+		if (old_status != status) {
+			changed = true;
+			/* something changed */
+		}
+		if (status == connector_status_connected) {
+			DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector));
+			repoll = false;
+		}
+	}
+
+	if (repoll) {
+		ret = delayed_slow_work_enqueue(delayed_work, 5*HZ);
+		if (ret)
+			DRM_ERROR("delayed enqueue failed %d\n", ret);
+	}
+
+	if (changed) {
+		if (fb_helper->fb_poll_changed)
+			fb_helper->fb_poll_changed(fb_helper);
+	}
+}
+
+struct slow_work_ops output_poll_ops = {
+	.execute = output_poll_execute,
+};
+
+void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper)
+{
+	int ret;
+
+	ret = slow_work_register_user(THIS_MODULE);
+
+	delayed_slow_work_init(&fb_helper->output_poll_slow_work, &output_poll_ops);
+	fb_helper->poll_enabled = true;
+}
+EXPORT_SYMBOL(drm_fb_helper_poll_init);
+
+void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper)
+{
+	delayed_slow_work_cancel(&fb_helper->output_poll_slow_work);
+	slow_work_unregister_user(THIS_MODULE);
+}
+EXPORT_SYMBOL(drm_fb_helper_poll_fini);
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 01a9d3b..867ecd5 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -321,18 +321,23 @@ static int radeonfb_probe(struct radeon_fbdev *rfbdev)
 	return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel);
 }
 
-void radeonfb_hotplug(struct drm_device *dev)
+void radeonfb_hotplug(struct drm_device *dev, bool polled)
 {
 	struct radeon_device *rdev = dev->dev_private;
 	int max_width, max_height;
 
 	max_width = rdev->mode_info.rfbdev->rfb.base.width;
 	max_height = rdev->mode_info.rfbdev->rfb.base.height;
-	drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height);
+	drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height, polled);
 
 	radeonfb_probe(rdev->mode_info.rfbdev);
 }
 
+static void radeon_fb_poll_changed(struct drm_fb_helper *fb_helper)
+{
+	radeonfb_hotplug(fb_helper->dev, true);
+}
+
 static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
 {
 	struct fb_info *info;
@@ -381,8 +386,12 @@ int radeon_fbdev_init(struct radeon_device *rdev)
 
 	drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
 
+	rfbdev->helper.fb_poll_changed = radeon_fb_poll_changed;
+	drm_fb_helper_poll_init(&rfbdev->helper);
+
 	drm_fb_helper_initial_config(&rfbdev->helper);
 	radeonfb_probe(rfbdev);
+
 	return 0;
 
 }
@@ -392,6 +401,7 @@ void radeon_fbdev_fini(struct radeon_device *rdev)
 	if (!rdev->mode_info.rfbdev)
 		return;
 
+	drm_fb_helper_poll_fini(&rdev->mode_info.rfbdev->helper);
 	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
 	kfree(rdev->mode_info.rfbdev);
 	rdev->mode_info.rfbdev = NULL;
diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
index 19ed835..2580807 100644
--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
@@ -55,7 +55,7 @@ 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);
+	radeonfb_hotplug(dev, false);
 
 	drm_sysfs_hotplug_event(dev);
 }
diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
index 847ddbb..f40f9bf 100644
--- a/drivers/gpu/drm/radeon/radeon_mode.h
+++ b/drivers/gpu/drm/radeon/radeon_mode.h
@@ -591,5 +591,5 @@ 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);
+void radeonfb_hotplug(struct drm_device *dev, bool polled);
 #endif
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 50094f9..a073d73 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -30,6 +30,8 @@
 #ifndef DRM_FB_HELPER_H
 #define DRM_FB_HELPER_H
 
+#include <linux/slow-work.h>
+
 struct drm_fb_helper_crtc {
 	uint32_t crtc_id;
 	struct drm_mode_set mode_set;
@@ -86,8 +88,12 @@ struct drm_fb_helper {
 	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
 
+	struct delayed_slow_work output_poll_slow_work;
+	bool poll_enabled;
 	int (*fb_probe)(struct drm_fb_helper *helper,
 			struct drm_fb_helper_surface_size *sizes);
+
+	void (*fb_poll_changed)(struct drm_fb_helper *helper);
 };
 
 int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper,
@@ -118,9 +124,11 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
 
-bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, u32 max_width,
-				 u32 max_height);
+bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
+				 u32 max_width, u32 max_height, bool polled);
 bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper);
 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
 
+void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper);
+void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper);
 #endif
-- 
1.6.5.2


------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [PATCH 6/6] drm/kms/fb: use slow work mechanism for normal hotplug also.
  2010-03-30  5:34         ` [PATCH 5/6] drm/kms/fb: add polling support for when nothing is connected Dave Airlie
@ 2010-03-30  5:34           ` Dave Airlie
  0 siblings, 0 replies; 7+ messages in thread
From: Dave Airlie @ 2010-03-30  5:34 UTC (permalink / raw)
  To: dri-devel

From: Dave Airlie <airlied@redhat.com>

a) slow work is always used now for any fbcon hotplug, as its not
   a fast task and is more suited to being ran under slow work.

b) attempt to not do any fbdev changes when X is running as we'll
   just mess it up. This hooks set_par to hopefully do the changes
   once X hands control to fbdev.

This also adds the nouveau/intel hotplug support.

Signed-off-by: Dave Airlie <airlied@redhat.com>
---
 drivers/gpu/drm/drm_fb_helper.c         |  209 ++++++++++++++++++------------
 drivers/gpu/drm/i915/i915_irq.c         |    1 +
 drivers/gpu/drm/i915/intel_drv.h        |    2 +
 drivers/gpu/drm/i915/intel_fb.c         |   42 ++++---
 drivers/gpu/drm/nouveau/nouveau_fbcon.c |   47 +++++---
 drivers/gpu/drm/nouveau/nouveau_fbcon.h |    2 +
 drivers/gpu/drm/nouveau/nv50_display.c  |    3 +
 drivers/gpu/drm/radeon/radeon_fb.c      |   74 +++++------
 include/drm/drm_fb_helper.h             |   47 ++++---
 9 files changed, 243 insertions(+), 184 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index f879b3a..3757a25 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -41,6 +41,8 @@ MODULE_LICENSE("GPL and additional rights");
 
 static LIST_HEAD(kernel_fb_helper_list);
 
+static struct slow_work_ops output_status_change_ops;
+
 /* simple single crtc case helper function */
 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper)
 {
@@ -418,54 +420,81 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper)
 	kfree(helper->crtc_info);
 }
 
-int drm_fb_helper_init_crtc_count(struct drm_device *dev,
-				  struct drm_fb_helper *helper,
-				  int crtc_count, int max_conn_count)
+int drm_fb_helper_init(struct drm_device *dev,
+		       struct drm_fb_helper *fb_helper,
+		       int crtc_count, int max_conn_count,
+		       bool polled)
 {
 	struct drm_crtc *crtc;
 	int ret = 0;
 	int i;
 
-	INIT_LIST_HEAD(&helper->kernel_fb_list);
-	helper->dev = dev;
-	helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
-	if (!helper->crtc_info)
+	fb_helper->dev = dev;
+	fb_helper->poll_enabled = polled;
+
+	slow_work_register_user(THIS_MODULE);
+	delayed_slow_work_init(&fb_helper->output_status_change_slow_work,
+			       &output_status_change_ops);
+
+	INIT_LIST_HEAD(&fb_helper->kernel_fb_list);
+
+	fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL);
+	if (!fb_helper->crtc_info)
 		return -ENOMEM;
-	helper->crtc_count = crtc_count;
 
-	helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
-	if (!helper->connector_info) {
-		kfree(helper->crtc_info);
+	fb_helper->crtc_count = crtc_count;
+	fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL);
+	if (!fb_helper->connector_info) {
+		kfree(fb_helper->crtc_info);
 		return -ENOMEM;
 	}
-	helper->connector_count = 0;
+	fb_helper->connector_count = 0;
 
 	for (i = 0; i < crtc_count; i++) {
-		helper->crtc_info[i].mode_set.connectors =
+		fb_helper->crtc_info[i].mode_set.connectors =
 			kcalloc(max_conn_count,
 				sizeof(struct drm_connector *),
 				GFP_KERNEL);
 
-		if (!helper->crtc_info[i].mode_set.connectors) {
+		if (!fb_helper->crtc_info[i].mode_set.connectors) {
 			ret = -ENOMEM;
 			goto out_free;
 		}
-		helper->crtc_info[i].mode_set.num_connectors = 0;
+		fb_helper->crtc_info[i].mode_set.num_connectors = 0;
 	}
 
 	i = 0;
 	list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
-		helper->crtc_info[i].crtc_id = crtc->base.id;
-		helper->crtc_info[i].mode_set.crtc = crtc;
+		fb_helper->crtc_info[i].crtc_id = crtc->base.id;
+		fb_helper->crtc_info[i].mode_set.crtc = crtc;
 		i++;
 	}
-	helper->conn_limit = max_conn_count;
+	fb_helper->conn_limit = max_conn_count;
 	return 0;
 out_free:
-	drm_fb_helper_crtc_free(helper);
+	drm_fb_helper_crtc_free(fb_helper);
 	return -ENOMEM;
 }
-EXPORT_SYMBOL(drm_fb_helper_init_crtc_count);
+EXPORT_SYMBOL(drm_fb_helper_init);
+
+void drm_fb_helper_fini(struct drm_fb_helper *fb_helper)
+{
+	if (!list_empty(&fb_helper->kernel_fb_list)) {
+		list_del(&fb_helper->kernel_fb_list);
+		if (list_empty(&kernel_fb_helper_list)) {
+			printk(KERN_INFO "unregistered panic notifier\n");
+			atomic_notifier_chain_unregister(&panic_notifier_list,
+							 &paniced);
+			unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
+		}
+	}
+
+	drm_fb_helper_crtc_free(fb_helper);
+
+	delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work);
+	slow_work_unregister_user(THIS_MODULE);
+}
+EXPORT_SYMBOL(drm_fb_helper_fini);
 
 static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green,
 		     u16 blue, u16 regno, struct fb_info *info)
@@ -708,6 +737,11 @@ int drm_fb_helper_set_par(struct fb_info *info)
 		}
 	}
 	mutex_unlock(&dev->mode_config.mutex);
+
+	if (fb_helper->delayed_hotplug) {
+		fb_helper->delayed_hotplug = false;
+		delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0);
+	}
 	return 0;
 }
 EXPORT_SYMBOL(drm_fb_helper_set_par);
@@ -749,7 +783,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 {
 	int new_fb = 0;
 	int crtc_count = 0;
-	int ret, i;
+	int i;
 	struct fb_info *info;
 	struct drm_fb_helper_surface_size sizes;
 	int gamma_size = 0;
@@ -825,7 +859,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 	}
 
 	/* push down into drivers */
-	new_fb = (*fb_helper->fb_probe)(fb_helper, &sizes);
+	new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
 	if (new_fb < 0)
 		return new_fb;
 
@@ -838,11 +872,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 
 	if (new_fb) {
 		info->var.pixclock = 0;
-		ret = fb_alloc_cmap(&info->cmap, gamma_size, 0);
-		if (ret)
-			return ret;
 		if (register_framebuffer(info) < 0) {
-			fb_dealloc_cmap(&info->cmap);
 			return -EINVAL;
 		}
 
@@ -868,23 +898,6 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
 }
 EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
 
-void drm_fb_helper_free(struct drm_fb_helper *helper)
-{
-	if (!list_empty(&helper->kernel_fb_list)) {
-		list_del(&helper->kernel_fb_list);
-		if (list_empty(&kernel_fb_helper_list)) {
-			printk(KERN_INFO "unregistered panic notifier\n");
-			atomic_notifier_chain_unregister(&panic_notifier_list,
-							 &paniced);
-			unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
-		}
-	}
-	drm_fb_helper_crtc_free(helper);
-	if (helper->fbdev->cmap.len)
-		fb_dealloc_cmap(&helper->fbdev->cmap);
-}
-EXPORT_SYMBOL(drm_fb_helper_free);
-
 void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 			    uint32_t depth)
 {
@@ -1289,7 +1302,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
  * RETURNS:
  * Zero if everything went ok, nonzero otherwise.
  */
-bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
+bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
 {
 	struct drm_device *dev = fb_helper->dev;
 	int count = 0;
@@ -1302,13 +1315,12 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 	count = drm_fb_helper_probe_connector_modes(fb_helper,
 						    dev->mode_config.max_width,
 						    dev->mode_config.max_height);
-
 	/*
 	 * we shouldn't end up with no modes here.
 	 */
 	if (count == 0) {
 		if (fb_helper->poll_enabled) {
-			delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work,
+			delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work,
 						  5*HZ);
 			printk(KERN_INFO "No connectors reported connected with modes - started polling\n");
 		} else
@@ -1316,85 +1328,114 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper)
 	}
 	drm_setup_crtcs(fb_helper);
 
-	return 0;
+	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
 }
 EXPORT_SYMBOL(drm_fb_helper_initial_config);
 
-bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
-				 u32 max_width, u32 max_height, bool polled)
+/* we got a hotplug irq - need to update fbcon */
+void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper)
+{
+	/* if we don't have the fbdev registered yet do nothing */
+	if (!fb_helper->fbdev)
+		return;
+
+	/* schedule a slow work asap */
+	delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0);
+}
+EXPORT_SYMBOL(drm_helper_fb_hpd_irq_event);
+
+bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, bool polled)
 {
 	int count = 0;
 	int ret;
-	DRM_DEBUG_KMS("\n");
+	u32 max_width, max_height, bpp_sel;
 
+	if (!fb_helper->fb)
+		return false;
+	DRM_DEBUG_KMS("\n");
+	
+	max_width = fb_helper->fb->width;
+	max_height = fb_helper->fb->height;
+	bpp_sel = fb_helper->fb->bits_per_pixel;
+      
 	count = drm_fb_helper_probe_connector_modes(fb_helper, max_width,
 						    max_height);
 	if (fb_helper->poll_enabled && !polled) {
 		if (count) {
-			delayed_slow_work_cancel(&fb_helper->output_poll_slow_work);
+			delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work);
 		} else {
-			ret = delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work, 5*HZ);
+			ret = delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ);
 		}
 	}
 	drm_setup_crtcs(fb_helper);
 
-	return true;
+	return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
 }
 EXPORT_SYMBOL(drm_helper_fb_hotplug_event);
 
-static void output_poll_execute(struct slow_work *work)
+/*
+ * delayed work queue execution function
+ * - check if fbdev is actually in use on the gpu
+ *   - if not set delayed flag and repoll if necessary
+ * - check for connector status change
+ * - repoll if 0 modes found
+ *- call driver output status changed notifier
+ */
+static void output_status_change_execute(struct slow_work *work)
 {
 	struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work);
-	struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_poll_slow_work);
-	struct drm_device *dev = fb_helper->dev;
+	struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_status_change_slow_work);
 	struct drm_connector *connector;
 	enum drm_connector_status old_status, status;
-	bool repoll = true, changed = false;
+	bool repoll, changed = false;
 	int ret;
+	int i;
+	bool bound = false, crtcs_bound = false;
+	struct drm_crtc *crtc;
 
-	list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+	repoll = fb_helper->poll_enabled;
+
+	/* first of all check the fbcon framebuffer is actually bound to any crtc */
+	/* take into account that no crtc at all maybe bound */
+	list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) {
+		if (crtc->fb)
+			crtcs_bound = true;
+		if (crtc->fb == fb_helper->fb)
+			bound = true;
+	}
+
+	if (bound == false && crtcs_bound) {
+		fb_helper->delayed_hotplug = true;
+		goto requeue;
+	}
+
+	for (i = 0; i < fb_helper->connector_count; i++) {
+		connector = fb_helper->connector_info[i]->connector;
 		old_status = connector->status;
 		status = connector->funcs->detect(connector);
 		if (old_status != status) {
 			changed = true;
-			/* something changed */
 		}
-		if (status == connector_status_connected) {
+		if (status == connector_status_connected && repoll) {
 			DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector));
 			repoll = false;
 		}
 	}
 
+	if (changed) {
+		if (fb_helper->funcs->fb_output_status_changed)
+			fb_helper->funcs->fb_output_status_changed(fb_helper);
+	}
+
+requeue:
 	if (repoll) {
 		ret = delayed_slow_work_enqueue(delayed_work, 5*HZ);
 		if (ret)
 			DRM_ERROR("delayed enqueue failed %d\n", ret);
 	}
-
-	if (changed) {
-		if (fb_helper->fb_poll_changed)
-			fb_helper->fb_poll_changed(fb_helper);
-	}
 }
 
-struct slow_work_ops output_poll_ops = {
-	.execute = output_poll_execute,
+static struct slow_work_ops output_status_change_ops = {
+	.execute = output_status_change_execute,
 };
 
-void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper)
-{
-	int ret;
-
-	ret = slow_work_register_user(THIS_MODULE);
-
-	delayed_slow_work_init(&fb_helper->output_poll_slow_work, &output_poll_ops);
-	fb_helper->poll_enabled = true;
-}
-EXPORT_SYMBOL(drm_fb_helper_poll_init);
-
-void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper)
-{
-	delayed_slow_work_cancel(&fb_helper->output_poll_slow_work);
-	slow_work_unregister_user(THIS_MODULE);
-}
-EXPORT_SYMBOL(drm_fb_helper_poll_fini);
diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
index 5388354..fdf08f2 100644
--- a/drivers/gpu/drm/i915/i915_irq.c
+++ b/drivers/gpu/drm/i915/i915_irq.c
@@ -266,6 +266,7 @@ static void i915_hotplug_work_func(struct work_struct *work)
 		}
 	}
 	/* Just fire off a uevent and let userspace tell us what to do */
+	intelfb_hotplug(dev, false);
 	drm_sysfs_hotplug_event(dev);
 }
 
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index 9ffb9f2..11fce59 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -228,4 +228,6 @@ extern int intel_overlay_put_image(struct drm_device *dev, void *data,
 				   struct drm_file *file_priv);
 extern int intel_overlay_attrs(struct drm_device *dev, void *data,
 			       struct drm_file *file_priv);
+
+void intelfb_hotplug(struct drm_device *dev, bool polled);
 #endif /* __INTEL_DRV_H__ */
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
index dc126b8..e722f42 100644
--- a/drivers/gpu/drm/i915/intel_fb.c
+++ b/drivers/gpu/drm/i915/intel_fb.c
@@ -65,12 +65,6 @@ static struct fb_ops intelfb_ops = {
 	.fb_setcmap = drm_fb_helper_setcmap,
 };
 
-static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
-	.gamma_set = intel_crtc_fb_gamma_set,
-	.gamma_get = intel_crtc_fb_gamma_get,
-};
-
-
 static int intelfb_create(struct intel_fbdev *ifbdev,
 			  struct drm_fb_helper_surface_size *sizes)
 {
@@ -129,7 +123,6 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
 
 	ifbdev->helper.fb = fb;
 	ifbdev->helper.fbdev = info;
-	ifbdev->helper.funcs = &intel_fb_helper_funcs;
 
 	strcpy(info->fix.id, "inteldrmfb");
 
@@ -154,6 +147,12 @@ static int intelfb_create(struct intel_fbdev *ifbdev,
 		ret = -ENOSPC;
 		goto out_unpin;
 	}
+
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret) {
+		ret = -ENOMEM;
+		goto out_unpin;
+	}
 	info->screen_size = size;
 
 //	memset(info->screen_base, 0, size);
@@ -205,15 +204,18 @@ static int intel_fb_find_or_create_single(struct drm_fb_helper *helper,
 	return new_fb;
 }
 
-static int intelfb_probe(struct intel_fbdev *ifbdev)
+void intelfb_hotplug(struct drm_device *dev, bool polled)
 {
-	int ret;
-
-	DRM_DEBUG_KMS("\n");
-	ret = drm_fb_helper_single_fb_probe(&ifbdev->helper, 32);
-	return ret;
+	drm_i915_private_t *dev_priv = dev->dev_private;
+	drm_helper_fb_hpd_irq_event(&dev_priv->fbdev->helper);
 }
 
+static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+	.gamma_set = intel_crtc_fb_gamma_set,
+	.gamma_get = intel_crtc_fb_gamma_get,
+	.fb_probe = intel_fb_find_or_create_single,
+};
+
 int intel_fbdev_destroy(struct drm_device *dev,
 			struct intel_fbdev *ifbdev)
 {
@@ -224,10 +226,12 @@ int intel_fbdev_destroy(struct drm_device *dev,
 		info = ifbdev->helper.fbdev;
 		unregister_framebuffer(info);
 		iounmap(info->screen_base);
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
 		framebuffer_release(info);
 	}
 
-	drm_fb_helper_free(&ifbdev->helper);
+	drm_fb_helper_fini(&ifbdev->helper);
 
 	drm_framebuffer_cleanup(&ifb->base);
 	if (ifb->obj)
@@ -246,13 +250,13 @@ int intel_fbdev_init(struct drm_device *dev)
 		return -ENOMEM;
 
 	dev_priv->fbdev = ifbdev;
+	ifbdev->helper.funcs = &intel_fb_helper_funcs;
+
+	drm_fb_helper_init(dev, &ifbdev->helper, 2,
+			   INTELFB_CONN_LIMIT, false);
 
-	drm_fb_helper_init_crtc_count(dev, &ifbdev->helper, 2,
-				      INTELFB_CONN_LIMIT);
 	drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
-	ifbdev->helper.fb_probe = intel_fb_find_or_create_single;
-	drm_fb_helper_initial_config(&ifbdev->helper);
-	intelfb_probe(ifbdev);
+	drm_fb_helper_initial_config(&ifbdev->helper, 32);
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index fd5d3cd..d7cb238 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -156,11 +156,6 @@ static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
 	*blue = nv_crtc->lut.b[regno];
 }
 
-static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
-	.gamma_set = nouveau_fbcon_gamma_set,
-	.gamma_get = nouveau_fbcon_gamma_get
-};
-
 #if defined(__i386__) || defined(__x86_64__)
 static bool
 nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev)
@@ -272,6 +267,12 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
 		goto out_unref;
 	}
 
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret) {
+		ret = -ENOMEM;
+		goto out_unref;
+	}
+
 	info->par = nfbdev;
 
 	nouveau_framebuffer_init(dev, &nfbdev->nouveau_fb, &mode_cmd, nvbo);
@@ -282,7 +283,6 @@ nouveau_fbcon_create(struct nouveau_fbdev *nfbdev,
 	/* setup helper */
 	nfbdev->helper.fb = fb;
 	nfbdev->helper.fbdev = info;
-	nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs;
 
 	strcpy(info->fix.id, "nouveaufb");
 	if (nouveau_nofbaccel)
@@ -381,12 +381,15 @@ nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper,
 	return new_fb;
 }
 
-static int
-nouveau_fbcon_probe(struct nouveau_fbdev *nfbdev)
+void nouveau_fbcon_hotplug(struct drm_device *dev)
 {
-	NV_DEBUG_KMS(nfbdev->dev, "\n");
+	struct drm_nouveau_private *dev_priv = dev->dev_private;
+	drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper);
+}
 
-	return drm_fb_helper_single_fb_probe(&nfbdev->helper, 32);
+static void nouveau_fbcon_output_status_changed(struct drm_fb_helper *fb_helper)
+{
+	drm_helper_fb_hotplug_event(fb_helper, true);
 }
 
 int
@@ -398,6 +401,8 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
 	if (nfbdev->helper.fbdev) {
 		info = nfbdev->helper.fbdev;
 		unregister_framebuffer(info);
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
 		framebuffer_release(info);
 	}
 
@@ -406,7 +411,7 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
 		drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem);
 		nouveau_fb->nvbo = NULL;
 	}
-	drm_fb_helper_free(&nfbdev->helper);
+	drm_fb_helper_fini(&nfbdev->helper);
 	drm_framebuffer_cleanup(&nouveau_fb->base);
 	return 0;
 }
@@ -420,6 +425,14 @@ void nouveau_fbcon_gpu_lockup(struct fb_info *info)
 	info->flags |= FBINFO_HWACCEL_DISABLED;
 }
 
+static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
+	.gamma_set = nouveau_fbcon_gamma_set,
+	.gamma_get = nouveau_fbcon_gamma_get,
+	.fb_probe = nouveau_fbcon_find_or_create_single,
+	.fb_output_status_changed = nouveau_fbcon_output_status_changed,
+};
+
+
 int nouveau_fbcon_init(struct drm_device *dev)
 {
 	struct drm_nouveau_private *dev_priv = dev->dev_private;
@@ -431,14 +444,12 @@ int nouveau_fbcon_init(struct drm_device *dev)
 
 	nfbdev->dev = dev;
 	dev_priv->nfbdev = nfbdev;
-
-	drm_fb_helper_init_crtc_count(dev, &nfbdev->helper,
-				      2, 4);
-	nfbdev->helper.fb_probe = nouveau_fbcon_find_or_create_single;
+	nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs;
+		
+	drm_fb_helper_init(dev, &nfbdev->helper,
+			   2, 4, true);
 	drm_fb_helper_single_add_all_connectors(&nfbdev->helper);
-
-	drm_fb_helper_initial_config(&nfbdev->helper);
-	nouveau_fbcon_probe(nfbdev);
+	drm_fb_helper_initial_config(&nfbdev->helper, 32);
 	return 0;
 }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
index 7835b56..bf8e00d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
@@ -57,5 +57,7 @@ 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);
+
+void nouveau_fbcon_hotplug(struct drm_device *dev);
 #endif /* __NV50_FBCON_H__ */
 
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index fac6c88..d4f06c2 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -29,6 +29,7 @@
 #include "nouveau_encoder.h"
 #include "nouveau_connector.h"
 #include "nouveau_fb.h"
+#include "nouveau_fbcon.h"
 #include "drm_crtc_helper.h"
 
 static void
@@ -941,6 +942,8 @@ nv50_display_irq_hotplug(struct drm_device *dev)
 	nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054));
 	if (dev_priv->chipset >= 0x90)
 		nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));
+
+	nouveau_fbcon_hotplug(dev);
 }
 
 void
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index 867ecd5..79a6e6f 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -86,11 +86,6 @@ static int radeon_align_pitch(struct radeon_device *rdev, int width, int bpp, bo
 	return aligned;
 }
 
-static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
-	.gamma_set = radeon_crtc_fb_gamma_set,
-	.gamma_get = radeon_crtc_fb_gamma_get,
-};
-
 static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj)
 {
 	struct radeon_bo *rbo = gobj->driver_private;
@@ -222,7 +217,6 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev,
 	/* setup helper */
 	rfbdev->helper.fb = fb;
 	rfbdev->helper.fbdev = info;
-	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
 
 	memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
 
@@ -252,10 +246,18 @@ static int radeonfb_create(struct radeon_fbdev *rfbdev,
 	info->pixmap.access_align = 32;
 	info->pixmap.flags = FB_PIXMAP_SYSTEM;
 	info->pixmap.scan_align = 1;
+
 	if (info->screen_base == NULL) {
 		ret = -ENOSPC;
 		goto out_unref;
 	}
+
+	ret = fb_alloc_cmap(&info->cmap, 256, 0);
+	if (ret) {
+		ret = -ENOMEM;
+		goto out_unref;
+	}
+
 	DRM_INFO("fb mappable at 0x%lX\n",  info->fix.smem_start);
 	DRM_INFO("vram apper at 0x%lX\n",  (unsigned long)rdev->mc.aper_base);
 	DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo));
@@ -309,33 +311,16 @@ int radeon_parse_options(char *options)
 	return 0;
 }
 
-static int radeonfb_probe(struct radeon_fbdev *rfbdev)
-{
-	struct radeon_device *rdev = rfbdev->rdev;
-	int bpp_sel = 32;
-
-	/* select 8 bpp console on RN50 or 16MB cards */
-	if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
-		bpp_sel = 8;
-
-	return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel);
-}
-
 void radeonfb_hotplug(struct drm_device *dev, bool polled)
 {
 	struct radeon_device *rdev = dev->dev_private;
-	int max_width, max_height;
-
-	max_width = rdev->mode_info.rfbdev->rfb.base.width;
-	max_height = rdev->mode_info.rfbdev->rfb.base.height;
-	drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height, polled);
 
-	radeonfb_probe(rdev->mode_info.rfbdev);
+	drm_helper_fb_hpd_irq_event(&rdev->mode_info.rfbdev->helper);
 }
 
-static void radeon_fb_poll_changed(struct drm_fb_helper *fb_helper)
+static void radeon_fb_output_status_changed(struct drm_fb_helper *fb_helper)
 {
-	radeonfb_hotplug(fb_helper->dev, true);
+	drm_helper_fb_hotplug_event(fb_helper, true);
 }
 
 static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
@@ -347,7 +332,10 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
 
 	if (rfbdev->helper.fbdev) {
 		info = rfbdev->helper.fbdev;
+
 		unregister_framebuffer(info);
+		if (info->cmap.len)
+			fb_dealloc_cmap(&info->cmap);
 		framebuffer_release(info);
 	}
 
@@ -361,16 +349,27 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
 		}
 		drm_gem_object_unreference_unlocked(rfb->obj);
 	}
-	drm_fb_helper_free(&rfbdev->helper);
+	drm_fb_helper_fini(&rfbdev->helper);
 	drm_framebuffer_cleanup(&rfb->base);
 
 	return 0;
 }
-MODULE_LICENSE("GPL");
+
+static struct drm_fb_helper_funcs radeon_fb_helper_funcs = {
+	.gamma_set = radeon_crtc_fb_gamma_set,
+	.gamma_get = radeon_crtc_fb_gamma_get,
+	.fb_probe = radeon_fb_find_or_create_single,
+	.fb_output_status_changed = radeon_fb_output_status_changed,
+};
 
 int radeon_fbdev_init(struct radeon_device *rdev)
 {
 	struct radeon_fbdev *rfbdev;
+	int bpp_sel = 32;
+
+	/* select 8 bpp console on RN50 or 16MB cards */
+	if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
+		bpp_sel = 8;
 
 	rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL);
 	if (!rfbdev)
@@ -378,20 +377,13 @@ int radeon_fbdev_init(struct radeon_device *rdev)
 
 	rfbdev->rdev = rdev;
 	rdev->mode_info.rfbdev = rfbdev;
+	rfbdev->helper.funcs = &radeon_fb_helper_funcs;
 
-	drm_fb_helper_init_crtc_count(rdev->ddev, &rfbdev->helper,
-				      rdev->num_crtc,
-				      RADEONFB_CONN_LIMIT);
-	rfbdev->helper.fb_probe = radeon_fb_find_or_create_single;
-
+	drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
+			   rdev->num_crtc,
+			   RADEONFB_CONN_LIMIT, true);
 	drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
-
-	rfbdev->helper.fb_poll_changed = radeon_fb_poll_changed;
-	drm_fb_helper_poll_init(&rfbdev->helper);
-
-	drm_fb_helper_initial_config(&rfbdev->helper);
-	radeonfb_probe(rfbdev);
-
+	drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
 	return 0;
 
 }
@@ -401,7 +393,6 @@ void radeon_fbdev_fini(struct radeon_device *rdev)
 	if (!rdev->mode_info.rfbdev)
 		return;
 
-	drm_fb_helper_poll_fini(&rdev->mode_info.rfbdev->helper);
 	radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
 	kfree(rdev->mode_info.rfbdev);
 	rdev->mode_info.rfbdev = NULL;
@@ -428,4 +419,3 @@ bool radeon_fbdev_robj_is_fb(struct radeon_device *rdev, struct radeon_bo *robj)
 		return true;
 	return false;
 }
-
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index a073d73..9b55a94 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -32,20 +32,14 @@
 
 #include <linux/slow-work.h>
 
+struct drm_fb_helper;
+
 struct drm_fb_helper_crtc {
 	uint32_t crtc_id;
 	struct drm_mode_set mode_set;
 	struct drm_display_mode *desired_mode;
 };
 
-
-struct drm_fb_helper_funcs {
-	void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green,
-			  u16 blue, int regno);
-	void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green,
-			  u16 *blue, int regno);
-};
-
 /* mode specified on the command line */
 struct drm_fb_helper_cmdline_mode {
 	bool specified;
@@ -69,6 +63,19 @@ struct drm_fb_helper_surface_size {
 	u32 surface_depth;
 };
 
+struct drm_fb_helper_funcs {
+	void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green,
+			  u16 blue, int regno);
+	void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green,
+			  u16 *blue, int regno);
+
+	int (*fb_probe)(struct drm_fb_helper *helper,
+			struct drm_fb_helper_surface_size *sizes);
+
+	void (*fb_output_status_changed)(struct drm_fb_helper *helper);
+
+};
+
 struct drm_fb_helper_connector {
 	struct drm_fb_helper_cmdline_mode cmdline_mode;
 	struct drm_connector *connector;
@@ -88,21 +95,20 @@ struct drm_fb_helper {
 	u32 pseudo_palette[17];
 	struct list_head kernel_fb_list;
 
-	struct delayed_slow_work output_poll_slow_work;
+	struct delayed_slow_work output_status_change_slow_work;
 	bool poll_enabled;
-	int (*fb_probe)(struct drm_fb_helper *helper,
-			struct drm_fb_helper_surface_size *sizes);
-
-	void (*fb_poll_changed)(struct drm_fb_helper *helper);
+	/* we got a hotplug but fbdev wasn't running the console
+	   delay until next set_par */
+	bool delayed_hotplug;
 };
 
 int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper,
 				  int preferred_bpp);
 
-int drm_fb_helper_init_crtc_count(struct drm_device *dev,
-				  struct drm_fb_helper *helper, int crtc_count,
-				  int max_conn);
-void drm_fb_helper_free(struct drm_fb_helper *helper);
+int drm_fb_helper_init(struct drm_device *dev,
+		       struct drm_fb_helper *helper, int crtc_count,
+		       int max_conn, bool polled);
+void drm_fb_helper_fini(struct drm_fb_helper *helper);
 int drm_fb_helper_blank(int blank, struct fb_info *info);
 int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
 			      struct fb_info *info);
@@ -125,10 +131,9 @@ void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
 int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info);
 
 bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper,
-				 u32 max_width, u32 max_height, bool polled);
-bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper);
+				 bool polled);
+bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel);
 int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper);
 
-void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper);
-void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper);
+void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper);
 #endif
-- 
1.6.5.2


------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--

^ permalink raw reply related	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2010-03-30  5:34 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2010-03-30  5:34 drm/kms/fb: clean up fbdev integration + add fbcon polling/hotplug Dave Airlie
2010-03-30  5:34 ` [PATCH 1/6] drm/fb: fix fbdev object model + cleanup properly Dave Airlie
2010-03-30  5:34   ` [PATCH 2/6] drm/kms/fb: move to using fb helper crtc grouping instead of core crtc list Dave Airlie
2010-03-30  5:34     ` [PATCH 3/6] drm/kms/fb: separate fbdev connector list from core drm connectors Dave Airlie
2010-03-30  5:34       ` [PATCH 4/6] drm/kms/fb: provide a 1024x768 fbcon if no outputs found Dave Airlie
2010-03-30  5:34         ` [PATCH 5/6] drm/kms/fb: add polling support for when nothing is connected Dave Airlie
2010-03-30  5:34           ` [PATCH 6/6] drm/kms/fb: use slow work mechanism for normal hotplug also Dave Airlie

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.