Merge branch 'drm-fbdev-cleanup' into drm-core-next
authorDave Airlie <airlied@redhat.com>
Tue, 20 Apr 2010 03:16:04 +0000 (13:16 +1000)
committerDave Airlie <airlied@redhat.com>
Tue, 20 Apr 2010 03:16:04 +0000 (13:16 +1000)
* drm-fbdev-cleanup:
  drm/fb: remove drm_fb_helper_setcolreg
  drm/kms/fb: use slow work mechanism for normal hotplug also.
  drm/kms/fb: add polling support for when nothing is connected.
  drm/kms/fb: provide a 1024x768 fbcon if no outputs found.
  drm/kms/fb: separate fbdev connector list from core drm connectors
  drm/kms/fb: move to using fb helper crtc grouping instead of core crtc list
  drm/fb: fix fbdev object model + cleanup properly.

Conflicts:
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/nouveau/nouveau_drv.h

19 files changed:
1  2 
drivers/gpu/drm/drm_crtc.c
drivers/gpu/drm/drm_fb_helper.c
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fb.c
drivers/gpu/drm/nouveau/nouveau_drv.h
drivers/gpu/drm/nouveau/nouveau_fbcon.c
drivers/gpu/drm/nouveau/nouveau_irq.c
drivers/gpu/drm/nouveau/nouveau_state.c
drivers/gpu/drm/nouveau/nv50_display.c
drivers/gpu/drm/nouveau/nv50_fbcon.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_connectors.c
drivers/gpu/drm/radeon/radeon_device.c
drivers/gpu/drm/radeon/radeon_fb.c
include/drm/drm_crtc.h

   *      Jesse Barnes <jesse.barnes@intel.com>
   */
  #include <linux/list.h>
 +#include <linux/slab.h>
  #include "drm.h"
  #include "drmP.h"
  #include "drm_crtc.h"
 +#include "drm_edid.h"
  
  struct drm_prop_enum_list {
        int type;
@@@ -495,7 -493,6 +495,6 @@@ void drm_connector_cleanup(struct drm_c
        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);
@@@ -859,7 -856,6 +858,6 @@@ void drm_mode_config_init(struct drm_de
        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);
@@@ -2351,7 -2347,7 +2349,7 @@@ int drm_mode_connector_update_edid_prop
                                            struct edid *edid)
  {
        struct drm_device *dev = connector->dev;
 -      int ret = 0;
 +      int ret = 0, size;
  
        if (connector->edid_blob_ptr)
                drm_property_destroy_blob(dev, connector->edid_blob_ptr);
                return ret;
        }
  
 -      connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 128, edid);
 +      size = EDID_LENGTH * (1 + edid->extensions);
 +      connector->edid_blob_ptr = drm_property_create_blob(connector->dev,
 +                                                          size, edid);
  
        ret = drm_connector_property_set_value(connector,
                                               dev->mode_config.edid_property,
@@@ -29,7 -29,6 +29,7 @@@
   */
  #include <linux/kernel.h>
  #include <linux/sysrq.h>
 +#include <linux/slab.h>
  #include <linux/fb.h>
  #include "drmP.h"
  #include "drm_crtc.h"
@@@ -42,15 -41,35 +42,35 @@@ MODULE_LICENSE("GPL and additional righ
  
  static LIST_HEAD(kernel_fb_helper_list);
  
- int drm_fb_helper_add_connector(struct drm_connector *connector)
+ 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)
  {
-       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
@@@ -65,7 -84,7 +85,7 @@@
   *
   * 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;
        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;
  
@@@ -204,18 -223,21 +224,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;
  }
@@@ -293,6 -315,7 +316,7 @@@ static void drm_fb_helper_on(struct fb_
        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;
  
         * 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)
        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;
  
         * 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)
@@@ -401,50 -414,89 +415,89 @@@ static void drm_fb_helper_crtc_free(str
  {
        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);
  }
  
- int drm_fb_helper_init_crtc_count(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_device *dev = helper->dev;
        struct drm_crtc *crtc;
        int ret = 0;
        int i;
  
-       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;
+       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;
+       }
+       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)
  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;
  }
  EXPORT_SYMBOL(drm_fb_helper_setcmap);
  
- int drm_fb_helper_setcolreg(unsigned regno,
-                           unsigned red,
-                           unsigned green,
-                           unsigned blue,
-                           unsigned transp,
-                           struct fb_info *info)
- {
-       struct drm_fb_helper *fb_helper = info->par;
-       struct drm_device *dev = fb_helper->dev;
-       struct drm_crtc *crtc;
-       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;
-               ret = setcolreg(crtc, red, green, blue, regno, info);
-               if (ret)
-                       return ret;
-               crtc_funcs->load_lut(crtc);
-       }
-       return 0;
- }
- EXPORT_SYMBOL(drm_fb_helper_setcolreg);
  int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
                            struct fb_info *info)
  {
@@@ -687,23 -699,21 +700,21 @@@ int drm_fb_helper_set_par(struct fb_inf
                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;
-               if (crtc->fb == fb_helper->crtc_info[i].mode_set.fb) {
-                       mutex_lock(&dev->mode_config.mutex);
-                       ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
+       mutex_lock(&dev->mode_config.mutex);
+       for (i = 0; i < fb_helper->crtc_count; i++) {
+               crtc = fb_helper->crtc_info[i].mode_set.crtc;
+               ret = crtc->funcs->set_config(&fb_helper->crtc_info[i].mode_set);
+               if (ret) {
                        mutex_unlock(&dev->mode_config.mutex);
-                       if (ret)
-                               return ret;
+                       return ret;
                }
        }
+       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);
@@@ -718,14 -728,9 +729,9 @@@ int drm_fb_helper_pan_display(struct fb
        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;
  
                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_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 drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
+                                 int preferred_bpp)
  {
-       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;
+       int i;
        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;
+       int gamma_size = 0;
+       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) {
-               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) {
                        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;
                }
        }
  
-       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->vdisplay < fb_height)
-                                       fb_height = crtc->desired_mode->vdisplay;
-                               if (crtc->desired_mode->hdisplay > surface_width)
-                                       surface_width = crtc->desired_mode->hdisplay;
-                               if (crtc->desired_mode->vdisplay > surface_height)
-                                       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++;
                }
        }
  
-       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;
-               }
+               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;
        }
  
-       info = fb->fbdev;
-       fb_helper = info->par;
+       /* push down into drivers */
+       new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes);
+       if (new_fb < 0)
+               return new_fb;
  
-       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;
-               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();
-                               }
-               }
+       info = fb_helper->fbdev;
  
-               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;
-       fb_helper->fb = fb;
  
        if (new_fb) {
                info->var.pixclock = 0;
-               ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
-               if (ret)
-                       return ret;
                if (register_framebuffer(info) < 0) {
-                       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 */
                                               &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);
  
- 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);
-       }
-       drm_fb_helper_crtc_free(helper);
-       fb_dealloc_cmap(&helper->fb->fbdev->cmap);
- }
- EXPORT_SYMBOL(drm_fb_helper_free);
  void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch,
                            uint32_t depth)
  {
  }
  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;
        info->var.yres = fb_height;
  }
  EXPORT_SYMBOL(drm_fb_helper_fill_var);
+ 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;
+       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_fb_helper_connector *fb_connector, int width, int height)
+ {
+       struct drm_display_mode *mode;
+       list_for_each_entry(mode, &fb_connector->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_fb_helper_connector *fb_connector)
+ {
+       struct drm_fb_helper_cmdline_mode *cmdline_mode;
+       cmdline_mode = &fb_connector->cmdline_mode;
+       return cmdline_mode->specified;
+ }
+ 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_cmdline_mode *cmdline_mode;
+       struct drm_display_mode *mode = NULL;
+       cmdline_mode = &fb_helper_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, &fb_helper_conn->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(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, &fb_helper_conn->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_fb_helper *fb_helper,
+                                 bool *enabled)
+ {
+       bool any_enabled = false;
+       struct drm_connector *connector;
+       int i = 0;
+       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];
+       }
+       if (any_enabled)
+               return;
+       for (i = 0; i < fb_helper->connector_count; i++) {
+               connector = fb_helper->connector_info[i]->connector;
+               enabled[i] = drm_connector_enabled(connector, false);
+       }
+ }
+ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
+                                struct drm_display_mode **modes,
+                                bool *enabled, int width, int height)
+ {
+       struct drm_fb_helper_connector *fb_helper_conn;
+       int i;
+       for (i = 0; i < fb_helper->connector_count; i++) {
+               fb_helper_conn = fb_helper->connector_info[i];
+               if (enabled[i] == false)
+                       continue;
+               DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
+                             fb_helper_conn->connector->base.id);
+               /* got for command line mode first */
+               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",
+                                     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(&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");
+       }
+       return true;
+ }
+ 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_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->connector_count)
+               return 0;
+       fb_helper_conn = fb_helper->connector_info[n];
+       connector = fb_helper_conn->connector;
+       best_crtcs[n] = NULL;
+       best_crtc = NULL;
+       best_score = drm_pick_crtcs(fb_helper, best_crtcs, modes, n+1, width, height);
+       if (modes[n] == NULL)
+               return best_score;
+       crtcs = kzalloc(dev->mode_config.num_connector *
+                       sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
+       if (!crtcs)
+               return best_score;
+       my_score = 1;
+       if (connector->status == connector_status_connected)
+               my_score++;
+       if (drm_has_cmdline_mode(fb_helper_conn))
+               my_score++;
+       if (drm_has_preferred_mode(fb_helper_conn, width, height))
+               my_score++;
+       connector_funcs = connector->helper_private;
+       encoder = connector_funcs->best_encoder(connector);
+       if (!encoder)
+               goto out;
+       /* select a crtc for this connector and then attempt to configure
+          remaining connectors */
+       for (c = 0; c < fb_helper->crtc_count; c++) {
+               crtc = &fb_helper->crtc_info[c];
+               if ((encoder->possible_crtcs & (1 << c)) == 0) {
+                       continue;
+               }
+               for (o = 0; o < n; o++)
+                       if (best_crtcs[o] == crtc)
+                               break;
+               if (o < n) {
+                       /* ignore cloning for now */
+                       continue;
+               }
+               crtcs[n] = crtc;
+               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_fb_helper_crtc *));
+               }
+       }
+ out:
+       kfree(crtcs);
+       return best_score;
+ }
+ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
+ {
+       struct drm_device *dev = fb_helper->dev;
+       struct drm_fb_helper_crtc **crtcs;
+       struct drm_display_mode **modes;
+       struct drm_encoder *encoder;
+       struct drm_mode_set *modeset;
+       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_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,
+                         sizeof(bool), GFP_KERNEL);
+       drm_enable_connectors(fb_helper, enabled);
+       ret = drm_target_preferred(fb_helper, 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(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;
+       }
+       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;
+               if (mode && fb_crtc) {
+                       DRM_DEBUG_KMS("desired mode %s set on crtc %d\n",
+                                     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++] = fb_helper->connector_info[i]->connector;
+               }
+       }
+       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_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel)
+ {
+       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(fb_helper->dev);
+       drm_fb_helper_parse_command_line(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_status_change_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 drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
+ }
+ EXPORT_SYMBOL(drm_fb_helper_initial_config);
+ /* 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;
+       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_status_change_slow_work);
+               } else {
+                       ret = delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ);
+               }
+       }
+       drm_setup_crtcs(fb_helper);
+       return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
+ }
+ EXPORT_SYMBOL(drm_helper_fb_hotplug_event);
+ /*
+  * 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_status_change_slow_work);
+       struct drm_connector *connector;
+       enum drm_connector_status old_status, status;
+       bool repoll, changed = false;
+       int ret;
+       int i;
+       bool bound = false, crtcs_bound = false;
+       struct drm_crtc *crtc;
+       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;
+               }
+               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);
+       }
+ }
+ static struct slow_work_ops output_status_change_ops = {
+       .execute = output_status_change_execute,
+ };
@@@ -38,7 -38,6 +38,7 @@@
  #include <linux/acpi.h>
  #include <linux/pnp.h>
  #include <linux/vga_switcheroo.h>
 +#include <linux/slab.h>
  
  /* Really want an OS-independent resettable timer.  Would like to have
   * this loop run for (eg) 3 sec, but have the timer reset every time
@@@ -1492,7 -1491,7 +1492,7 @@@ static int i915_load_modeset_init(struc
  
        I915_WRITE(INSTPM, (1 << 5) | (1 << 21));
  
-       drm_helper_initial_config(dev);
+       intel_fbdev_init(dev);
  
        return 0;
  
@@@ -1579,7 -1578,7 +1579,7 @@@ static void i915_get_mem_freq(struct dr
   */
  int i915_driver_load(struct drm_device *dev, unsigned long flags)
  {
 -      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct drm_i915_private *dev_priv;
        resource_size_t base, size;
        int ret = 0, mmio_bar;
        uint32_t agp_size, prealloc_size, prealloc_start;
        /* Start out suspended */
        dev_priv->mm.suspended = 1;
  
 +      intel_detect_pch(dev);
 +
        if (drm_core_check_feature(dev, DRIVER_MODESET)) {
                ret = i915_load_modeset_init(dev, prealloc_start,
                                             prealloc_size, agp_size);
@@@ -1884,29 -1881,29 +1884,29 @@@ struct drm_ioctl_desc i915_ioctls[] = 
        DRM_IOCTL_DEF(DRM_I915_GET_VBLANK_PIPE,  i915_vblank_pipe_get, DRM_AUTH ),
        DRM_IOCTL_DEF(DRM_I915_VBLANK_SWAP, i915_vblank_swap, DRM_AUTH),
        DRM_IOCTL_DEF(DRM_I915_HWS_ADDR, i915_set_status_page, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_INIT, i915_gem_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER, i915_gem_execbuffer, DRM_AUTH),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER2, i915_gem_execbuffer2, DRM_AUTH),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_THROTTLE, i915_gem_throttle_ioctl, DRM_AUTH),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_ENTERVT, i915_gem_entervt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_LEAVEVT, i915_gem_leavevt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_CREATE, i915_gem_create_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_PREAD, i915_gem_pread_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0),
 -      DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, 0),
 -      DRM_IOCTL_DEF(DRM_I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW),
 -      DRM_IOCTL_DEF(DRM_I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_INIT, i915_gem_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER, i915_gem_execbuffer, DRM_AUTH|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER2, i915_gem_execbuffer2, DRM_AUTH|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_THROTTLE, i915_gem_throttle_ioctl, DRM_AUTH|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_ENTERVT, i915_gem_entervt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_LEAVEVT, i915_gem_leavevt_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_CREATE, i915_gem_create_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_PREAD, i915_gem_pread_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_PWRITE, i915_gem_pwrite_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_MMAP, i915_gem_mmap_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_MMAP_GTT, i915_gem_mmap_gtt_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_SET_DOMAIN, i915_gem_set_domain_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_SW_FINISH, i915_gem_sw_finish_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_SET_TILING, i915_gem_set_tiling, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_GET_TILING, i915_gem_get_tiling, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
 +      DRM_IOCTL_DEF(DRM_I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
  };
  
  int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
@@@ -205,7 -205,6 +205,7 @@@ struct intel_device_info 
        u8 is_g4x : 1;
        u8 is_pineview : 1;
        u8 is_ironlake : 1;
 +      u8 is_gen6 : 1;
        u8 has_fbc : 1;
        u8 has_rc6 : 1;
        u8 has_pipe_cxsr : 1;
@@@ -221,11 -220,8 +221,13 @@@ enum no_fbc_reason 
        FBC_NOT_TILED, /* buffer not tiled */
  };
  
 +enum intel_pch {
 +      PCH_IBX,        /* Ibexpeak PCH */
 +      PCH_CPT,        /* Cougarpoint PCH */
 +};
 +
+ struct intel_fbdev;
  typedef struct drm_i915_private {
        struct drm_device *dev;
  
        /* Display functions */
        struct drm_i915_display_funcs display;
  
 +      /* PCH chipset type */
 +      enum intel_pch pch_type;
 +
        /* Register state */
        bool modeset_on_lid;
        u8 saveLBB;
        /* Reclocking support */
        bool render_reclock_avail;
        bool lvds_downclock_avail;
 +      /* indicate whether the LVDS EDID is OK */
 +      bool lvds_edid_good;
        /* indicates the reduced downclock for LVDS*/
        int lvds_downclock;
        struct work_struct idle_work;
        u8 max_delay;
  
        enum no_fbc_reason no_fbc_reason;
+       /* list of fbdev register on this device */
+       struct intel_fbdev *fbdev;
  } drm_i915_private_t;
  
  /** driver private structure attached to each drm_gem_object */
@@@ -741,8 -735,6 +746,8 @@@ struct drm_i915_gem_object 
        atomic_t pending_flip;
  };
  
 +#define to_intel_bo(x) ((struct drm_i915_gem_object *) (x)->driver_private)
 +
  /**
   * Request queue structure.
   *
@@@ -1000,9 -992,6 +1005,9 @@@ extern int intel_modeset_vga_set_state(
  extern void i8xx_disable_fbc(struct drm_device *dev);
  extern void g4x_disable_fbc(struct drm_device *dev);
  
 +extern void intel_detect_pch (struct drm_device *dev);
 +extern int intel_trans_dp_port_sel (struct drm_crtc *crtc);
 +
  /**
   * Lock test for when it's just for synchronization of ring access.
   *
@@@ -1100,7 -1089,6 +1105,7 @@@ extern int i915_wait_ring(struct drm_de
  #define IS_IRONLAKE_M(dev)    ((dev)->pci_device == 0x0046)
  #define IS_IRONLAKE(dev)      (INTEL_INFO(dev)->is_ironlake)
  #define IS_I9XX(dev)          (INTEL_INFO(dev)->is_i9xx)
 +#define IS_GEN6(dev)          (INTEL_INFO(dev)->is_gen6)
  #define IS_MOBILE(dev)                (INTEL_INFO(dev)->is_mobile)
  
  #define IS_GEN3(dev)  (IS_I915G(dev) ||                       \
  
  #define I915_NEED_GFX_HWS(dev)        (INTEL_INFO(dev)->need_gfx_hws)
  
 -#define IS_GEN6(dev)  ((dev)->pci_device == 0x0102)
 -
  /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
   * rows, which changed the alignment requirements and fence programming.
   */
  #define SUPPORTS_INTEGRATED_DP(dev)   (IS_G4X(dev) || IS_IRONLAKE(dev))
  #define SUPPORTS_EDP(dev)             (IS_IRONLAKE_M(dev))
  #define SUPPORTS_TV(dev)              (IS_I9XX(dev) && IS_MOBILE(dev) && \
 -                                      !IS_IRONLAKE(dev) && !IS_PINEVIEW(dev))
 +                                      !IS_IRONLAKE(dev) && !IS_PINEVIEW(dev) && \
 +                                      !IS_GEN6(dev))
  #define I915_HAS_HOTPLUG(dev)          (INTEL_INFO(dev)->has_hotplug)
  /* dsparb controlled by hw only */
  #define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
  #define HAS_PCH_SPLIT(dev) (IS_IRONLAKE(dev) ||       \
                            IS_GEN6(dev))
  
 +#define INTEL_PCH_TYPE(dev) (((struct drm_i915_private *)(dev)->dev_private)->pch_type)
 +#define HAS_PCH_CPT(dev) (INTEL_PCH_TYPE(dev) == PCH_CPT)
 +
  #define PRIMARY_RINGBUFFER_SIZE         (128*1024)
  
  #endif
@@@ -27,7 -27,6 +27,7 @@@
   */
  
  #include <linux/sysrq.h>
 +#include <linux/slab.h>
  #include "drmP.h"
  #include "drm.h"
  #include "i915_drm.h"
@@@ -169,13 -168,9 +169,13 @@@ void intel_enable_asle (struct drm_devi
  
        if (HAS_PCH_SPLIT(dev))
                ironlake_enable_display_irq(dev_priv, DE_GSE);
 -      else
 +      else {
                i915_enable_pipestat(dev_priv, 1,
                                     I915_LEGACY_BLC_EVENT_ENABLE);
 +              if (IS_I965G(dev))
 +                      i915_enable_pipestat(dev_priv, 0,
 +                                           I915_LEGACY_BLC_EVENT_ENABLE);
 +      }
  }
  
  /**
@@@ -260,17 -255,18 +260,18 @@@ static void i915_hotplug_work_func(stru
                                                    hotplug_work);
        struct drm_device *dev = dev_priv->dev;
        struct drm_mode_config *mode_config = &dev->mode_config;
 -      struct drm_connector *connector;
 +      struct drm_encoder *encoder;
  
 -      if (mode_config->num_connector) {
 -              list_for_each_entry(connector, &mode_config->connector_list, head) {
 -                      struct intel_output *intel_output = to_intel_output(connector);
 +      if (mode_config->num_encoder) {
 +              list_for_each_entry(encoder, &mode_config->encoder_list, head) {
 +                      struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
        
 -                      if (intel_output->hot_plug)
 -                              (*intel_output->hot_plug) (intel_output);
 +                      if (intel_encoder->hot_plug)
 +                              (*intel_encoder->hot_plug) (intel_encoder);
                }
        }
        /* Just fire off a uevent and let userspace tell us what to do */
+       intelfb_hotplug(dev, false);
        drm_sysfs_hotplug_event(dev);
  }
  
@@@ -448,7 -444,7 +449,7 @@@ i915_error_object_create(struct drm_dev
        if (src == NULL)
                return NULL;
  
 -      src_priv = src->driver_private;
 +      src_priv = to_intel_bo(src);
        if (src_priv->pages == NULL)
                return NULL;
  
@@@ -950,8 -946,7 +951,8 @@@ irqreturn_t i915_driver_irq_handler(DRM
                        intel_finish_page_flip(dev, 1);
                }
  
 -              if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
 +              if ((pipea_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
 +                  (pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
                    (iir & I915_ASLE_INTERRUPT))
                        opregion_asle_intr(dev);
  
@@@ -28,7 -28,6 +28,7 @@@
  #include <linux/input.h>
  #include <linux/i2c.h>
  #include <linux/kernel.h>
 +#include <linux/slab.h>
  #include "drmP.h"
  #include "intel_drv.h"
  #include "i915_drm.h"
@@@ -742,18 -741,36 +742,18 @@@ bool intel_pipe_has_type (struct drm_cr
  {
      struct drm_device *dev = crtc->dev;
      struct drm_mode_config *mode_config = &dev->mode_config;
 -    struct drm_connector *l_entry;
 +    struct drm_encoder *l_entry;
  
 -    list_for_each_entry(l_entry, &mode_config->connector_list, head) {
 -          if (l_entry->encoder &&
 -              l_entry->encoder->crtc == crtc) {
 -                  struct intel_output *intel_output = to_intel_output(l_entry);
 -                  if (intel_output->type == type)
 +    list_for_each_entry(l_entry, &mode_config->encoder_list, head) {
 +          if (l_entry && l_entry->crtc == crtc) {
 +                  struct intel_encoder *intel_encoder = enc_to_intel_encoder(l_entry);
 +                  if (intel_encoder->type == type)
                            return true;
            }
      }
      return false;
  }
  
 -struct drm_connector *
 -intel_pipe_get_output (struct drm_crtc *crtc)
 -{
 -    struct drm_device *dev = crtc->dev;
 -    struct drm_mode_config *mode_config = &dev->mode_config;
 -    struct drm_connector *l_entry, *ret = NULL;
 -
 -    list_for_each_entry(l_entry, &mode_config->connector_list, head) {
 -          if (l_entry->encoder &&
 -              l_entry->encoder->crtc == crtc) {
 -                  ret = l_entry;
 -                  break;
 -          }
 -    }
 -    return ret;
 -}
 -
  #define INTELPllInvalid(s)   do { /* DRM_DEBUG(s); */ return false; } while (0)
  /**
   * Returns whether the given set of divisors are valid for a given refclk with
@@@ -985,7 -1002,7 +985,7 @@@ static void i8xx_enable_fbc(struct drm_
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
 -      struct drm_i915_gem_object *obj_priv = intel_fb->obj->driver_private;
 +      struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int plane, i;
        u32 fbc_ctl, fbc_ctl2;
        /* enable it... */
        fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
        if (IS_I945GM(dev))
 -              fbc_ctl |= FBC_C3_IDLE; /* 945 needs special SR handling */
 +              fbc_ctl |= FBC_CTL_C3_IDLE; /* 945 needs special SR handling */
        fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
        fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
        if (obj_priv->tiling_mode != I915_TILING_NONE)
@@@ -1062,7 -1079,7 +1062,7 @@@ static void g4x_enable_fbc(struct drm_c
        struct drm_i915_private *dev_priv = dev->dev_private;
        struct drm_framebuffer *fb = crtc->fb;
        struct intel_framebuffer *intel_fb = to_intel_framebuffer(fb);
 -      struct drm_i915_gem_object *obj_priv = intel_fb->obj->driver_private;
 +      struct drm_i915_gem_object *obj_priv = to_intel_bo(intel_fb->obj);
        struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
        int plane = (intel_crtc->plane == 0 ? DPFC_CTL_PLANEA :
                     DPFC_CTL_PLANEB);
@@@ -1158,7 -1175,7 +1158,7 @@@ static void intel_update_fbc(struct drm
                return;
  
        intel_fb = to_intel_framebuffer(fb);
 -      obj_priv = intel_fb->obj->driver_private;
 +      obj_priv = to_intel_bo(intel_fb->obj);
  
        /*
         * If FBC is already on, we just have to verify that we can
@@@ -1225,7 -1242,7 +1225,7 @@@ out_disable
  static int
  intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
  {
 -      struct drm_i915_gem_object *obj_priv = obj->driver_private;
 +      struct drm_i915_gem_object *obj_priv = to_intel_bo(obj);
        u32 alignment;
        int ret;
  
@@@ -1305,7 -1322,7 +1305,7 @@@ intel_pipe_set_base(struct drm_crtc *cr
  
        intel_fb = to_intel_framebuffer(crtc->fb);
        obj = intel_fb->obj;
 -      obj_priv = obj->driver_private;
 +      obj_priv = to_intel_bo(obj);
  
        mutex_lock(&dev->struct_mutex);
        ret = intel_pin_and_fence_fb_obj(dev, obj);
  
        if (old_fb) {
                intel_fb = to_intel_framebuffer(old_fb);
 -              obj_priv = intel_fb->obj->driver_private;
 +              obj_priv = to_intel_bo(intel_fb->obj);
                i915_gem_object_unpin(intel_fb->obj);
        }
        intel_increase_pllclock(crtc, true);
@@@ -1492,219 -1509,6 +1492,219 @@@ static void ironlake_set_pll_edp (struc
        udelay(500);
  }
  
 +/* The FDI link training functions for ILK/Ibexpeak. */
 +static void ironlake_fdi_link_train(struct drm_crtc *crtc)
 +{
 +      struct drm_device *dev = crtc->dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 +      int pipe = intel_crtc->pipe;
 +      int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
 +      int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
 +      int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
 +      int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
 +      u32 temp, tries = 0;
 +
 +      /* enable CPU FDI TX and PCH FDI RX */
 +      temp = I915_READ(fdi_tx_reg);
 +      temp |= FDI_TX_ENABLE;
 +      temp &= ~(7 << 19);
 +      temp |= (intel_crtc->fdi_lanes - 1) << 19;
 +      temp &= ~FDI_LINK_TRAIN_NONE;
 +      temp |= FDI_LINK_TRAIN_PATTERN_1;
 +      I915_WRITE(fdi_tx_reg, temp);
 +      I915_READ(fdi_tx_reg);
 +
 +      temp = I915_READ(fdi_rx_reg);
 +      temp &= ~FDI_LINK_TRAIN_NONE;
 +      temp |= FDI_LINK_TRAIN_PATTERN_1;
 +      I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
 +      I915_READ(fdi_rx_reg);
 +      udelay(150);
 +
 +      /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
 +         for train result */
 +      temp = I915_READ(fdi_rx_imr_reg);
 +      temp &= ~FDI_RX_SYMBOL_LOCK;
 +      temp &= ~FDI_RX_BIT_LOCK;
 +      I915_WRITE(fdi_rx_imr_reg, temp);
 +      I915_READ(fdi_rx_imr_reg);
 +      udelay(150);
 +
 +      for (;;) {
 +              temp = I915_READ(fdi_rx_iir_reg);
 +              DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
 +
 +              if ((temp & FDI_RX_BIT_LOCK)) {
 +                      DRM_DEBUG_KMS("FDI train 1 done.\n");
 +                      I915_WRITE(fdi_rx_iir_reg,
 +                                 temp | FDI_RX_BIT_LOCK);
 +                      break;
 +              }
 +
 +              tries++;
 +
 +              if (tries > 5) {
 +                      DRM_DEBUG_KMS("FDI train 1 fail!\n");
 +                      break;
 +              }
 +      }
 +
 +      /* Train 2 */
 +      temp = I915_READ(fdi_tx_reg);
 +      temp &= ~FDI_LINK_TRAIN_NONE;
 +      temp |= FDI_LINK_TRAIN_PATTERN_2;
 +      I915_WRITE(fdi_tx_reg, temp);
 +
 +      temp = I915_READ(fdi_rx_reg);
 +      temp &= ~FDI_LINK_TRAIN_NONE;
 +      temp |= FDI_LINK_TRAIN_PATTERN_2;
 +      I915_WRITE(fdi_rx_reg, temp);
 +      udelay(150);
 +
 +      tries = 0;
 +
 +      for (;;) {
 +              temp = I915_READ(fdi_rx_iir_reg);
 +              DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
 +
 +              if (temp & FDI_RX_SYMBOL_LOCK) {
 +                      I915_WRITE(fdi_rx_iir_reg,
 +                                 temp | FDI_RX_SYMBOL_LOCK);
 +                      DRM_DEBUG_KMS("FDI train 2 done.\n");
 +                      break;
 +              }
 +
 +              tries++;
 +
 +              if (tries > 5) {
 +                      DRM_DEBUG_KMS("FDI train 2 fail!\n");
 +                      break;
 +              }
 +      }
 +
 +      DRM_DEBUG_KMS("FDI train done\n");
 +}
 +
 +static int snb_b_fdi_train_param [] = {
 +      FDI_LINK_TRAIN_400MV_0DB_SNB_B,
 +      FDI_LINK_TRAIN_400MV_6DB_SNB_B,
 +      FDI_LINK_TRAIN_600MV_3_5DB_SNB_B,
 +      FDI_LINK_TRAIN_800MV_0DB_SNB_B,
 +};
 +
 +/* The FDI link training functions for SNB/Cougarpoint. */
 +static void gen6_fdi_link_train(struct drm_crtc *crtc)
 +{
 +      struct drm_device *dev = crtc->dev;
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
 +      int pipe = intel_crtc->pipe;
 +      int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
 +      int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
 +      int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
 +      int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
 +      u32 temp, i;
 +
 +      /* enable CPU FDI TX and PCH FDI RX */
 +      temp = I915_READ(fdi_tx_reg);
 +      temp |= FDI_TX_ENABLE;
 +      temp &= ~(7 << 19);
 +      temp |= (intel_crtc->fdi_lanes - 1) << 19;
 +      temp &= ~FDI_LINK_TRAIN_NONE;
 +      temp |= FDI_LINK_TRAIN_PATTERN_1;
 +      temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
 +      /* SNB-B */
 +      temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
 +      I915_WRITE(fdi_tx_reg, temp);
 +      I915_READ(fdi_tx_reg);
 +
 +      temp = I915_READ(fdi_rx_reg);
 +      if (HAS_PCH_CPT(dev)) {
 +              temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
 +              temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
 +      } else {
 +              temp &= ~FDI_LINK_TRAIN_NONE;
 +              temp |= FDI_LINK_TRAIN_PATTERN_1;
 +      }
 +      I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
 +      I915_READ(fdi_rx_reg);
 +      udelay(150);
 +
 +      /* Train 1: umask FDI RX Interrupt symbol_lock and bit_lock bit
 +         for train result */
 +      temp = I915_READ(fdi_rx_imr_reg);
 +      temp &= ~FDI_RX_SYMBOL_LOCK;
 +      temp &= ~FDI_RX_BIT_LOCK;
 +      I915_WRITE(fdi_rx_imr_reg, temp);
 +      I915_READ(fdi_rx_imr_reg);
 +      udelay(150);
 +
 +      for (i = 0; i < 4; i++ ) {
 +              temp = I915_READ(fdi_tx_reg);
 +              temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
 +              temp |= snb_b_fdi_train_param[i];
 +              I915_WRITE(fdi_tx_reg, temp);
 +              udelay(500);
 +
 +              temp = I915_READ(fdi_rx_iir_reg);
 +              DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
 +
 +              if (temp & FDI_RX_BIT_LOCK) {
 +                      I915_WRITE(fdi_rx_iir_reg,
 +                                 temp | FDI_RX_BIT_LOCK);
 +                      DRM_DEBUG_KMS("FDI train 1 done.\n");
 +                      break;
 +              }
 +      }
 +      if (i == 4)
 +              DRM_DEBUG_KMS("FDI train 1 fail!\n");
 +
 +      /* Train 2 */
 +      temp = I915_READ(fdi_tx_reg);
 +      temp &= ~FDI_LINK_TRAIN_NONE;
 +      temp |= FDI_LINK_TRAIN_PATTERN_2;
 +      if (IS_GEN6(dev)) {
 +              temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
 +              /* SNB-B */
 +              temp |= FDI_LINK_TRAIN_400MV_0DB_SNB_B;
 +      }
 +      I915_WRITE(fdi_tx_reg, temp);
 +
 +      temp = I915_READ(fdi_rx_reg);
 +      if (HAS_PCH_CPT(dev)) {
 +              temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
 +              temp |= FDI_LINK_TRAIN_PATTERN_2_CPT;
 +      } else {
 +              temp &= ~FDI_LINK_TRAIN_NONE;
 +              temp |= FDI_LINK_TRAIN_PATTERN_2;
 +      }
 +      I915_WRITE(fdi_rx_reg, temp);
 +      udelay(150);
 +
 +      for (i = 0; i < 4; i++ ) {
 +              temp = I915_READ(fdi_tx_reg);
 +              temp &= ~FDI_LINK_TRAIN_VOL_EMP_MASK;
 +              temp |= snb_b_fdi_train_param[i];
 +              I915_WRITE(fdi_tx_reg, temp);
 +              udelay(500);
 +
 +              temp = I915_READ(fdi_rx_iir_reg);
 +              DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
 +
 +              if (temp & FDI_RX_SYMBOL_LOCK) {
 +                      I915_WRITE(fdi_rx_iir_reg,
 +                                 temp | FDI_RX_SYMBOL_LOCK);
 +                      DRM_DEBUG_KMS("FDI train 2 done.\n");
 +                      break;
 +              }
 +      }
 +      if (i == 4)
 +              DRM_DEBUG_KMS("FDI train 2 fail!\n");
 +
 +      DRM_DEBUG_KMS("FDI train done.\n");
 +}
 +
  static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
  {
        struct drm_device *dev = crtc->dev;
        int dspbase_reg = (plane == 0) ? DSPAADDR : DSPBADDR;
        int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
        int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
 -      int fdi_rx_iir_reg = (pipe == 0) ? FDI_RXA_IIR : FDI_RXB_IIR;
 -      int fdi_rx_imr_reg = (pipe == 0) ? FDI_RXA_IMR : FDI_RXB_IMR;
        int transconf_reg = (pipe == 0) ? TRANSACONF : TRANSBCONF;
        int pf_ctl_reg = (pipe == 0) ? PFA_CTL_1 : PFB_CTL_1;
        int pf_win_size = (pipe == 0) ? PFA_WIN_SZ : PFB_WIN_SZ;
        int trans_vtot_reg = (pipe == 0) ? TRANS_VTOTAL_A : TRANS_VTOTAL_B;
        int trans_vblank_reg = (pipe == 0) ? TRANS_VBLANK_A : TRANS_VBLANK_B;
        int trans_vsync_reg = (pipe == 0) ? TRANS_VSYNC_A : TRANS_VSYNC_B;
 +      int trans_dpll_sel = (pipe == 0) ? 0 : 1;
        u32 temp;
 -      int tries = 5, j, n;
 +      int n;
        u32 pipe_bpc;
  
        temp = I915_READ(pipeconf_reg);
                        /* enable eDP PLL */
                        ironlake_enable_pll_edp(crtc);
                } else {
 -                      /* enable PCH DPLL */
 -                      temp = I915_READ(pch_dpll_reg);
 -                      if ((temp & DPLL_VCO_ENABLE) == 0) {
 -                              I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
 -                              I915_READ(pch_dpll_reg);
 -                      }
  
                        /* enable PCH FDI RX PLL, wait warmup plus DMI latency */
                        temp = I915_READ(fdi_rx_reg);
                         */
                        temp &= ~(0x7 << 16);
                        temp |= (pipe_bpc << 11);
 -                      I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE |
 -                                      FDI_SEL_PCDCLK |
 -                                      FDI_DP_PORT_WIDTH_X4); /* default 4 lanes */
 +                      temp &= ~(7 << 19);
 +                      temp |= (intel_crtc->fdi_lanes - 1) << 19;
 +                      I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
 +                      I915_READ(fdi_rx_reg);
 +                      udelay(200);
 +
 +                      /* Switch from Rawclk to PCDclk */
 +                      temp = I915_READ(fdi_rx_reg);
 +                      I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK);
                        I915_READ(fdi_rx_reg);
                        udelay(200);
  
                }
  
                if (!HAS_eDP) {
 -                      /* enable CPU FDI TX and PCH FDI RX */
 -                      temp = I915_READ(fdi_tx_reg);
 -                      temp |= FDI_TX_ENABLE;
 -                      temp |= FDI_DP_PORT_WIDTH_X4; /* default */
 -                      temp &= ~FDI_LINK_TRAIN_NONE;
 -                      temp |= FDI_LINK_TRAIN_PATTERN_1;
 -                      I915_WRITE(fdi_tx_reg, temp);
 -                      I915_READ(fdi_tx_reg);
 -
 -                      temp = I915_READ(fdi_rx_reg);
 -                      temp &= ~FDI_LINK_TRAIN_NONE;
 -                      temp |= FDI_LINK_TRAIN_PATTERN_1;
 -                      I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENABLE);
 -                      I915_READ(fdi_rx_reg);
 -
 -                      udelay(150);
 -
 -                      /* Train FDI. */
 -                      /* umask FDI RX Interrupt symbol_lock and bit_lock bit
 -                         for train result */
 -                      temp = I915_READ(fdi_rx_imr_reg);
 -                      temp &= ~FDI_RX_SYMBOL_LOCK;
 -                      temp &= ~FDI_RX_BIT_LOCK;
 -                      I915_WRITE(fdi_rx_imr_reg, temp);
 -                      I915_READ(fdi_rx_imr_reg);
 -                      udelay(150);
 +                      /* For PCH output, training FDI link */
 +                      if (IS_GEN6(dev))
 +                              gen6_fdi_link_train(crtc);
 +                      else
 +                              ironlake_fdi_link_train(crtc);
  
 -                      temp = I915_READ(fdi_rx_iir_reg);
 -                      DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
 -
 -                      if ((temp & FDI_RX_BIT_LOCK) == 0) {
 -                              for (j = 0; j < tries; j++) {
 -                                      temp = I915_READ(fdi_rx_iir_reg);
 -                                      DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
 -                                                              temp);
 -                                      if (temp & FDI_RX_BIT_LOCK)
 -                                              break;
 -                                      udelay(200);
 -                              }
 -                              if (j != tries)
 -                                      I915_WRITE(fdi_rx_iir_reg,
 -                                                      temp | FDI_RX_BIT_LOCK);
 -                              else
 -                                      DRM_DEBUG_KMS("train 1 fail\n");
 -                      } else {
 -                              I915_WRITE(fdi_rx_iir_reg,
 -                                              temp | FDI_RX_BIT_LOCK);
 -                              DRM_DEBUG_KMS("train 1 ok 2!\n");
 +                      /* enable PCH DPLL */
 +                      temp = I915_READ(pch_dpll_reg);
 +                      if ((temp & DPLL_VCO_ENABLE) == 0) {
 +                              I915_WRITE(pch_dpll_reg, temp | DPLL_VCO_ENABLE);
 +                              I915_READ(pch_dpll_reg);
                        }
 -                      temp = I915_READ(fdi_tx_reg);
 -                      temp &= ~FDI_LINK_TRAIN_NONE;
 -                      temp |= FDI_LINK_TRAIN_PATTERN_2;
 -                      I915_WRITE(fdi_tx_reg, temp);
 -
 -                      temp = I915_READ(fdi_rx_reg);
 -                      temp &= ~FDI_LINK_TRAIN_NONE;
 -                      temp |= FDI_LINK_TRAIN_PATTERN_2;
 -                      I915_WRITE(fdi_rx_reg, temp);
 -
 -                      udelay(150);
 +                      udelay(200);
  
 -                      temp = I915_READ(fdi_rx_iir_reg);
 -                      DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
 -
 -                      if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
 -                              for (j = 0; j < tries; j++) {
 -                                      temp = I915_READ(fdi_rx_iir_reg);
 -                                      DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
 -                                                              temp);
 -                                      if (temp & FDI_RX_SYMBOL_LOCK)
 -                                              break;
 -                                      udelay(200);
 -                              }
 -                              if (j != tries) {
 -                                      I915_WRITE(fdi_rx_iir_reg,
 -                                                      temp | FDI_RX_SYMBOL_LOCK);
 -                                      DRM_DEBUG_KMS("train 2 ok 1!\n");
 -                              } else
 -                                      DRM_DEBUG_KMS("train 2 fail\n");
 -                      } else {
 -                              I915_WRITE(fdi_rx_iir_reg,
 -                                              temp | FDI_RX_SYMBOL_LOCK);
 -                              DRM_DEBUG_KMS("train 2 ok 2!\n");
 +                      if (HAS_PCH_CPT(dev)) {
 +                              /* Be sure PCH DPLL SEL is set */
 +                              temp = I915_READ(PCH_DPLL_SEL);
 +                              if (trans_dpll_sel == 0 &&
 +                                              (temp & TRANSA_DPLL_ENABLE) == 0)
 +                                      temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
 +                              else if (trans_dpll_sel == 1 &&
 +                                              (temp & TRANSB_DPLL_ENABLE) == 0)
 +                                      temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
 +                              I915_WRITE(PCH_DPLL_SEL, temp);
 +                              I915_READ(PCH_DPLL_SEL);
                        }
 -                      DRM_DEBUG_KMS("train done\n");
  
                        /* set transcoder timing */
                        I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
                        I915_WRITE(trans_vblank_reg, I915_READ(cpu_vblank_reg));
                        I915_WRITE(trans_vsync_reg, I915_READ(cpu_vsync_reg));
  
 +                      /* enable normal train */
 +                      temp = I915_READ(fdi_tx_reg);
 +                      temp &= ~FDI_LINK_TRAIN_NONE;
 +                      I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
 +                                      FDI_TX_ENHANCE_FRAME_ENABLE);
 +                      I915_READ(fdi_tx_reg);
 +
 +                      temp = I915_READ(fdi_rx_reg);
 +                      if (HAS_PCH_CPT(dev)) {
 +                              temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
 +                              temp |= FDI_LINK_TRAIN_NORMAL_CPT;
 +                      } else {
 +                              temp &= ~FDI_LINK_TRAIN_NONE;
 +                              temp |= FDI_LINK_TRAIN_NONE;
 +                      }
 +                      I915_WRITE(fdi_rx_reg, temp | FDI_RX_ENHANCE_FRAME_ENABLE);
 +                      I915_READ(fdi_rx_reg);
 +
 +                      /* wait one idle pattern time */
 +                      udelay(100);
 +
 +                      /* For PCH DP, enable TRANS_DP_CTL */
 +                      if (HAS_PCH_CPT(dev) &&
 +                          intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT)) {
 +                              int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
 +                              int reg;
 +
 +                              reg = I915_READ(trans_dp_ctl);
 +                              reg &= ~TRANS_DP_PORT_SEL_MASK;
 +                              reg = TRANS_DP_OUTPUT_ENABLE |
 +                                    TRANS_DP_ENH_FRAMING |
 +                                    TRANS_DP_VSYNC_ACTIVE_HIGH |
 +                                    TRANS_DP_HSYNC_ACTIVE_HIGH;
 +
 +                              switch (intel_trans_dp_port_sel(crtc)) {
 +                              case PCH_DP_B:
 +                                      reg |= TRANS_DP_PORT_SEL_B;
 +                                      break;
 +                              case PCH_DP_C:
 +                                      reg |= TRANS_DP_PORT_SEL_C;
 +                                      break;
 +                              case PCH_DP_D:
 +                                      reg |= TRANS_DP_PORT_SEL_D;
 +                                      break;
 +                              default:
 +                                      DRM_DEBUG_KMS("Wrong PCH DP port return. Guess port B\n");
 +                                      reg |= TRANS_DP_PORT_SEL_B;
 +                                      break;
 +                              }
 +
 +                              I915_WRITE(trans_dp_ctl, reg);
 +                              POSTING_READ(trans_dp_ctl);
 +                      }
 +
                        /* enable PCH transcoder */
                        temp = I915_READ(transconf_reg);
                        /*
                        while ((I915_READ(transconf_reg) & TRANS_STATE_ENABLE) == 0)
                                ;
  
 -                      /* enable normal */
 -
 -                      temp = I915_READ(fdi_tx_reg);
 -                      temp &= ~FDI_LINK_TRAIN_NONE;
 -                      I915_WRITE(fdi_tx_reg, temp | FDI_LINK_TRAIN_NONE |
 -                                      FDI_TX_ENHANCE_FRAME_ENABLE);
 -                      I915_READ(fdi_tx_reg);
 -
 -                      temp = I915_READ(fdi_rx_reg);
 -                      temp &= ~FDI_LINK_TRAIN_NONE;
 -                      I915_WRITE(fdi_rx_reg, temp | FDI_LINK_TRAIN_NONE |
 -                                      FDI_RX_ENHANCE_FRAME_ENABLE);
 -                      I915_READ(fdi_rx_reg);
 -
 -                      /* wait one idle pattern time */
 -                      udelay(100);
 -
                }
  
                intel_crtc_load_lut(crtc);
                        I915_READ(pf_ctl_reg);
                }
                I915_WRITE(pf_win_size, 0);
 +              POSTING_READ(pf_win_size);
 +
  
                /* disable CPU FDI tx and PCH FDI rx */
                temp = I915_READ(fdi_tx_reg);
                temp &= ~FDI_LINK_TRAIN_NONE;
                temp |= FDI_LINK_TRAIN_PATTERN_1;
                I915_WRITE(fdi_tx_reg, temp);
 +              POSTING_READ(fdi_tx_reg);
  
                temp = I915_READ(fdi_rx_reg);
 -              temp &= ~FDI_LINK_TRAIN_NONE;
 -              temp |= FDI_LINK_TRAIN_PATTERN_1;
 +              if (HAS_PCH_CPT(dev)) {
 +                      temp &= ~FDI_LINK_TRAIN_PATTERN_MASK_CPT;
 +                      temp |= FDI_LINK_TRAIN_PATTERN_1_CPT;
 +              } else {
 +                      temp &= ~FDI_LINK_TRAIN_NONE;
 +                      temp |= FDI_LINK_TRAIN_PATTERN_1;
 +              }
                I915_WRITE(fdi_rx_reg, temp);
 +              POSTING_READ(fdi_rx_reg);
  
                udelay(100);
  
                                }
                        }
                }
 +
                temp = I915_READ(transconf_reg);
                /* BPC in transcoder is consistent with that in pipeconf */
                temp &= ~PIPE_BPC_MASK;
                I915_READ(transconf_reg);
                udelay(100);
  
 +              if (HAS_PCH_CPT(dev)) {
 +                      /* disable TRANS_DP_CTL */
 +                      int trans_dp_ctl = (pipe == 0) ? TRANS_DP_CTL_A : TRANS_DP_CTL_B;
 +                      int reg;
 +
 +                      reg = I915_READ(trans_dp_ctl);
 +                      reg &= ~(TRANS_DP_OUTPUT_ENABLE | TRANS_DP_PORT_SEL_MASK);
 +                      I915_WRITE(trans_dp_ctl, reg);
 +                      POSTING_READ(trans_dp_ctl);
 +
 +                      /* disable DPLL_SEL */
 +                      temp = I915_READ(PCH_DPLL_SEL);
 +                      if (trans_dpll_sel == 0)
 +                              temp &= ~(TRANSA_DPLL_ENABLE | TRANSA_DPLLB_SEL);
 +                      else
 +                              temp &= ~(TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
 +                      I915_WRITE(PCH_DPLL_SEL, temp);
 +                      I915_READ(PCH_DPLL_SEL);
 +
 +              }
 +
                /* disable PCH DPLL */
                temp = I915_READ(pch_dpll_reg);
 -              if ((temp & DPLL_VCO_ENABLE) != 0) {
 -                      I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
 -                      I915_READ(pch_dpll_reg);
 -              }
 +              I915_WRITE(pch_dpll_reg, temp & ~DPLL_VCO_ENABLE);
 +              I915_READ(pch_dpll_reg);
  
                if (HAS_eDP) {
                        ironlake_disable_pll_edp(crtc);
                }
  
 +              /* Switch from PCDclk to Rawclk */
                temp = I915_READ(fdi_rx_reg);
                temp &= ~FDI_SEL_PCDCLK;
                I915_WRITE(fdi_rx_reg, temp);
                I915_READ(fdi_rx_reg);
  
 +              /* Disable CPU FDI TX PLL */
 +              temp = I915_READ(fdi_tx_reg);
 +              I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE);
 +              I915_READ(fdi_tx_reg);
 +              udelay(100);
 +
                temp = I915_READ(fdi_rx_reg);
                temp &= ~FDI_RX_PLL_ENABLE;
                I915_WRITE(fdi_rx_reg, temp);
                I915_READ(fdi_rx_reg);
  
 -              /* Disable CPU FDI TX PLL */
 -              temp = I915_READ(fdi_tx_reg);
 -              if ((temp & FDI_TX_PLL_ENABLE) != 0) {
 -                      I915_WRITE(fdi_tx_reg, temp & ~FDI_TX_PLL_ENABLE);
 -                      I915_READ(fdi_tx_reg);
 -                      udelay(100);
 -              }
 -
                /* Wait for the clocks to turn off. */
                udelay(100);
                break;
@@@ -2531,30 -2330,6 +2531,30 @@@ static struct intel_watermark_params i8
        I830_FIFO_LINE_SIZE
  };
  
 +static struct intel_watermark_params ironlake_display_wm_info = {
 +      ILK_DISPLAY_FIFO,
 +      ILK_DISPLAY_MAXWM,
 +      ILK_DISPLAY_DFTWM,
 +      2,
 +      ILK_FIFO_LINE_SIZE
 +};
 +
 +static struct intel_watermark_params ironlake_display_srwm_info = {
 +      ILK_DISPLAY_SR_FIFO,
 +      ILK_DISPLAY_MAX_SRWM,
 +      ILK_DISPLAY_DFT_SRWM,
 +      2,
 +      ILK_FIFO_LINE_SIZE
 +};
 +
 +static struct intel_watermark_params ironlake_cursor_srwm_info = {
 +      ILK_CURSOR_SR_FIFO,
 +      ILK_CURSOR_MAX_SRWM,
 +      ILK_CURSOR_DFT_SRWM,
 +      2,
 +      ILK_FIFO_LINE_SIZE
 +};
 +
  /**
   * intel_calculate_wm - calculate watermark level
   * @clock_in_khz: pixel clock
@@@ -2673,6 -2448,66 +2673,6 @@@ static void pineview_disable_cxsr(struc
        DRM_INFO("Big FIFO is disabled\n");
  }
  
 -static void pineview_enable_cxsr(struct drm_device *dev, unsigned long clock,
 -                               int pixel_size)
 -{
 -      struct drm_i915_private *dev_priv = dev->dev_private;
 -      u32 reg;
 -      unsigned long wm;
 -      struct cxsr_latency *latency;
 -
 -      latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev), dev_priv->fsb_freq,
 -              dev_priv->mem_freq);
 -      if (!latency) {
 -              DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
 -              pineview_disable_cxsr(dev);
 -              return;
 -      }
 -
 -      /* Display SR */
 -      wm = intel_calculate_wm(clock, &pineview_display_wm, pixel_size,
 -                              latency->display_sr);
 -      reg = I915_READ(DSPFW1);
 -      reg &= 0x7fffff;
 -      reg |= wm << 23;
 -      I915_WRITE(DSPFW1, reg);
 -      DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg);
 -
 -      /* cursor SR */
 -      wm = intel_calculate_wm(clock, &pineview_cursor_wm, pixel_size,
 -                              latency->cursor_sr);
 -      reg = I915_READ(DSPFW3);
 -      reg &= ~(0x3f << 24);
 -      reg |= (wm & 0x3f) << 24;
 -      I915_WRITE(DSPFW3, reg);
 -
 -      /* Display HPLL off SR */
 -      wm = intel_calculate_wm(clock, &pineview_display_hplloff_wm,
 -              latency->display_hpll_disable, I915_FIFO_LINE_SIZE);
 -      reg = I915_READ(DSPFW3);
 -      reg &= 0xfffffe00;
 -      reg |= wm & 0x1ff;
 -      I915_WRITE(DSPFW3, reg);
 -
 -      /* cursor HPLL off SR */
 -      wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm, pixel_size,
 -                              latency->cursor_hpll_disable);
 -      reg = I915_READ(DSPFW3);
 -      reg &= ~(0x3f << 16);
 -      reg |= (wm & 0x3f) << 16;
 -      I915_WRITE(DSPFW3, reg);
 -      DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg);
 -
 -      /* activate cxsr */
 -      reg = I915_READ(DSPFW3);
 -      reg |= PINEVIEW_SELF_REFRESH_EN;
 -      I915_WRITE(DSPFW3, reg);
 -
 -      DRM_INFO("Big FIFO is enabled\n");
 -
 -      return;
 -}
 -
  /*
   * Latency for FIFO fetches is dependent on several factors:
   *   - memory configuration (speed, channels)
@@@ -2757,71 -2592,6 +2757,71 @@@ static int i830_get_fifo_size(struct dr
        return size;
  }
  
 +static void pineview_update_wm(struct drm_device *dev,  int planea_clock,
 +                        int planeb_clock, int sr_hdisplay, int pixel_size)
 +{
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      u32 reg;
 +      unsigned long wm;
 +      struct cxsr_latency *latency;
 +      int sr_clock;
 +
 +      latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev), dev_priv->fsb_freq,
 +                                       dev_priv->mem_freq);
 +      if (!latency) {
 +              DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
 +              pineview_disable_cxsr(dev);
 +              return;
 +      }
 +
 +      if (!planea_clock || !planeb_clock) {
 +              sr_clock = planea_clock ? planea_clock : planeb_clock;
 +
 +              /* Display SR */
 +              wm = intel_calculate_wm(sr_clock, &pineview_display_wm,
 +                                      pixel_size, latency->display_sr);
 +              reg = I915_READ(DSPFW1);
 +              reg &= ~DSPFW_SR_MASK;
 +              reg |= wm << DSPFW_SR_SHIFT;
 +              I915_WRITE(DSPFW1, reg);
 +              DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg);
 +
 +              /* cursor SR */
 +              wm = intel_calculate_wm(sr_clock, &pineview_cursor_wm,
 +                                      pixel_size, latency->cursor_sr);
 +              reg = I915_READ(DSPFW3);
 +              reg &= ~DSPFW_CURSOR_SR_MASK;
 +              reg |= (wm & 0x3f) << DSPFW_CURSOR_SR_SHIFT;
 +              I915_WRITE(DSPFW3, reg);
 +
 +              /* Display HPLL off SR */
 +              wm = intel_calculate_wm(sr_clock, &pineview_display_hplloff_wm,
 +                                      pixel_size, latency->display_hpll_disable);
 +              reg = I915_READ(DSPFW3);
 +              reg &= ~DSPFW_HPLL_SR_MASK;
 +              reg |= wm & DSPFW_HPLL_SR_MASK;
 +              I915_WRITE(DSPFW3, reg);
 +
 +              /* cursor HPLL off SR */
 +              wm = intel_calculate_wm(sr_clock, &pineview_cursor_hplloff_wm,
 +                                      pixel_size, latency->cursor_hpll_disable);
 +              reg = I915_READ(DSPFW3);
 +              reg &= ~DSPFW_HPLL_CURSOR_MASK;
 +              reg |= (wm & 0x3f) << DSPFW_HPLL_CURSOR_SHIFT;
 +              I915_WRITE(DSPFW3, reg);
 +              DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg);
 +
 +              /* activate cxsr */
 +              reg = I915_READ(DSPFW3);
 +              reg |= PINEVIEW_SELF_REFRESH_EN;
 +              I915_WRITE(DSPFW3, reg);
 +              DRM_DEBUG_KMS("Self-refresh is enabled\n");
 +      } else {
 +              pineview_disable_cxsr(dev);
 +              DRM_DEBUG_KMS("Self-refresh is disabled\n");
 +      }
 +}
 +
  static void g4x_update_wm(struct drm_device *dev,  int planea_clock,
                          int planeb_clock, int sr_hdisplay, int pixel_size)
  {
@@@ -3042,108 -2812,6 +3042,108 @@@ static void i830_update_wm(struct drm_d
        I915_WRITE(FW_BLC, fwater_lo);
  }
  
 +#define ILK_LP0_PLANE_LATENCY         700
 +
 +static void ironlake_update_wm(struct drm_device *dev,  int planea_clock,
 +                     int planeb_clock, int sr_hdisplay, int pixel_size)
 +{
 +      struct drm_i915_private *dev_priv = dev->dev_private;
 +      int planea_wm, planeb_wm, cursora_wm, cursorb_wm;
 +      int sr_wm, cursor_wm;
 +      unsigned long line_time_us;
 +      int sr_clock, entries_required;
 +      u32 reg_value;
 +
 +      /* Calculate and update the watermark for plane A */
 +      if (planea_clock) {
 +              entries_required = ((planea_clock / 1000) * pixel_size *
 +                                   ILK_LP0_PLANE_LATENCY) / 1000;
 +              entries_required = DIV_ROUND_UP(entries_required,
 +                                 ironlake_display_wm_info.cacheline_size);
 +              planea_wm = entries_required +
 +                          ironlake_display_wm_info.guard_size;
 +
 +              if (planea_wm > (int)ironlake_display_wm_info.max_wm)
 +                      planea_wm = ironlake_display_wm_info.max_wm;
 +
 +              cursora_wm = 16;
 +              reg_value = I915_READ(WM0_PIPEA_ILK);
 +              reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
 +              reg_value |= (planea_wm << WM0_PIPE_PLANE_SHIFT) |
 +                           (cursora_wm & WM0_PIPE_CURSOR_MASK);
 +              I915_WRITE(WM0_PIPEA_ILK, reg_value);
 +              DRM_DEBUG_KMS("FIFO watermarks For pipe A - plane %d, "
 +                              "cursor: %d\n", planea_wm, cursora_wm);
 +      }
 +      /* Calculate and update the watermark for plane B */
 +      if (planeb_clock) {
 +              entries_required = ((planeb_clock / 1000) * pixel_size *
 +                                   ILK_LP0_PLANE_LATENCY) / 1000;
 +              entries_required = DIV_ROUND_UP(entries_required,
 +                                 ironlake_display_wm_info.cacheline_size);
 +              planeb_wm = entries_required +
 +                          ironlake_display_wm_info.guard_size;
 +
 +              if (planeb_wm > (int)ironlake_display_wm_info.max_wm)
 +                      planeb_wm = ironlake_display_wm_info.max_wm;
 +
 +              cursorb_wm = 16;
 +              reg_value = I915_READ(WM0_PIPEB_ILK);
 +              reg_value &= ~(WM0_PIPE_PLANE_MASK | WM0_PIPE_CURSOR_MASK);
 +              reg_value |= (planeb_wm << WM0_PIPE_PLANE_SHIFT) |
 +                           (cursorb_wm & WM0_PIPE_CURSOR_MASK);
 +              I915_WRITE(WM0_PIPEB_ILK, reg_value);
 +              DRM_DEBUG_KMS("FIFO watermarks For pipe B - plane %d, "
 +                              "cursor: %d\n", planeb_wm, cursorb_wm);
 +      }
 +
 +      /*
 +       * Calculate and update the self-refresh watermark only when one
 +       * display plane is used.
 +       */
 +      if (!planea_clock || !planeb_clock) {
 +              int line_count;
 +              /* Read the self-refresh latency. The unit is 0.5us */
 +              int ilk_sr_latency = I915_READ(MLTR_ILK) & ILK_SRLT_MASK;
 +
 +              sr_clock = planea_clock ? planea_clock : planeb_clock;
 +              line_time_us = ((sr_hdisplay * 1000) / sr_clock);
 +
 +              /* Use ns/us then divide to preserve precision */
 +              line_count = ((ilk_sr_latency * 500) / line_time_us + 1000)
 +                             / 1000;
 +
 +              /* calculate the self-refresh watermark for display plane */
 +              entries_required = line_count * sr_hdisplay * pixel_size;
 +              entries_required = DIV_ROUND_UP(entries_required,
 +                                 ironlake_display_srwm_info.cacheline_size);
 +              sr_wm = entries_required +
 +                      ironlake_display_srwm_info.guard_size;
 +
 +              /* calculate the self-refresh watermark for display cursor */
 +              entries_required = line_count * pixel_size * 64;
 +              entries_required = DIV_ROUND_UP(entries_required,
 +                                 ironlake_cursor_srwm_info.cacheline_size);
 +              cursor_wm = entries_required +
 +                          ironlake_cursor_srwm_info.guard_size;
 +
 +              /* configure watermark and enable self-refresh */
 +              reg_value = I915_READ(WM1_LP_ILK);
 +              reg_value &= ~(WM1_LP_LATENCY_MASK | WM1_LP_SR_MASK |
 +                             WM1_LP_CURSOR_MASK);
 +              reg_value |= WM1_LP_SR_EN |
 +                           (ilk_sr_latency << WM1_LP_LATENCY_SHIFT) |
 +                           (sr_wm << WM1_LP_SR_SHIFT) | cursor_wm;
 +
 +              I915_WRITE(WM1_LP_ILK, reg_value);
 +              DRM_DEBUG_KMS("self-refresh watermark: display plane %d "
 +                              "cursor %d\n", sr_wm, cursor_wm);
 +
 +      } else {
 +              /* Turn off self refresh if both pipes are enabled */
 +              I915_WRITE(WM1_LP_ILK, I915_READ(WM1_LP_ILK) & ~WM1_LP_SR_EN);
 +      }
 +}
  /**
   * intel_update_watermarks - update FIFO watermark values based on current modes
   *
@@@ -3213,6 -2881,12 +3213,6 @@@ static void intel_update_watermarks(str
        if (enabled <= 0)
                return;
  
 -      /* Single plane configs can enable self refresh */
 -      if (enabled == 1 && IS_PINEVIEW(dev))
 -              pineview_enable_cxsr(dev, sr_clock, pixel_size);
 -      else if (IS_PINEVIEW(dev))
 -              pineview_disable_cxsr(dev);
 -
        dev_priv->display.update_wm(dev, planea_clock, planeb_clock,
                                    sr_hdisplay, pixel_size);
  }
@@@ -3242,15 -2916,14 +3242,15 @@@ static int intel_crtc_mode_set(struct d
        int dspsize_reg = (plane == 0) ? DSPASIZE : DSPBSIZE;
        int dsppos_reg = (plane == 0) ? DSPAPOS : DSPBPOS;
        int pipesrc_reg = (pipe == 0) ? PIPEASRC : PIPEBSRC;
 -      int refclk, num_outputs = 0;
 +      int refclk, num_connectors = 0;
        intel_clock_t clock, reduced_clock;
        u32 dpll = 0, fp = 0, fp2 = 0, dspcntr, pipeconf;
        bool ok, has_reduced_clock = false, is_sdvo = false, is_dvo = false;
        bool is_crt = false, is_lvds = false, is_tv = false, is_dp = false;
        bool is_edp = false;
        struct drm_mode_config *mode_config = &dev->mode_config;
 -      struct drm_connector *connector;
 +      struct drm_encoder *encoder;
 +      struct intel_encoder *intel_encoder = NULL;
        const intel_limit_t *limit;
        int ret;
        struct fdi_m_n m_n = {0};
        int pch_fp_reg = (pipe == 0) ? PCH_FPA0 : PCH_FPB0;
        int pch_dpll_reg = (pipe == 0) ? PCH_DPLL_A : PCH_DPLL_B;
        int fdi_rx_reg = (pipe == 0) ? FDI_RXA_CTL : FDI_RXB_CTL;
 +      int fdi_tx_reg = (pipe == 0) ? FDI_TXA_CTL : FDI_TXB_CTL;
 +      int trans_dpll_sel = (pipe == 0) ? 0 : 1;
        int lvds_reg = LVDS;
        u32 temp;
        int sdvo_pixel_multiply;
  
        drm_vblank_pre_modeset(dev, pipe);
  
 -      list_for_each_entry(connector, &mode_config->connector_list, head) {
 -              struct intel_output *intel_output = to_intel_output(connector);
 +      list_for_each_entry(encoder, &mode_config->encoder_list, head) {
  
 -              if (!connector->encoder || connector->encoder->crtc != crtc)
 +              if (!encoder || encoder->crtc != crtc)
                        continue;
  
 -              switch (intel_output->type) {
 +              intel_encoder = enc_to_intel_encoder(encoder);
 +
 +              switch (intel_encoder->type) {
                case INTEL_OUTPUT_LVDS:
                        is_lvds = true;
                        break;
                case INTEL_OUTPUT_SDVO:
                case INTEL_OUTPUT_HDMI:
                        is_sdvo = true;
 -                      if (intel_output->needs_tv_clock)
 +                      if (intel_encoder->needs_tv_clock)
                                is_tv = true;
                        break;
                case INTEL_OUTPUT_DVO:
                        break;
                }
  
 -              num_outputs++;
 +              num_connectors++;
        }
  
 -      if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) {
 +      if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2) {
                refclk = dev_priv->lvds_ssc_freq * 1000;
                DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
                                        refclk / 1000);
  
        /* FDI link */
        if (HAS_PCH_SPLIT(dev)) {
 -              int lane, link_bw, bpp;
 +              int lane = 0, link_bw, bpp;
                /* eDP doesn't require FDI link, so just set DP M/N
                   according to current link config */
                if (is_edp) {
 -                      struct drm_connector *edp;
                        target_clock = mode->clock;
 -                      edp = intel_pipe_get_output(crtc);
 -                      intel_edp_link_config(to_intel_output(edp),
 +                      intel_edp_link_config(intel_encoder,
                                        &lane, &link_bw);
                } else {
                        /* DP over FDI requires target mode clock
                                target_clock = mode->clock;
                        else
                                target_clock = adjusted_mode->clock;
 -                      lane = 4;
                        link_bw = 270000;
                }
  
                        bpp = 24;
                }
  
 +              if (!lane) {
 +                      /* 
 +                       * Account for spread spectrum to avoid
 +                       * oversubscribing the link. Max center spread
 +                       * is 2.5%; use 5% for safety's sake.
 +                       */
 +                      u32 bps = target_clock * bpp * 21 / 20;
 +                      lane = bps / (link_bw * 8) + 1;
 +              }
 +
 +              intel_crtc->fdi_lanes = lane;
 +
                ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
        }
  
                /* XXX: just matching BIOS for now */
                /*      dpll |= PLL_REF_INPUT_TVCLKINBC; */
                dpll |= 3;
 -      else if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2)
 +      else if (is_lvds && dev_priv->lvds_use_ssc && num_connectors < 2)
                dpll |= PLLB_REF_INPUT_SPREADSPECTRUMIN;
        else
                dpll |= PLL_REF_INPUT_DREFCLK;
                        pipeconf &= ~PIPEACONF_DOUBLE_WIDE;
        }
  
 -      dspcntr |= DISPLAY_PLANE_ENABLE;
 -      pipeconf |= PIPEACONF_ENABLE;
 -      dpll |= DPLL_VCO_ENABLE;
 -
 -
        /* Disable the panel fitter if it was on our pipe */
        if (!HAS_PCH_SPLIT(dev) && intel_panel_fitter_pipe(dev) == pipe)
                I915_WRITE(PFIT_CONTROL, 0);
                udelay(150);
        }
  
 +      /* enable transcoder DPLL */
 +      if (HAS_PCH_CPT(dev)) {
 +              temp = I915_READ(PCH_DPLL_SEL);
 +              if (trans_dpll_sel == 0)
 +                      temp |= (TRANSA_DPLL_ENABLE | TRANSA_DPLLA_SEL);
 +              else
 +                      temp |= (TRANSB_DPLL_ENABLE | TRANSB_DPLLB_SEL);
 +              I915_WRITE(PCH_DPLL_SEL, temp);
 +              I915_READ(PCH_DPLL_SEL);
 +              udelay(150);
 +      }
 +
        /* The LVDS pin pair needs to be on before the DPLLs are enabled.
         * This is an exception to the general rule that mode_set doesn't turn
         * things on.
                        lvds_reg = PCH_LVDS;
  
                lvds = I915_READ(lvds_reg);
 -              lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP | LVDS_PIPEB_SELECT;
 +              lvds |= LVDS_PORT_EN | LVDS_A0A2_CLKA_POWER_UP;
 +              if (pipe == 1) {
 +                      if (HAS_PCH_CPT(dev))
 +                              lvds |= PORT_TRANS_B_SEL_CPT;
 +                      else
 +                              lvds |= LVDS_PIPEB_SELECT;
 +              } else {
 +                      if (HAS_PCH_CPT(dev))
 +                              lvds &= ~PORT_TRANS_SEL_MASK;
 +                      else
 +                              lvds &= ~LVDS_PIPEB_SELECT;
 +              }
                /* set the corresponsding LVDS_BORDER bit */
                lvds |= dev_priv->lvds_border_bits;
                /* Set the B0-B3 data pairs corresponding to whether we're going to
        }
        if (is_dp)
                intel_dp_set_m_n(crtc, mode, adjusted_mode);
 +      else if (HAS_PCH_SPLIT(dev)) {
 +              /* For non-DP output, clear any trans DP clock recovery setting.*/
 +              if (pipe == 0) {
 +                      I915_WRITE(TRANSA_DATA_M1, 0);
 +                      I915_WRITE(TRANSA_DATA_N1, 0);
 +                      I915_WRITE(TRANSA_DP_LINK_M1, 0);
 +                      I915_WRITE(TRANSA_DP_LINK_N1, 0);
 +              } else {
 +                      I915_WRITE(TRANSB_DATA_M1, 0);
 +                      I915_WRITE(TRANSB_DATA_N1, 0);
 +                      I915_WRITE(TRANSB_DP_LINK_M1, 0);
 +                      I915_WRITE(TRANSB_DP_LINK_N1, 0);
 +              }
 +      }
  
        if (!is_edp) {
                I915_WRITE(fp_reg, fp);
                        /* enable FDI RX PLL too */
                        temp = I915_READ(fdi_rx_reg);
                        I915_WRITE(fdi_rx_reg, temp | FDI_RX_PLL_ENABLE);
 +                      I915_READ(fdi_rx_reg);
 +                      udelay(200);
 +
 +                      /* enable FDI TX PLL too */
 +                      temp = I915_READ(fdi_tx_reg);
 +                      I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
 +                      I915_READ(fdi_tx_reg);
 +
 +                      /* enable FDI RX PCDCLK */
 +                      temp = I915_READ(fdi_rx_reg);
 +                      I915_WRITE(fdi_rx_reg, temp | FDI_SEL_PCDCLK);
 +                      I915_READ(fdi_rx_reg);
                        udelay(200);
                }
        }
@@@ -3893,7 -3510,7 +3893,7 @@@ static int intel_crtc_cursor_set(struc
        if (!bo)
                return -ENOENT;
  
 -      obj_priv = bo->driver_private;
 +      obj_priv = to_intel_bo(bo);
  
        if (bo->size < width * height * 4) {
                DRM_ERROR("buffer is to small\n");
@@@ -4037,9 -3654,9 +4037,9 @@@ static void intel_crtc_gamma_set(struc
   * detection.
   *
   * It will be up to the load-detect code to adjust the pipe as appropriate for
 - * its requirements.  The pipe will be connected to no other outputs.
 + * its requirements.  The pipe will be connected to no other encoders.
   *
 - * Currently this code will only succeed if there is a pipe with no outputs
 + * Currently this code will only succeed if there is a pipe with no encoders
   * configured for it.  In the future, it could choose to temporarily disable
   * some outputs to free up a pipe for its use.
   *
@@@ -4052,15 -3669,14 +4052,15 @@@ static struct drm_display_mode load_det
                 704, 832, 0, 480, 489, 491, 520, 0, DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC),
  };
  
 -struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
 +struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
 +                                          struct drm_connector *connector,
                                            struct drm_display_mode *mode,
                                            int *dpms_mode)
  {
        struct intel_crtc *intel_crtc;
        struct drm_crtc *possible_crtc;
        struct drm_crtc *supported_crtc =NULL;
 -      struct drm_encoder *encoder = &intel_output->enc;
 +      struct drm_encoder *encoder = &intel_encoder->enc;
        struct drm_crtc *crtc = NULL;
        struct drm_device *dev = encoder->dev;
        struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
        }
  
        encoder->crtc = crtc;
 -      intel_output->base.encoder = encoder;
 -      intel_output->load_detect_temp = true;
 +      connector->encoder = encoder;
 +      intel_encoder->load_detect_temp = true;
  
        intel_crtc = to_intel_crtc(crtc);
        *dpms_mode = intel_crtc->dpms_mode;
        return crtc;
  }
  
 -void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_mode)
 +void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
 +                                  struct drm_connector *connector, int dpms_mode)
  {
 -      struct drm_encoder *encoder = &intel_output->enc;
 +      struct drm_encoder *encoder = &intel_encoder->enc;
        struct drm_device *dev = encoder->dev;
        struct drm_crtc *crtc = encoder->crtc;
        struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
        struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
  
 -      if (intel_output->load_detect_temp) {
 +      if (intel_encoder->load_detect_temp) {
                encoder->crtc = NULL;
 -              intel_output->base.encoder = NULL;
 -              intel_output->load_detect_temp = false;
 +              connector->encoder = NULL;
 +              intel_encoder->load_detect_temp = false;
                crtc->enabled = drm_helper_crtc_in_use(crtc);
                drm_helper_disable_unused_functions(dev);
        }
  
 -      /* Switch crtc and output back off if necessary */
 +      /* Switch crtc and encoder back off if necessary */
        if (crtc->enabled && dpms_mode != DRM_MODE_DPMS_ON) {
                if (encoder->crtc == crtc)
                        encoder_funcs->dpms(encoder, dpms_mode);
@@@ -4540,7 -4155,7 +4540,7 @@@ void intel_finish_page_flip(struct drm_
        work = intel_crtc->unpin_work;
        if (work == NULL || !work->pending) {
                if (work && !work->pending) {
 -                      obj_priv = work->pending_flip_obj->driver_private;
 +                      obj_priv = to_intel_bo(work->pending_flip_obj);
                        DRM_DEBUG_DRIVER("flip finish: %p (%d) not pending?\n",
                                         obj_priv,
                                         atomic_read(&obj_priv->pending_flip));
  
        spin_unlock_irqrestore(&dev->event_lock, flags);
  
 -      obj_priv = work->pending_flip_obj->driver_private;
 +      obj_priv = to_intel_bo(work->pending_flip_obj);
  
        /* Initial scanout buffer will have a 0 pending flip count */
        if ((atomic_read(&obj_priv->pending_flip) == 0) ||
@@@ -4636,7 -4251,7 +4636,7 @@@ static int intel_crtc_page_flip(struct 
        ret = intel_pin_and_fence_fb_obj(dev, obj);
        if (ret != 0) {
                DRM_DEBUG_DRIVER("flip queue: %p pin & fence failed\n",
 -                        obj->driver_private);
 +                        to_intel_bo(obj));
                kfree(work);
                intel_crtc->unpin_work = NULL;
                mutex_unlock(&dev->struct_mutex);
        crtc->fb = fb;
        i915_gem_object_flush_write_domain(obj);
        drm_vblank_get(dev, intel_crtc->pipe);
 -      obj_priv = obj->driver_private;
 +      obj_priv = to_intel_bo(obj);
        atomic_inc(&obj_priv->pending_flip);
        work->pending_flip_obj = obj;
  
@@@ -4776,15 -4391,15 +4776,15 @@@ struct drm_crtc *intel_get_crtc_from_pi
        return crtc;
  }
  
 -static int intel_connector_clones(struct drm_device *dev, int type_mask)
 +static int intel_encoder_clones(struct drm_device *dev, int type_mask)
  {
        int index_mask = 0;
 -      struct drm_connector *connector;
 +      struct drm_encoder *encoder;
        int entry = 0;
  
 -        list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 -              struct intel_output *intel_output = to_intel_output(connector);
 -              if (type_mask & intel_output->clone_mask)
 +        list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 +              struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
 +              if (type_mask & intel_encoder->clone_mask)
                        index_mask |= (1 << entry);
                entry++;
        }
  static void intel_setup_outputs(struct drm_device *dev)
  {
        struct drm_i915_private *dev_priv = dev->dev_private;
 -      struct drm_connector *connector;
 +      struct drm_encoder *encoder;
  
        intel_crt_init(dev);
  
                        intel_dp_init(dev, DP_A);
  
                if (I915_READ(HDMIB) & PORT_DETECTED) {
 -                      /* check SDVOB */
 -                      /* found = intel_sdvo_init(dev, HDMIB); */
 -                      found = 0;
 +                      /* PCH SDVOB multiplex with HDMIB */
 +                      found = intel_sdvo_init(dev, PCH_SDVOB);
                        if (!found)
                                intel_hdmi_init(dev, HDMIB);
                        if (!found && (I915_READ(PCH_DP_B) & DP_DETECTED))
        if (SUPPORTS_TV(dev))
                intel_tv_init(dev);
  
 -      list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
 -              struct intel_output *intel_output = to_intel_output(connector);
 -              struct drm_encoder *encoder = &intel_output->enc;
 +      list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
 +              struct intel_encoder *intel_encoder = enc_to_intel_encoder(encoder);
  
 -              encoder->possible_crtcs = intel_output->crtc_mask;
 -              encoder->possible_clones = intel_connector_clones(dev,
 -                                              intel_output->clone_mask);
 +              encoder->possible_crtcs = intel_encoder->crtc_mask;
 +              encoder->possible_clones = intel_encoder_clones(dev,
 +                                              intel_encoder->clone_mask);
        }
  }
  
  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);
@@@ -4915,18 -4528,13 +4911,13 @@@ static const struct drm_framebuffer_fun
        .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);
        }
  
        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 *
@@@ -5100,39 -4708,6 +5091,39 @@@ void intel_init_clock_gating(struct drm
         * specs, but enable as much else as we can.
         */
        if (HAS_PCH_SPLIT(dev)) {
 +              uint32_t dspclk_gate = VRHUNIT_CLOCK_GATE_DISABLE;
 +
 +              if (IS_IRONLAKE(dev)) {
 +                      /* Required for FBC */
 +                      dspclk_gate |= DPFDUNIT_CLOCK_GATE_DISABLE;
 +                      /* Required for CxSR */
 +                      dspclk_gate |= DPARBUNIT_CLOCK_GATE_DISABLE;
 +
 +                      I915_WRITE(PCH_3DCGDIS0,
 +                                 MARIUNIT_CLOCK_GATE_DISABLE |
 +                                 SVSMUNIT_CLOCK_GATE_DISABLE);
 +              }
 +
 +              I915_WRITE(PCH_DSPCLK_GATE_D, dspclk_gate);
 +
 +              /*
 +               * According to the spec the following bits should be set in
 +               * order to enable memory self-refresh
 +               * The bit 22/21 of 0x42004
 +               * The bit 5 of 0x42020
 +               * The bit 15 of 0x45000
 +               */
 +              if (IS_IRONLAKE(dev)) {
 +                      I915_WRITE(ILK_DISPLAY_CHICKEN2,
 +                                      (I915_READ(ILK_DISPLAY_CHICKEN2) |
 +                                      ILK_DPARB_GATE | ILK_VSDPFD_FULL));
 +                      I915_WRITE(ILK_DSPCLK_GATE,
 +                                      (I915_READ(ILK_DSPCLK_GATE) |
 +                                              ILK_DPARB_CLK_GATE));
 +                      I915_WRITE(DISP_ARB_CTL,
 +                                      (I915_READ(DISP_ARB_CTL) |
 +                                              DISP_FBC_WM_DIS));
 +              }
                return;
        } else if (IS_G4X(dev)) {
                uint32_t dspclk_gate;
                struct drm_i915_gem_object *obj_priv = NULL;
  
                if (dev_priv->pwrctx) {
 -                      obj_priv = dev_priv->pwrctx->driver_private;
 +                      obj_priv = to_intel_bo(dev_priv->pwrctx);
                } else {
                        struct drm_gem_object *pwrctx;
  
                        pwrctx = intel_alloc_power_context(dev);
                        if (pwrctx) {
                                dev_priv->pwrctx = pwrctx;
 -                              obj_priv = pwrctx->driver_private;
 +                              obj_priv = to_intel_bo(pwrctx);
                        }
                }
  
@@@ -5216,7 -4791,7 +5207,7 @@@ static void intel_init_display(struct d
                        dev_priv->display.fbc_enabled = g4x_fbc_enabled;
                        dev_priv->display.enable_fbc = g4x_enable_fbc;
                        dev_priv->display.disable_fbc = g4x_disable_fbc;
 -              } else if (IS_I965GM(dev) || IS_I945GM(dev) || IS_I915GM(dev)) {
 +              } else if (IS_I965GM(dev)) {
                        dev_priv->display.fbc_enabled = i8xx_fbc_enabled;
                        dev_priv->display.enable_fbc = i8xx_enable_fbc;
                        dev_priv->display.disable_fbc = i8xx_disable_fbc;
                        i830_get_display_clock_speed;
  
        /* For FIFO watermark updates */
 -      if (HAS_PCH_SPLIT(dev))
 -              dev_priv->display.update_wm = NULL;
 -      else if (IS_G4X(dev))
 +      if (HAS_PCH_SPLIT(dev)) {
 +              if (IS_IRONLAKE(dev)) {
 +                      if (I915_READ(MLTR_ILK) & ILK_SRLT_MASK)
 +                              dev_priv->display.update_wm = ironlake_update_wm;
 +                      else {
 +                              DRM_DEBUG_KMS("Failed to get proper latency. "
 +                                            "Disable CxSR\n");
 +                              dev_priv->display.update_wm = NULL;
 +                      }
 +              } else
 +                      dev_priv->display.update_wm = NULL;
 +      } else if (IS_PINEVIEW(dev)) {
 +              if (!intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
 +                                          dev_priv->fsb_freq,
 +                                          dev_priv->mem_freq)) {
 +                      DRM_INFO("failed to find known CxSR latency "
 +                               "(found fsb freq %d, mem freq %d), "
 +                               "disabling CxSR\n",
 +                               dev_priv->fsb_freq, dev_priv->mem_freq);
 +                      /* Disable CxSR and never update its watermark again */
 +                      pineview_disable_cxsr(dev);
 +                      dev_priv->display.update_wm = NULL;
 +              } else
 +                      dev_priv->display.update_wm = pineview_update_wm;
 +      } else if (IS_G4X(dev))
                dev_priv->display.update_wm = g4x_update_wm;
        else if (IS_I965G(dev))
                dev_priv->display.update_wm = i965_update_wm;
@@@ -5345,6 -4898,13 +5336,6 @@@ void intel_modeset_init(struct drm_devi
                    (unsigned long)dev);
  
        intel_setup_overlay(dev);
 -
 -      if (IS_PINEVIEW(dev) && !intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
 -                                                      dev_priv->fsb_freq,
 -                                                      dev_priv->mem_freq))
 -              DRM_INFO("failed to find known CxSR latency "
 -                       "(found fsb freq %d, mem freq %d), disabling CxSR\n",
 -                       dev_priv->fsb_freq, dev_priv->mem_freq);
  }
  
  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)
        if (dev_priv->pwrctx) {
                struct drm_i915_gem_object *obj_priv;
  
 -              obj_priv = dev_priv->pwrctx->driver_private;
 +              obj_priv = to_intel_bo(dev_priv->pwrctx);
                I915_WRITE(PWRCTXA, obj_priv->gtt_offset &~ PWRCTX_EN);
                I915_READ(PWRCTXA);
                i915_gem_object_unpin(dev_priv->pwrctx);
  }
  
  
 -/* current intel driver doesn't take advantage of encoders
 -   always give back the encoder for the connector
 -*/
 -struct drm_encoder *intel_best_encoder(struct drm_connector *connector)
 +/*
 + * Return which encoder is currently attached for connector.
 + */
 +struct drm_encoder *intel_attached_encoder (struct drm_connector *connector)
  {
 -      struct intel_output *intel_output = to_intel_output(connector);
 +      struct drm_mode_object *obj;
 +      struct drm_encoder *encoder;
 +      int i;
 +
 +      for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
 +              if (connector->encoder_ids[i] == 0)
 +                      break;
  
 -      return &intel_output->enc;
 +              obj = drm_mode_object_find(connector->dev,
 +                                           connector->encoder_ids[i],
 +                                           DRM_MODE_OBJECT_ENCODER);
 +              if (!obj)
 +                      continue;
 +
 +              encoder = obj_to_encoder(obj);
 +              return encoder;
 +      }
 +      return NULL;
  }
  
  /*
@@@ -95,7 -95,9 +95,7 @@@ struct intel_framebuffer 
  };
  
  
 -struct intel_output {
 -      struct drm_connector base;
 -
 +struct intel_encoder {
        struct drm_encoder enc;
        int type;
        struct i2c_adapter *i2c_bus;
        bool load_detect_temp;
        bool needs_tv_clock;
        void *dev_priv;
 -      void (*hot_plug)(struct intel_output *);
 +      void (*hot_plug)(struct intel_encoder *);
        int crtc_mask;
        int clone_mask;
  };
  
 +struct intel_connector {
 +      struct drm_connector base;
 +      void *dev_priv;
 +};
 +
  struct intel_crtc;
  struct intel_overlay {
        struct drm_device *dev;
@@@ -152,19 -149,18 +152,19 @@@ struct intel_crtc 
        bool lowfreq_avail;
        struct intel_overlay *overlay;
        struct intel_unpin_work *unpin_work;
 +      int fdi_lanes;
  };
  
  #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
 -#define to_intel_output(x) container_of(x, struct intel_output, base)
 -#define enc_to_intel_output(x) container_of(x, struct intel_output, enc)
 +#define to_intel_connector(x) container_of(x, struct intel_connector, base)
 +#define enc_to_intel_encoder(x) container_of(x, struct intel_encoder, enc)
  #define to_intel_framebuffer(x) container_of(x, struct intel_framebuffer, base)
  
  struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg,
                                     const char *name);
  void intel_i2c_destroy(struct i2c_adapter *adapter);
 -int intel_ddc_get_modes(struct intel_output *intel_output);
 -extern bool intel_ddc_probe(struct intel_output *intel_output);
 +int intel_ddc_get_modes(struct drm_connector *c, struct i2c_adapter *adapter);
 +extern bool intel_ddc_probe(struct intel_encoder *intel_encoder);
  void intel_i2c_quirk_set(struct drm_device *dev, bool enable);
  void intel_i2c_reset_gmbus(struct drm_device *dev);
  
@@@ -179,7 -175,7 +179,7 @@@ extern void intel_dp_init(struct drm_de
  void
  intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
                 struct drm_display_mode *adjusted_mode);
 -extern void intel_edp_link_config (struct intel_output *, int *, int *);
 +extern void intel_edp_link_config (struct intel_encoder *, int *, int *);
  
  
  extern int intel_panel_fitter_pipe (struct drm_device *dev);
@@@ -187,7 -183,7 +187,7 @@@ extern void intel_crtc_load_lut(struct 
  extern void intel_encoder_prepare (struct drm_encoder *encoder);
  extern void intel_encoder_commit (struct drm_encoder *encoder);
  
 -extern struct drm_encoder *intel_best_encoder(struct drm_connector *connector);
 +extern struct drm_encoder *intel_attached_encoder(struct drm_connector *connector);
  
  extern struct drm_display_mode *intel_crtc_mode_get(struct drm_device *dev,
                                                    struct drm_crtc *crtc);
@@@ -195,20 -191,15 +195,17 @@@ int intel_get_pipe_from_crtc_id(struct 
                                struct drm_file *file_priv);
  extern void intel_wait_for_vblank(struct drm_device *dev);
  extern struct drm_crtc *intel_get_crtc_from_pipe(struct drm_device *dev, int pipe);
 -extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_output *intel_output,
 +extern struct drm_crtc *intel_get_load_detect_pipe(struct intel_encoder *intel_encoder,
 +                                                 struct drm_connector *connector,
                                                   struct drm_display_mode *mode,
                                                   int *dpms_mode);
 -extern void intel_release_load_detect_pipe(struct intel_output *intel_output,
 +extern void intel_release_load_detect_pipe(struct intel_encoder *intel_encoder,
 +                                         struct drm_connector *connector,
                                           int dpms_mode);
  
  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);
@@@ -218,10 -209,12 +215,12 @@@ extern void intel_init_clock_gating(str
  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);
@@@ -235,4 -228,6 +234,6 @@@ extern int intel_overlay_put_image(stru
                                   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__ */
@@@ -30,6 -30,7 +30,6 @@@
  #include <linux/string.h>
  #include <linux/mm.h>
  #include <linux/tty.h>
 -#include <linux/slab.h>
  #include <linux/sysrq.h>
  #include <linux/delay.h>
  #include <linux/fb.h>
  #include "i915_drm.h"
  #include "i915_drv.h"
  
- struct intelfb_par {
+ struct intel_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;
  };
  
@@@ -54,7 -56,6 +55,6 @@@ static struct fb_ops intelfb_ops = 
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
-       .fb_setcolreg = drm_fb_helper_setcolreg,
        .fb_fillrect = cfb_fillrect,
        .fb_copyarea = cfb_copyarea,
        .fb_imageblit = cfb_imageblit,
        .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,
- };
- /**
-  * 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 intel_fbdev *ifbdev,
+                         struct drm_fb_helper_surface_size *sizes)
  {
+       struct drm_device *dev = ifbdev->helper.dev;
        struct fb_info *info;
-       struct intelfb_par *par;
        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;
        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);
                ret = -ENOMEM;
                goto out;
        }
 -      obj_priv = fbo->driver_private;
 +      obj_priv = to_intel_bo(fbo);
  
        mutex_lock(&dev->struct_mutex);
  
        /* 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");
-               goto out_unpin;
-       }
-       list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
-       intel_fb = to_intel_framebuffer(fb);
-       *fb_p = fb;
-       info = framebuffer_alloc(sizeof(struct intelfb_par), device);
+       info = framebuffer_alloc(0, device);
        if (!info) {
                ret = -ENOMEM;
                goto out_unpin;
        }
  
-       par = info->par;
+       info->par = ifbdev;
  
-       par->helper.funcs = &intel_fb_helper_funcs;
-       par->helper.dev = dev;
-       ret = drm_fb_helper_init_crtc_count(&par->helper, 2,
-                                           INTELFB_CONN_LIMIT);
-       if (ret)
-               goto out_unref;
+       intel_framebuffer_init(dev, &ifbdev->ifb, &mode_cmd, fbo);
+       fb = &ifbdev->ifb.base;
+       ifbdev->helper.fb = fb;
+       ifbdev->helper.fbdev = info;
  
        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))
                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);
  
        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);
        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);
+                     fb->width, fb->height,
+                     obj_priv->gtt_offset, fbo);
  
        mutex_unlock(&dev->struct_mutex);
        vga_switcheroo_client_fb_set(dev->pdev, info);
@@@ -247,35 -187,86 +186,86 @@@ out
        return ret;
  }
  
- int intelfb_probe(struct drm_device *dev)
+ static int intel_fb_find_or_create_single(struct drm_fb_helper *helper,
+                                         struct drm_fb_helper_surface_size *sizes)
  {
+       struct intel_fbdev *ifbdev = (struct intel_fbdev *)helper;
+       int new_fb = 0;
        int ret;
  
-       DRM_DEBUG_KMS("\n");
-       ret = drm_fb_helper_single_fb_probe(dev, 32, intelfb_create);
-       return ret;
+       if (!helper->fb) {
+               ret = intelfb_create(ifbdev, sizes);
+               if (ret)
+                       return ret;
+               new_fb = 1;
+       }
+       return new_fb;
  }
- EXPORT_SYMBOL(intelfb_probe);
  
int intelfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
void intelfb_hotplug(struct drm_device *dev, bool polled)
  {
-       struct fb_info *info;
+       drm_i915_private_t *dev_priv = dev->dev_private;
+       drm_helper_fb_hpd_irq_event(&dev_priv->fbdev->helper);
+ }
  
-       if (!fb)
-               return -EINVAL;
+ 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,
+ };
  
-       info = fb->fbdev;
+ int intel_fbdev_destroy(struct drm_device *dev,
+                       struct intel_fbdev *ifbdev)
+ {
+       struct fb_info *info;
+       struct intel_framebuffer *ifb = &ifbdev->ifb;
  
-       if (info) {
-               struct intelfb_par *par = info->par;
+       if (ifbdev->helper.fbdev) {
+               info = ifbdev->helper.fbdev;
                unregister_framebuffer(info);
                iounmap(info->screen_base);
-               if (info->par)
-                       drm_fb_helper_free(&par->helper);
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
                framebuffer_release(info);
        }
  
+       drm_fb_helper_fini(&ifbdev->helper);
+       drm_framebuffer_cleanup(&ifb->base);
+       if (ifb->obj)
+               drm_gem_object_unreference_unlocked(ifb->obj);
+       return 0;
+ }
+ int intel_fbdev_init(struct drm_device *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;
+       ifbdev->helper.funcs = &intel_fb_helper_funcs;
+       drm_fb_helper_init(dev, &ifbdev->helper, 2,
+                          INTELFB_CONN_LIMIT, false);
+       drm_fb_helper_single_add_all_connectors(&ifbdev->helper);
+       drm_fb_helper_initial_config(&ifbdev->helper, 32);
        return 0;
  }
- EXPORT_SYMBOL(intelfb_remove);
+ 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");
@@@ -76,7 -76,6 +76,7 @@@ struct nouveau_bo 
        struct ttm_buffer_object bo;
        struct ttm_placement placement;
        u32 placements[3];
 +      u32 busy_placements[3];
        struct ttm_bo_kmap_obj kmap;
        struct list_head head;
  
@@@ -520,7 -519,6 +520,7 @@@ struct drm_nouveau_private 
  
        struct workqueue_struct *wq;
        struct work_struct irq_work;
 +      struct work_struct hpd_work;
  
        struct list_head vbl_waiting;
  
                atomic_t validate_sequence;
        } ttm;
  
 +      struct fb_info *fbdev_info;
 +
+       int fifo_alloc_count;
        struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
  
        struct nouveau_engine engine;
        uint32_t ramro_offset;
        uint32_t ramro_size;
  
 -      /* base physical addresses */
 -      uint64_t fb_phys;
 -      uint64_t fb_available_size;
 -      uint64_t fb_mappable_pages;
 -      uint64_t fb_aper_free;
 -
        struct {
                enum {
                        NOUVEAU_GART_NONE = 0,
                struct nouveau_gpuobj *sg_ctxdma;
                struct page *sg_dummy_page;
                dma_addr_t sg_dummy_bus;
 -
 -              /* nottm hack */
 -              struct drm_ttm_backend *sg_be;
 -              unsigned long sg_handle;
        } gart_info;
  
        /* nv10-nv40 tiling regions */
                spinlock_t lock;
        } tile;
  
 +      /* VRAM/fb configuration */
 +      uint64_t vram_size;
 +      uint64_t vram_sys_base;
 +
 +      uint64_t fb_phys;
 +      uint64_t fb_available_size;
 +      uint64_t fb_mappable_pages;
 +      uint64_t fb_aper_free;
 +      int fb_mtrr;
 +
        /* G8x/G9x virtual address space */
        uint64_t vm_gart_base;
        uint64_t vm_gart_size;
        uint64_t vm_end;
        struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
        int vm_vram_pt_nr;
 -      uint64_t vram_sys_base;
 -
 -      /* the mtrr covering the FB */
 -      int fb_mtrr;
  
        struct mem_block *ramin_heap;
  
        uint32_t dac_users[4];
  
        struct nouveau_suspend_resume {
 -              uint32_t fifo_mode;
 -              uint32_t graph_ctx_control;
 -              uint32_t graph_state;
                uint32_t *ramin_copy;
 -              uint64_t ramin_size;
        } susres;
  
        struct backlight_device *backlight;
        struct {
                struct dentry *channel_root;
        } debugfs;
+       struct nouveau_fbdev *nfbdev;
  };
  
  static inline struct drm_nouveau_private *
@@@ -710,7 -717,7 +713,7 @@@ extern struct mem_block *nouveau_mem_al
                                                 struct drm_file *, int tail);
  extern void nouveau_mem_takedown(struct mem_block **heap);
  extern void nouveau_mem_free_block(struct mem_block *);
 -extern uint64_t nouveau_mem_fb_amount(struct drm_device *);
 +extern int  nouveau_mem_detect(struct drm_device *dev);
  extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);
  extern int  nouveau_mem_init(struct drm_device *);
  extern int  nouveau_mem_init_agp(struct drm_device *);
@@@ -1117,8 -1124,7 +1120,8 @@@ extern int nouveau_bo_pin(struct nouvea
  extern int nouveau_bo_unpin(struct nouveau_bo *);
  extern int nouveau_bo_map(struct nouveau_bo *);
  extern void nouveau_bo_unmap(struct nouveau_bo *);
 -extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t memtype);
 +extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t type,
 +                                   uint32_t busy);
  extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index);
  extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val);
  extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index);
@@@ -1162,10 -1168,6 +1165,10 @@@ extern int nouveau_gem_ioctl_info(struc
  int nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
  int nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
  
 +/* nv50_gpio.c */
 +int nv50_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
 +int nv50_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
 +
  #ifndef ioread32_native
  #ifdef __BIG_ENDIAN
  #define ioread16_native ioread16be
@@@ -30,6 -30,7 +30,6 @@@
  #include <linux/string.h>
  #include <linux/mm.h>
  #include <linux/tty.h>
 -#include <linux/slab.h>
  #include <linux/sysrq.h>
  #include <linux/delay.h>
  #include <linux/fb.h>
@@@ -52,8 -53,8 +52,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;
@@@ -97,7 -98,6 +97,6 @@@ static struct fb_ops nouveau_fbcon_ops 
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
-       .fb_setcolreg = drm_fb_helper_setcolreg,
        .fb_fillrect = cfb_fillrect,
        .fb_copyarea = cfb_copyarea,
        .fb_imageblit = cfb_imageblit,
@@@ -111,7 -111,6 +110,6 @@@ static struct fb_ops nv04_fbcon_ops = 
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
-       .fb_setcolreg = drm_fb_helper_setcolreg,
        .fb_fillrect = nv04_fbcon_fillrect,
        .fb_copyarea = nv04_fbcon_copyarea,
        .fb_imageblit = nv04_fbcon_imageblit,
@@@ -125,7 -124,6 +123,6 @@@ static struct fb_ops nv50_fbcon_ops = 
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
-       .fb_setcolreg = drm_fb_helper_setcolreg,
        .fb_fillrect = nv50_fbcon_fillrect,
        .fb_copyarea = nv50_fbcon_copyarea,
        .fb_imageblit = nv50_fbcon_imageblit,
@@@ -155,11 -153,6 +152,6 @@@ static void nouveau_fbcon_gamma_get(str
        *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)
@@@ -198,11 -191,10 +190,10 @@@ not_fb
  }
  #endif
  
- void
- nouveau_fbcon_zfill(struct drm_device *dev)
static void
+ nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
  {
-       struct drm_nouveau_private *dev_priv = dev->dev_private;
-       struct fb_info *info = dev_priv->fbdev_info;
+       struct fb_info *info = nfbdev->helper.fbdev;
        struct fb_fillrect rect;
  
        /* Clear the entire fbcon.  The drm will program every connector
  }
  
  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 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;
        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);
  
        mutex_lock(&dev->struct_mutex);
  
-       fb = nouveau_framebuffer_create(dev, nvbo, &mode_cmd);
-       if (!fb) {
+       info = framebuffer_alloc(0, device);
+       if (!info) {
                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 = fb_alloc_cmap(&info->cmap, 256, 0);
+       if (ret) {
                ret = -ENOMEM;
                goto out_unref;
        }
  
-       par = info->par;
-       par->helper.funcs = &nouveau_fbcon_helper_funcs;
-       par->helper.dev = dev;
-       ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4);
-       if (ret)
-               goto out_unref;
-       dev_priv->fbdev_info = info;
+       info->par = nfbdev;
+       nouveau_framebuffer_init(dev, &nfbdev->nouveau_fb, &mode_cmd, nvbo);
+       nouveau_fb = &nfbdev->nouveau_fb;
+       fb = &nouveau_fb->base;
+       /* setup helper */
+       nfbdev->helper.fb = fb;
+       nfbdev->helper.fbdev = info;
  
        strcpy(info->fix.id, "nouveaufb");
        if (nouveau_nofbaccel)
        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, &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);
        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) {
                switch (dev_priv->card_type) {
                case NV_50:
                };
        }
  
-       nouveau_fbcon_zfill(dev);
+       nouveau_fbcon_zfill(dev, nfbdev);
  
        /* To allow resizeing without swapping buffers */
        NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n",
        return ret;
  }
  
- int
- nouveau_fbcon_probe(struct drm_device *dev)
+ static int
+ nouveau_fbcon_find_or_create_single(struct drm_fb_helper *helper,
+                                   struct drm_fb_helper_surface_size *sizes)
  {
-       NV_DEBUG_KMS(dev, "\n");
+       struct nouveau_fbdev *nfbdev = (struct nouveau_fbdev *)helper;
+       int new_fb = 0;
+       int ret;
+       if (!helper->fb) {
+               ret = nouveau_fbcon_create(nfbdev, sizes);
+               if (ret)
+                       return ret;
+               new_fb = 1;
+       }
+       return new_fb;
+ }
  
-       return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_create);
+ void nouveau_fbcon_hotplug(struct drm_device *dev)
+ {
+       struct drm_nouveau_private *dev_priv = dev->dev_private;
+       drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper);
+ }
+ static void nouveau_fbcon_output_status_changed(struct drm_fb_helper *fb_helper)
+ {
+       drm_helper_fb_hotplug_event(fb_helper, true);
  }
  
  int
- nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *nfbdev)
  {
-       struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fb);
+       struct nouveau_framebuffer *nouveau_fb = &nfbdev->nouveau_fb;
        struct fb_info *info;
  
-       if (!fb)
-               return -EINVAL;
-       info = fb->fbdev;
-       if (info) {
-               struct nouveau_fbcon_par *par = info->par;
+       if (nfbdev->helper.fbdev) {
+               info = nfbdev->helper.fbdev;
                unregister_framebuffer(info);
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
+               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;
-               if (par)
-                       drm_fb_helper_free(&par->helper);
-               framebuffer_release(info);
        }
+       drm_fb_helper_fini(&nfbdev->helper);
+       drm_framebuffer_cleanup(&nouveau_fb->base);
        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;
  }
+ 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;
+       struct nouveau_fbdev *nfbdev;
+       nfbdev = kzalloc(sizeof(struct nouveau_fbdev), GFP_KERNEL);
+       if (!nfbdev)
+               return -ENOMEM;
+       nfbdev->dev = dev;
+       dev_priv->nfbdev = nfbdev;
+       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, 32);
+       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;
+ }
+ 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);
+ }
@@@ -51,7 -51,6 +51,7 @@@ nouveau_irq_preinstall(struct drm_devic
  
        if (dev_priv->card_type == NV_50) {
                INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
 +              INIT_WORK(&dev_priv->hpd_work, nv50_display_irq_hotplug_bh);
                INIT_LIST_HEAD(&dev_priv->vbl_waiting);
        }
  }
@@@ -1204,7 -1203,7 +1204,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);
  
        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;
        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;
@@@ -24,7 -24,6 +24,7 @@@
   */
  
  #include <linux/swab.h>
 +#include <linux/slab.h>
  #include "drmP.h"
  #include "drm.h"
  #include "drm_sarea.h"
@@@ -34,6 -33,7 +34,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) {}
@@@ -341,7 -341,7 +342,7 @@@ nouveau_card_init_channel(struct drm_de
  
        gpuobj = NULL;
        ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY,
 -                                   0, nouveau_mem_fb_amount(dev),
 +                                   0, dev_priv->vram_size,
                                     NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM,
                                     &gpuobj);
        if (ret)
@@@ -427,10 -427,6 +428,10 @@@ nouveau_card_init(struct drm_device *de
                        goto out;
        }
  
 +      ret = nouveau_mem_detect(dev);
 +      if (ret)
 +              goto out_bios;
 +
        ret = nouveau_gpuobj_early_init(dev);
        if (ret)
                goto out_bios;
                else
                        ret = nv04_display_create(dev);
                if (ret)
 -                      goto out_irq;
 +                      goto out_channel;
        }
  
        ret = nouveau_backlight_init(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;
  
 +out_channel:
 +      if (dev_priv->channel) {
 +              nouveau_channel_free(dev_priv->channel);
 +              dev_priv->channel = NULL;
 +      }
  out_irq:
        drm_irq_uninstall(dev);
  out_fifo:
@@@ -542,7 -533,6 +543,7 @@@ out_mc
  out_gpuobj:
        nouveau_gpuobj_takedown(dev);
  out_mem:
 +      nouveau_sgdma_takedown(dev);
        nouveau_mem_close(dev);
  out_instmem:
        engine->instmem.takedown(dev);
@@@ -563,6 -553,7 +564,7 @@@ static void nouveau_card_takedown(struc
        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) {
@@@ -794,6 -785,7 +796,7 @@@ int nouveau_unload(struct drm_device *d
        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
@@@ -29,6 -29,7 +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
@@@ -143,7 -144,7 +144,7 @@@ nv50_evo_channel_new(struct drm_device 
        }
  
        ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoVRAM, 0, 0x19,
 -                                0, nouveau_mem_fb_amount(dev));
 +                                0, dev_priv->vram_size);
        if (ret) {
                nv50_evo_channel_del(pchan);
                return ret;
@@@ -231,7 -232,7 +232,7 @@@ nv50_display_init(struct drm_device *de
        /* This used to be in crtc unblank, but seems out of place there. */
        nv_wr32(dev, NV50_PDISPLAY_UNK_380, 0);
        /* RAM is clamped to 256 MiB. */
 -      ram_amount = nouveau_mem_fb_amount(dev);
 +      ram_amount = dev_priv->vram_size;
        NV_DEBUG_KMS(dev, "ram_amount %d\n", ram_amount);
        if (ram_amount > 256*1024*1024)
                ram_amount = 256*1024*1024;
@@@ -529,10 -530,8 +530,10 @@@ int nv50_display_create(struct drm_devi
        }
  
        ret = nv50_display_init(dev);
 -      if (ret)
 +      if (ret) {
 +              nv50_display_destroy(dev);
                return ret;
 +      }
  
        return 0;
  }
@@@ -887,12 -886,10 +888,12 @@@ nv50_display_error_handler(struct drm_d
        nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000);
  }
  
 -static void
 -nv50_display_irq_hotplug(struct drm_device *dev)
 +void
 +nv50_display_irq_hotplug_bh(struct work_struct *work)
  {
 -      struct drm_nouveau_private *dev_priv = dev->dev_private;
 +      struct drm_nouveau_private *dev_priv =
 +              container_of(work, struct drm_nouveau_private, hpd_work);
 +      struct drm_device *dev = dev_priv->dev;
        struct drm_connector *connector;
        const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
        uint32_t unplug_mask, plug_mask, change_mask;
        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
@@@ -953,10 -952,8 +956,10 @@@ nv50_display_irq_handler(struct drm_dev
        struct drm_nouveau_private *dev_priv = dev->dev_private;
        uint32_t delayed = 0;
  
 -      while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG)
 -              nv50_display_irq_hotplug(dev);
 +      if (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG) {
 +              if (!work_pending(&dev_priv->hpd_work))
 +                      queue_work(dev_priv->wq, &dev_priv->hpd_work);
 +      }
  
        while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
                uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
@@@ -6,8 -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 +49,8 @@@
  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 +84,8 @@@
  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;
  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;
 +      uint64_t fb;
        int ret, format;
  
 +      fb = info->fix.smem_start - dev_priv->fb_phys + dev_priv->vm_vram_base;
 +
        switch (info->var.bits_per_pixel) {
        case 8:
                format = 0xf3;
        OUT_RING(chan, info->fix.line_length);
        OUT_RING(chan, info->var.xres_virtual);
        OUT_RING(chan, info->var.yres_virtual);
 -      OUT_RING(chan, 0);
 -      OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
 -                       dev_priv->vm_vram_base);
 +      OUT_RING(chan, upper_32_bits(fb));
 +      OUT_RING(chan, lower_32_bits(fb));
        BEGIN_RING(chan, NvSub2D, 0x0230, 2);
        OUT_RING(chan, format);
        OUT_RING(chan, 1);
        OUT_RING(chan, info->fix.line_length);
        OUT_RING(chan, info->var.xres_virtual);
        OUT_RING(chan, info->var.yres_virtual);
 -      OUT_RING(chan, 0);
 -      OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
 -                       dev_priv->vm_vram_base);
 +      OUT_RING(chan, upper_32_bits(fb));
 +      OUT_RING(chan, lower_32_bits(fb));
  
        return 0;
  }
@@@ -99,7 -99,6 +99,7 @@@ extern int radeon_hw_i2c
   * symbol;
   */
  #define RADEON_MAX_USEC_TIMEOUT               100000  /* 100 ms */
 +#define RADEON_FENCE_JIFFIES_TIMEOUT  (HZ / 2)
  /* RADEON_IB_POOL_SIZE must be a power of 2 */
  #define RADEON_IB_POOL_SIZE           16
  #define RADEON_DEBUGFS_MAX_NUM_FILES  32
@@@ -183,8 -182,7 +183,8 @@@ struct radeon_fence_driver 
        uint32_t                        scratch_reg;
        atomic_t                        seq;
        uint32_t                        last_seq;
 -      unsigned long                   count_timeout;
 +      unsigned long                   last_jiffies;
 +      unsigned long                   last_timeout;
        wait_queue_head_t               queue;
        rwlock_t                        lock;
        struct list_head                created;
@@@ -199,6 -197,7 +199,6 @@@ struct radeon_fence 
        struct list_head                list;
        /* protected by radeon_fence.lock */
        uint32_t                        seq;
 -      unsigned long                   timeout;
        bool                            emited;
        bool                            signaled;
  };
@@@ -747,8 -746,7 +747,8 @@@ struct radeon_asic 
        int (*resume)(struct radeon_device *rdev);
        int (*suspend)(struct radeon_device *rdev);
        void (*vga_set_state)(struct radeon_device *rdev, bool state);
 -      int (*gpu_reset)(struct radeon_device *rdev);
 +      bool (*gpu_is_lockup)(struct radeon_device *rdev);
 +      int (*asic_reset)(struct radeon_device *rdev);
        void (*gart_tlb_flush)(struct radeon_device *rdev);
        int (*gart_set_page)(struct radeon_device *rdev, int i, uint64_t addr);
        int (*cp_init)(struct radeon_device *rdev, unsigned ring_size);
  /*
   * Asic structures
   */
 +struct r100_gpu_lockup {
 +      unsigned long   last_jiffies;
 +      u32             last_cp_rptr;
 +};
 +
  struct r100_asic {
 -      const unsigned  *reg_safe_bm;
 -      unsigned        reg_safe_bm_size;
 -      u32             hdp_cntl;
 +      const unsigned          *reg_safe_bm;
 +      unsigned                reg_safe_bm_size;
 +      u32                     hdp_cntl;
 +      struct r100_gpu_lockup  lockup;
  };
  
  struct r300_asic {
 -      const unsigned  *reg_safe_bm;
 -      unsigned        reg_safe_bm_size;
 -      u32             resync_scratch;
 -      u32             hdp_cntl;
 +      const unsigned          *reg_safe_bm;
 +      unsigned                reg_safe_bm_size;
 +      u32                     resync_scratch;
 +      u32                     hdp_cntl;
 +      struct r100_gpu_lockup  lockup;
  };
  
  struct r600_asic {
 -      unsigned max_pipes;
 -      unsigned max_tile_pipes;
 -      unsigned max_simds;
 -      unsigned max_backends;
 -      unsigned max_gprs;
 -      unsigned max_threads;
 -      unsigned max_stack_entries;
 -      unsigned max_hw_contexts;
 -      unsigned max_gs_threads;
 -      unsigned sx_max_export_size;
 -      unsigned sx_max_export_pos_size;
 -      unsigned sx_max_export_smx_size;
 -      unsigned sq_num_cf_insts;
 -      unsigned tiling_nbanks;
 -      unsigned tiling_npipes;
 -      unsigned tiling_group_size;
 +      unsigned                max_pipes;
 +      unsigned                max_tile_pipes;
 +      unsigned                max_simds;
 +      unsigned                max_backends;
 +      unsigned                max_gprs;
 +      unsigned                max_threads;
 +      unsigned                max_stack_entries;
 +      unsigned                max_hw_contexts;
 +      unsigned                max_gs_threads;
 +      unsigned                sx_max_export_size;
 +      unsigned                sx_max_export_pos_size;
 +      unsigned                sx_max_export_smx_size;
 +      unsigned                sq_num_cf_insts;
 +      unsigned                tiling_nbanks;
 +      unsigned                tiling_npipes;
 +      unsigned                tiling_group_size;
 +      struct r100_gpu_lockup  lockup;
  };
  
  struct rv770_asic {
 -      unsigned max_pipes;
 -      unsigned max_tile_pipes;
 -      unsigned max_simds;
 -      unsigned max_backends;
 -      unsigned max_gprs;
 -      unsigned max_threads;
 -      unsigned max_stack_entries;
 -      unsigned max_hw_contexts;
 -      unsigned max_gs_threads;
 -      unsigned sx_max_export_size;
 -      unsigned sx_max_export_pos_size;
 -      unsigned sx_max_export_smx_size;
 -      unsigned sq_num_cf_insts;
 -      unsigned sx_num_of_sets;
 -      unsigned sc_prim_fifo_size;
 -      unsigned sc_hiz_tile_fifo_size;
 -      unsigned sc_earlyz_tile_fifo_fize;
 -      unsigned tiling_nbanks;
 -      unsigned tiling_npipes;
 -      unsigned tiling_group_size;
 +      unsigned                max_pipes;
 +      unsigned                max_tile_pipes;
 +      unsigned                max_simds;
 +      unsigned                max_backends;
 +      unsigned                max_gprs;
 +      unsigned                max_threads;
 +      unsigned                max_stack_entries;
 +      unsigned                max_hw_contexts;
 +      unsigned                max_gs_threads;
 +      unsigned                sx_max_export_size;
 +      unsigned                sx_max_export_pos_size;
 +      unsigned                sx_max_export_smx_size;
 +      unsigned                sq_num_cf_insts;
 +      unsigned                sx_num_of_sets;
 +      unsigned                sc_prim_fifo_size;
 +      unsigned                sc_hiz_tile_fifo_size;
 +      unsigned                sc_earlyz_tile_fifo_fize;
 +      unsigned                tiling_nbanks;
 +      unsigned                tiling_npipes;
 +      unsigned                tiling_group_size;
 +      struct r100_gpu_lockup  lockup;
  };
  
  union radeon_asic_config {
@@@ -938,9 -927,6 +938,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;
@@@ -1156,8 -1142,7 +1153,8 @@@ static inline void radeon_ring_write(st
  #define radeon_suspend(rdev) (rdev)->asic->suspend((rdev))
  #define radeon_cs_parse(p) rdev->asic->cs_parse((p))
  #define radeon_vga_set_state(rdev, state) (rdev)->asic->vga_set_state((rdev), (state))
 -#define radeon_gpu_reset(rdev) (rdev)->asic->gpu_reset((rdev))
 +#define radeon_gpu_is_lockup(rdev) (rdev)->asic->gpu_is_lockup((rdev))
 +#define radeon_asic_reset(rdev) (rdev)->asic->asic_reset((rdev))
  #define radeon_gart_tlb_flush(rdev) (rdev)->asic->gart_tlb_flush((rdev))
  #define radeon_gart_set_page(rdev, i, p) (rdev)->asic->gart_set_page((rdev), (i), (p))
  #define radeon_cp_commit(rdev) (rdev)->asic->cp_commit((rdev))
  
  /* Common functions */
  /* AGP */
 +extern int radeon_gpu_reset(struct radeon_device *rdev);
  extern void radeon_agp_disable(struct radeon_device *rdev);
  extern int radeon_gart_table_vram_pin(struct radeon_device *rdev);
  extern void radeon_gart_restore(struct radeon_device *rdev);
@@@ -1213,8 -1197,6 +1210,8 @@@ extern int radeon_resume_kms(struct drm
  extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state);
  
  /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */
 +extern void r100_gpu_lockup_update(struct r100_gpu_lockup *lockup, struct radeon_cp *cp);
 +extern bool r100_gpu_cp_is_lockup(struct radeon_device *rdev, struct r100_gpu_lockup *lockup, struct radeon_cp *cp);
  
  /* rv200,rv250,rv280 */
  extern void r200_set_safe_registers(struct radeon_device *rdev);
@@@ -1291,7 -1273,7 +1288,7 @@@ extern void r600_scratch_init(struct ra
  extern int r600_blit_init(struct radeon_device *rdev);
  extern void r600_blit_fini(struct radeon_device *rdev);
  extern int r600_init_microcode(struct radeon_device *rdev);
 -extern int r600_gpu_reset(struct radeon_device *rdev);
 +extern int r600_asic_reset(struct radeon_device *rdev);
  /* r600 irq */
  extern int r600_irq_init(struct radeon_device *rdev);
  extern void r600_irq_fini(struct radeon_device *rdev);
@@@ -162,14 -162,12 +162,14 @@@ radeon_connector_analog_encoder_conflic
  {
        struct drm_device *dev = connector->dev;
        struct drm_connector *conflict;
 +      struct radeon_connector *radeon_conflict;
        int i;
  
        list_for_each_entry(conflict, &dev->mode_config.connector_list, head) {
                if (conflict == connector)
                        continue;
  
 +              radeon_conflict = to_radeon_connector(conflict);
                for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
                        if (conflict->encoder_ids[i] == 0)
                                break;
                                if (conflict->status != connector_status_connected)
                                        continue;
  
 +                              if (radeon_conflict->use_digital)
 +                                      continue;
 +
                                if (priority == true) {
                                        DRM_INFO("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict));
                                        DRM_INFO("in favor of %s\n", drm_get_connector_name(connector));
@@@ -292,7 -287,6 +292,7 @@@ int radeon_connector_set_property(struc
  
        if (property == rdev->mode_info.coherent_mode_property) {
                struct radeon_encoder_atom_dig *dig;
 +              bool new_coherent_mode;
  
                /* need to find digital encoder on connector */
                encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS);
                        return 0;
  
                dig = radeon_encoder->enc_priv;
 -              dig->coherent_mode = val ? true : false;
 -              radeon_property_change_mode(&radeon_encoder->base);
 +              new_coherent_mode = val ? true : false;
 +              if (dig->coherent_mode != new_coherent_mode) {
 +                      dig->coherent_mode = new_coherent_mode;
 +                      radeon_property_change_mode(&radeon_encoder->base);
 +              }
        }
  
        if (property == rdev->mode_info.tv_std_property) {
@@@ -1041,7 -1032,6 +1041,6 @@@ radeon_add_atom_connector(struct drm_de
        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)
        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)
                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)
                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)
                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)
                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)
        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,
                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)
@@@ -1250,7 -1226,6 +1235,6 @@@ radeon_add_legacy_connector(struct drm_
        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)
        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)
                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)
        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)
        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
                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)
@@@ -26,7 -26,6 +26,7 @@@
   *          Jerome Glisse
   */
  #include <linux/console.h>
 +#include <linux/slab.h>
  #include <drm/drmP.h>
  #include <drm/drm_crtc_helper.h>
  #include <drm/radeon_drm.h>
  #include "radeon.h"
  #include "atom.h"
  
 +static const char radeon_family_name[][16] = {
 +      "R100",
 +      "RV100",
 +      "RS100",
 +      "RV200",
 +      "RS200",
 +      "R200",
 +      "RV250",
 +      "RS300",
 +      "RV280",
 +      "R300",
 +      "R350",
 +      "RV350",
 +      "RV380",
 +      "R420",
 +      "R423",
 +      "RV410",
 +      "RS400",
 +      "RS480",
 +      "RS600",
 +      "RS690",
 +      "RS740",
 +      "RV515",
 +      "R520",
 +      "RV530",
 +      "RV560",
 +      "RV570",
 +      "R580",
 +      "R600",
 +      "RV610",
 +      "RV630",
 +      "RV670",
 +      "RV620",
 +      "RV635",
 +      "RS780",
 +      "RS880",
 +      "RV770",
 +      "RV730",
 +      "RV710",
 +      "RV740",
 +      "CEDAR",
 +      "REDWOOD",
 +      "JUNIPER",
 +      "CYPRESS",
 +      "HEMLOCK",
 +      "LAST",
 +};
 +
  /*
   * Clear GPU surface registers.
   */
@@@ -574,6 -525,7 +574,6 @@@ int radeon_device_init(struct radeon_de
        int r;
        int dma_bits;
  
 -      DRM_INFO("radeon: Initializing kernel modesetting.\n");
        rdev->shutdown = false;
        rdev->dev = &pdev->dev;
        rdev->ddev = ddev;
        rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
        rdev->gpu_lockup = false;
        rdev->accel_working = false;
 +
 +      DRM_INFO("initializing kernel modesetting (%s 0x%04X:0x%04X).\n",
 +              radeon_family_name[rdev->family], pdev->vendor, pdev->device);
 +
        /* mutex initialization are all done here so we
         * can recall function without having locking issues */
        mutex_init(&rdev->cs_mutex);
                /* Acceleration not working on AGP card try again
                 * with fallback to PCI or PCIE GART
                 */
 -              radeon_gpu_reset(rdev);
 +              radeon_asic_reset(rdev);
                radeon_fini(rdev);
                radeon_agp_disable(rdev);
                r = radeon_init(rdev);
@@@ -691,8 -639,6 +691,8 @@@ void radeon_device_fini(struct radeon_d
  {
        DRM_INFO("radeon: finishing device.\n");
        rdev->shutdown = true;
 +      /* evict vram memory */
 +      radeon_bo_evict_vram(rdev);
        radeon_fini(rdev);
        destroy_workqueue(rdev->wq);
        vga_switcheroo_unregister_client(rdev->pdev);
@@@ -730,9 -676,10 +730,10 @@@ int radeon_suspend_kms(struct drm_devic
                        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);
                        }
                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;
  }
@@@ -781,7 -728,7 +782,7 @@@ int radeon_resume_kms(struct drm_devic
        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 */
        return 0;
  }
  
 +int radeon_gpu_reset(struct radeon_device *rdev)
 +{
 +      int r;
 +
 +      radeon_save_bios_scratch_regs(rdev);
 +      radeon_suspend(rdev);
 +
 +      r = radeon_asic_reset(rdev);
 +      if (!r) {
 +              dev_info(rdev->dev, "GPU reset succeed\n");
 +              radeon_resume(rdev);
 +              radeon_restore_bios_scratch_regs(rdev);
 +              drm_helper_resume_force_mode(rdev->ddev);
 +              return 0;
 +      }
 +      /* bad news, how to tell it to userspace ? */
 +      dev_info(rdev->dev, "GPU reset failed\n");
 +      return r;
 +}
 +
  
  /*
   * Debugfs
   * Authors:
   *     David Airlie
   */
-     /*
-      *  Modularization
-      */
  #include <linux/module.h>
 +#include <linux/slab.h>
  #include <linux/fb.h>
  
  #include "drmP.h"
  
  #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_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 = {
        .owner = THIS_MODULE,
        .fb_check_var = drm_fb_helper_check_var,
        .fb_set_par = drm_fb_helper_set_par,
-       .fb_setcolreg = drm_fb_helper_setcolreg,
        .fb_fillrect = cfb_fillrect,
        .fb_copyarea = cfb_copyarea,
        .fb_imageblit = cfb_imageblit,
        .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)
  {
        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;
+       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);
+ }
  
- 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_pinned_object(struct radeon_fbdev *rfbdev,
+                                        struct drm_mode_fb_cmd *mode_cmd,
+                                        struct drm_gem_object **gobj_p)
  {
-       struct radeon_device *rdev = dev->dev_private;
-       struct fb_info *info;
-       struct radeon_fb_device *rfbdev;
-       struct drm_framebuffer *fb = NULL;
-       struct radeon_framebuffer *rfb;
-       struct drm_mode_fb_cmd mode_cmd;
+       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 = surface_width;
-       mode_cmd.height = surface_height;
-       /* avivo can't scanout real 24bpp */
-       if ((surface_bpp == 24) && ASIC_IS_AVIVO(rdev))
-               surface_bpp = 32;
-       mode_cmd.bpp = 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->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",
-                      surface_width, surface_height);
-               ret = -ENOMEM;
-               goto out;
+               printk(KERN_ERR "failed to allocate framebuffer (%d)\n",
+                      aligned_size);
+               return -ENOMEM;
        }
        rbo = gobj->driver_private;
  
                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;
  
        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);
-       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;
-       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;
        }
  
-       list_add(&fb->filp_head, &rdev->ddev->mode_config.fb_kernel_list);
+       *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;
  
-       *fb_p = fb;
-       rfb = to_radeon_framebuffer(fb);
-       rdev->fbdev_rfb = rfb;
-       rdev->fbdev_rbo = rbo;
+       ret = radeonfb_create_pinned_object(rfbdev, &mode_cmd, &gobj);
+       rbo = gobj->driver_private;
  
-       info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
+       /* okay we have an object now allocate the framebuffer */
+       info = framebuffer_alloc(0, device);
        if (info == NULL) {
                ret = -ENOMEM;
                goto out_unref;
        }
  
-       rdev->fbdev_info = info;
-       rfbdev = info->par;
-       rfbdev->helper.funcs = &radeon_fb_helper_funcs;
-       rfbdev->helper.dev = dev;
-       ret = drm_fb_helper_init_crtc_count(&rfbdev->helper, rdev->num_crtc,
-                                           RADEONFB_CONN_LIMIT);
-       if (ret)
-               goto out_unref;
+       info->par = rfbdev;
  
-       memset_io(fbptr, 0x0, aligned_size);
+       radeon_framebuffer_init(rdev->ddev, &rfbdev->rfb, &mode_cmd, gobj);
+       fb = &rfbdev->rfb.base;
+       /* setup helper */
+       rfbdev->helper.fb = fb;
+       rfbdev->helper.fbdev = info;
+       memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo));
  
        strcpy(info->fix.id, "radeondrmfb");
  
        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, 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;
        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)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);
  
-       fb->fbdev = info;
-       rfbdev->rfb = rfb;
-       rfbdev->rdev = rdev;
-       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) {
-               list_del(&fb->filp_head);
                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_fb_helper *helper,
+                                          struct drm_fb_helper_surface_size *sizes)
+ {
+       struct radeon_fbdev *rfbdev = (struct radeon_fbdev *)helper;
+       int new_fb = 0;
+       int ret;
+       if (!helper->fb) {
+               ret = radeonfb_create(rfbdev, sizes);
+               if (ret)
+                       return ret;
+               new_fb = 1;
+       }
+       return new_fb;
+ }
  static char *mode_option;
  int radeon_parse_options(char *options)
  {
        return 0;
  }
  
int radeonfb_probe(struct drm_device *dev)
void radeonfb_hotplug(struct drm_device *dev, bool polled)
  {
        struct radeon_device *rdev = dev->dev_private;
-       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;
+       drm_helper_fb_hpd_irq_event(&rdev->mode_info.rfbdev->helper);
+ }
  
-       return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeonfb_create);
+ static void radeon_fb_output_status_changed(struct drm_fb_helper *fb_helper)
+ {
+       drm_helper_fb_hotplug_event(fb_helper, true);
  }
  
int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_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;
+       if (rfbdev->helper.fbdev) {
+               info = rfbdev->helper.fbdev;
+               unregister_framebuffer(info);
+               if (info->cmap.len)
+                       fb_dealloc_cmap(&info->cmap);
+               framebuffer_release(info);
        }
-       info = fb->fbdev;
-       if (info) {
-               struct radeon_fb_device *rfbdev = info->par;
+       if (rfb->obj) {
                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);
+               drm_gem_object_unreference_unlocked(rfb->obj);
        }
+       drm_fb_helper_fini(&rfbdev->helper);
+       drm_framebuffer_cleanup(&rfb->base);
  
-       printk(KERN_INFO "unregistered panic notifier\n");
+       return 0;
+ }
  
+ 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)
+               return -ENOMEM;
+       rfbdev->rdev = rdev;
+       rdev->mode_info.rfbdev = rfbdev;
+       rfbdev->helper.funcs = &radeon_fb_helper_funcs;
+       drm_fb_helper_init(rdev->ddev, &rfbdev->helper,
+                          rdev->num_crtc,
+                          RADEONFB_CONN_LIMIT, true);
+       drm_fb_helper_single_add_all_connectors(&rfbdev->helper);
+       drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel);
        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;
+ }
+ 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;
  }
- EXPORT_SYMBOL(radeonfb_remove);
- MODULE_LICENSE("GPL");
diff --combined include/drm/drm_crtc.h
@@@ -271,8 -271,6 +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;
@@@ -369,9 -367,6 +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
@@@ -391,8 -386,6 +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 */
@@@ -521,7 -514,6 +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;
  };
  
  /**
@@@ -548,16 -540,9 +540,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 -575,6 +575,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;
@@@ -666,6 -648,8 +648,6 @@@ extern void drm_fb_release(struct drm_f
  extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
  extern struct edid *drm_get_edid(struct drm_connector *connector,
                                 struct i2c_adapter *adapter);
 -extern int drm_do_probe_ddc_edid(struct i2c_adapter *adapter,
 -                               unsigned char *buf, int len);
  extern int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid);
  extern void drm_mode_probed_add(struct drm_connector *connector, struct drm_display_mode *mode);
  extern void drm_mode_remove(struct drm_connector *connector, struct drm_display_mode *mode);
@@@ -797,10 -781,6 +779,10 @@@ extern struct drm_display_mode *drm_cvt
  extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev,
                                int hdisplay, int vdisplay, int vrefresh,
                                bool interlaced, int margins);
 +extern struct drm_display_mode *drm_gtf_mode_complex(struct drm_device *dev,
 +                              int hdisplay, int vdisplay, int vrefresh,
 +                              bool interlaced, int margins, int GTF_M,
 +                              int GTF_2C, int GTF_K, int GTF_2J);
  extern int drm_add_modes_noedid(struct drm_connector *connector,
                                int hdisplay, int vdisplay);