@@ -505,6 +505,9 @@ __drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
WARN_ON(!mutex_is_locked(&fb_helper->lock));
+ if (fb_helper->deferred_setup)
+ return 0;
+
drm_modeset_lock_all(dev);
ret = restore_fbdev_mode(fb_helper);
@@ -1611,6 +1614,23 @@ int drm_fb_helper_pan_display(struct fb_var_screeninfo *var,
}
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
@@ -2268,8 +2288,6 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper,
int i;
DRM_DEBUG_KMS("\n");
- if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
- DRM_DEBUG_KMS("No connectors reported connected with modes\n");
/* prevent concurrent modification of connector_count by hotplug */
lockdep_assert_held(&fb_helper->dev->mode_config.mutex);
@@ -2351,6 +2369,7 @@ static int __drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper,
int bpp_sel)
{
struct drm_device *dev = fb_helper->dev;
+ unsigned int width, height;
struct fb_info *info;
int ret;
@@ -2360,14 +2379,34 @@ static int __drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper,
WARN_ON(!mutex_is_locked(&fb_helper->lock));
mutex_lock(&dev->mode_config.mutex);
- drm_setup_crtcs(fb_helper,
- dev->mode_config.max_width,
- dev->mode_config.max_height);
+
+ width = dev->mode_config.max_width;
+ height = dev->mode_config.max_height;
+
+ if (drm_fb_helper_probe_connector_modes(fb_helper, width, height) == 0)
+ DRM_DEBUG_KMS("No connectors reported connected with modes\n");
+
+ /*
+ * 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 = bpp_sel;
+ fb_helper->deferred_setup = true;
+ mutex_unlock(&dev->mode_config.mutex);
+ return 0;
+ }
+
+ drm_setup_crtcs(fb_helper, width, height);
+
ret = drm_fb_helper_single_fb_probe(fb_helper, bpp_sel);
mutex_unlock(&dev->mode_config.mutex);
if (ret)
return ret;
+ fb_helper->deferred_setup = false;
+
info = fb_helper->fbdev;
info->var.pixclock = 0;
ret = register_framebuffer(info);
@@ -2451,6 +2490,10 @@ static int __drm_fb_helper_hotplug_event(struct drm_fb_helper *fb_helper)
WARN_ON(!mutex_is_locked(&fb_helper->lock));
+ if (fb_helper->deferred_setup)
+ return __drm_fb_helper_initial_config(fb_helper,
+ fb_helper->preferred_bpp);
+
mutex_lock(&dev->mode_config.mutex);
if (!fb_helper->fb || !drm_fb_helper_is_bound(fb_helper)) {
@@ -227,6 +227,29 @@ struct drm_fb_helper {
* needs to be reprobe when fbdev is in control again.
*/
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.
+ *
+ * Protected by @lock.
+ */
+ 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;
};
/**