diff mbox

[v4,07/11] drm/fb-helper: Support deferred setup

Message ID 20170329144401.1804-8-thierry.reding@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Thierry Reding March 29, 2017, 2:43 p.m. UTC
From: Thierry Reding <treding@nvidia.com>

FB helper code falls back to a 1024x768 mode if no outputs are connected
or don't report back any modes upon initialization. This can be annoying
because outputs that are added to FB helper later on can't be used with
FB helper if they don't support a matching mode.

The fallback is in place because VGA connectors can happen to report an
unknown connection status even when they are in fact connected.

Some drivers have custom solutions in place to defer FB helper setup
until at least one output is connected. But the logic behind these
solutions is always the same and there is nothing driver-specific about
it, so a better alterative is to fix the FB helper core and add support
for all drivers automatically.

This patch adds support for deferred FB helper setup. It checks all the
connectors for their connection status, and if all of them report to be
disconnected marks the FB helper as needing deferred setup. Whet setup
is deferred, the FB helper core will automatically retry setup after a
hotplug event, and it will keep trying until it succeeds.

Tested-by: John Stultz <john.stultz@linaro.org>
Reviewed-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/gpu/drm/drm_fb_helper.c | 53 +++++++++++++++++++++++++++++++++++++----
 include/drm/drm_fb_helper.h     | 23 ++++++++++++++++++
 2 files changed, 71 insertions(+), 5 deletions(-)
diff mbox

Patch

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 21a90322531c..e9fe47d218e1 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -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)) {
diff --git a/include/drm/drm_fb_helper.h b/include/drm/drm_fb_helper.h
index 44e2c57a7049..b0af21b371ea 100644
--- a/include/drm/drm_fb_helper.h
+++ b/include/drm/drm_fb_helper.h
@@ -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;
 };
 
 /**