@@ -1097,7 +1097,8 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
register_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
}
- list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
+ if (list_empty(&fb_helper->kernel_fb_list))
+ list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
return 0;
}
@@ -1770,23 +1771,47 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
{
struct drm_device *dev = fb_helper->dev;
u32 max_width, max_height;
+ bool modes_before_probe = false;
+ bool modes_after_probe = false;
+ int i;
- mutex_lock(&fb_helper->dev->mode_config.mutex);
+ drm_modeset_lock_all(dev);
if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
fb_helper->delayed_hotplug = true;
- mutex_unlock(&fb_helper->dev->mode_config.mutex);
+ drm_modeset_unlock_all(dev);
return 0;
}
DRM_DEBUG_KMS("\n");
- max_width = fb_helper->fb->width;
- max_height = fb_helper->fb->height;
+ /* If so far we have no modes and thus only the default 1024x768 fb,
+ * allow probed modes to occupy the maximum surface size */
+ for (i = 0; i < fb_helper->crtc_count; i++)
+ if (fb_helper->crtc_info[i].desired_mode) {
+ modes_before_probe = true;
+ break;
+ }
+ if (!modes_before_probe) {
+ max_width = dev->mode_config.max_width;
+ max_height = dev->mode_config.max_width;
+ } else {
+ max_width = fb_helper->fb->width;
+ max_height = fb_helper->fb->height;
+ }
drm_fb_helper_probe_connector_modes(fb_helper, max_width, max_height);
- mutex_unlock(&fb_helper->dev->mode_config.mutex);
-
- drm_modeset_lock_all(dev);
drm_setup_crtcs(fb_helper);
+
+ /* If previously there were no connectors with modes and now we
+ * found some, create new fb and replace default 1024x768 fb */
+ for (i = 0; i < fb_helper->crtc_count; i++)
+ if (fb_helper->crtc_info[i].desired_mode) {
+ modes_after_probe = true;
+ break;
+ }
+ if (!modes_before_probe && modes_after_probe)
+ drm_fb_helper_single_fb_probe(fb_helper,
+ fb_helper->fb->bits_per_pixel);
+
drm_modeset_unlock_all(dev);
drm_fb_helper_set_par(fb_helper->fbdev);
@@ -199,6 +199,8 @@ out:
return ret;
}
+static void intel_fbdev_destroy(struct fb_info *info);
+
static int intelfb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -220,6 +222,7 @@ static int intelfb_create(struct drm_fb_helper *helper,
" releasing it\n",
intel_fb->base.width, intel_fb->base.height,
sizes->fb_width, sizes->fb_height);
+ intel_fbdev_destroy(ifbdev->helper.fbdev);
drm_framebuffer_unreference(&intel_fb->base);
intel_fb = ifbdev->fb = NULL;
}
@@ -545,12 +548,9 @@ static const struct drm_fb_helper_funcs intel_fb_helper_funcs = {
.fb_probe = intelfb_create,
};
-static void intel_fbdev_destroy(struct drm_device *dev,
- struct intel_fbdev *ifbdev)
+static void intel_fbdev_destroy(struct fb_info *info)
{
- if (ifbdev->helper.fbdev) {
- struct fb_info *info = ifbdev->helper.fbdev;
-
+ if (info) {
unregister_framebuffer(info);
iounmap(info->screen_base);
if (info->cmap.len)
@@ -558,11 +558,6 @@ static void intel_fbdev_destroy(struct drm_device *dev,
framebuffer_release(info);
}
-
- drm_fb_helper_fini(&ifbdev->helper);
-
- drm_framebuffer_unregister_private(&ifbdev->fb->base);
- drm_framebuffer_remove(&ifbdev->fb->base);
}
/*
@@ -750,13 +745,18 @@ void intel_fbdev_initial_config(void *data, async_cookie_t cookie)
void intel_fbdev_fini(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
- if (!dev_priv->fbdev)
+ struct intel_fbdev *ifbdev = dev_priv->fbdev;
+
+ if (!ifbdev)
return;
flush_work(&dev_priv->fbdev_suspend_work);
-
async_synchronize_full();
- intel_fbdev_destroy(dev, dev_priv->fbdev);
+
+ intel_fbdev_destroy(ifbdev->helper.fbdev);
+ drm_fb_helper_fini(&ifbdev->helper);
+ drm_framebuffer_unregister_private(&ifbdev->fb->base);
+ drm_framebuffer_remove(&ifbdev->fb->base);
kfree(dev_priv->fbdev);
dev_priv->fbdev = NULL;
}
@@ -305,6 +305,9 @@ nouveau_fbcon_zfill(struct drm_device *dev, struct nouveau_fbdev *fbcon)
}
static int
+nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon);
+
+static int
nouveau_fbcon_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -322,6 +325,11 @@ nouveau_fbcon_create(struct drm_fb_helper *helper,
struct pci_dev *pdev = dev->pdev;
int size, ret;
+ if (helper->fbdev) {
+ nouveau_fbcon_accel_fini(dev);
+ nouveau_fbcon_destroy(dev, fbcon);
+ }
+
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
@@ -467,7 +475,6 @@ nouveau_fbcon_destroy(struct drm_device *dev, struct nouveau_fbdev *fbcon)
drm_gem_object_unreference_unlocked(&nouveau_fb->nvbo->gem);
nouveau_fb->nvbo = NULL;
}
- drm_fb_helper_fini(&fbcon->helper);
drm_framebuffer_unregister_private(&nouveau_fb->base);
drm_framebuffer_cleanup(&nouveau_fb->base);
return 0;
@@ -567,6 +574,7 @@ nouveau_fbcon_fini(struct drm_device *dev)
nouveau_fbcon_accel_fini(dev);
nouveau_fbcon_destroy(dev, drm->fbcon);
+ drm_fb_helper_fini(&drm->fbcon->helper);
kfree(drm->fbcon);
drm->fbcon = NULL;
}
@@ -216,6 +216,8 @@ out_unref:
return ret;
}
+static int radeon_fbdev_destroy(struct radeon_fbdev *rfbdev);
+
static int radeonfb_create(struct drm_fb_helper *helper,
struct drm_fb_helper_surface_size *sizes)
{
@@ -231,6 +233,9 @@ static int radeonfb_create(struct drm_fb_helper *helper,
int ret;
unsigned long tmp;
+ if (helper->fbdev)
+ radeon_fbdev_destroy(rfbdev);
+
mode_cmd.width = sizes->surface_width;
mode_cmd.height = sizes->surface_height;
@@ -337,7 +342,7 @@ void radeon_fb_output_poll_changed(struct radeon_device *rdev)
drm_fb_helper_hotplug_event(&rdev->mode_info.rfbdev->helper);
}
-static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev)
+static int radeon_fbdev_destroy(struct radeon_fbdev *rfbdev)
{
struct fb_info *info;
struct radeon_framebuffer *rfb = &rfbdev->rfb;
@@ -355,7 +360,6 @@ static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfb
radeonfb_destroy_pinned_object(rfb->obj);
rfb->obj = NULL;
}
- drm_fb_helper_fini(&rfbdev->helper);
drm_framebuffer_unregister_private(&rfb->base);
drm_framebuffer_cleanup(&rfb->base);
@@ -419,7 +423,8 @@ void radeon_fbdev_fini(struct radeon_device *rdev)
if (!rdev->mode_info.rfbdev)
return;
- radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev);
+ radeon_fbdev_destroy(rdev->mode_info.rfbdev);
+ drm_fb_helper_fini(&rdev->mode_info.rfbdev->helper);
kfree(rdev->mode_info.rfbdev);
rdev->mode_info.rfbdev = NULL;
}