@@ -487,6 +487,9 @@ int drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
if (!drm_fbdev_emulation)
return -ENODEV;
+ if (fb_helper->deferred_setup)
+ return 0;
+
mutex_lock(&fb_helper->lock);
drm_modeset_lock_all(dev);
@@ -1452,6 +1455,23 @@ unlock:
}
EXPORT_SYMBOL(drm_fb_helper_pan_display);
+static bool drm_fb_helper_maybe_connected(struct drm_fb_helper *helper)
+{
+ bool connected = false;
+ unsigned int i;
+
+ for (i = 0; i < helper->connector_count; i++) {
+ struct drm_fb_helper_connector *fb = helper->connector_info[i];
+
+ if (fb->connector->status != connector_status_disconnected) {
+ connected = true;
+ break;
+ }
+ }
+
+ return connected;
+}
+
/*
* Allocates the backing storage and sets up the fbdev info structure through
* the ->fb_probe callback and then registers the fbdev and sets up the panic
@@ -1554,6 +1574,17 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
sizes.fb_height = min_t(u32, desired_mode->vdisplay + y, sizes.fb_height);
}
+ /*
+ * If everything's disconnected, there's no use in attempting to set
+ * up fbdev.
+ */
+ if (!drm_fb_helper_maybe_connected(fb_helper)) {
+ DRM_INFO("No outputs connected, deferring setup\n");
+ fb_helper->preferred_bpp = preferred_bpp;
+ fb_helper->deferred_setup = true;
+ return 0;
+ }
+
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. */
@@ -1593,6 +1624,7 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
list_add(&fb_helper->kernel_fb_list, &kernel_fb_helper_list);
+ fb_helper->deferred_setup = false;
return 0;
}
@@ -2294,6 +2326,10 @@ int drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
if (!drm_fbdev_emulation)
return 0;
+ if (fb_helper->deferred_setup)
+ return drm_fb_helper_initial_config(fb_helper,
+ fb_helper->preferred_bpp);
+
mutex_lock(&fb_helper->lock);
mutex_lock(&fb_helper->dev->mode_config.mutex);
@@ -219,6 +219,27 @@ struct drm_fb_helper {
bool delayed_hotplug;
/**
+ * @deferred_setup:
+ *
+ * If no outputs are connected (disconnected or unknown) the FB helper
+ * code will defer setup until at least one of the outputs shows up.
+ * This field keeps track of the status so that setup can be retried
+ * at every hotplug event until it succeeds eventually.
+ */
+ bool deferred_setup;
+
+ /**
+ * @preferred_bpp:
+ *
+ * Temporary storage for the driver's preferred BPP setting passed to
+ * FB helper initialization. This needs to be tracked so that deferred
+ * FB helper setup can pass this on.
+ *
+ * See also: @deferred_setup
+ */
+ int preferred_bpp;
+
+ /**
* @atomic:
*
* Use atomic updates for restore_fbdev_mode(), etc. This defaults to