All of lore.kernel.org
 help / color / mirror / Atom feed
From: Lukas Wunner <lukas@wunner.de>
To: dri-devel@lists.freedesktop.org
Cc: Andreas Heider <andreas@meetr.de>,
	Paul Hordiienko <pvt.gord@gmail.com>,
	William Brown <william@blackhats.net.au>,
	Bruno Bierbaumer <bruno@bierbaumer.net>,
	Matthew Garrett <mjg59@coreos.com>,
	Dave Airlie <airlied@redhat.com>
Subject: [PATCH v2 16/22] drm: Create new fb and replace default 1024x768 fb on hotplug event
Date: Mon, 25 May 2015 15:15:52 +0200	[thread overview]
Message-ID: <afe73d5a7382f85c9bdbfc46197a52c4278c99c7.1439288957.git.lukas@wunner.de> (raw)
In-Reply-To: <53015af5b9a91332d3d74ef54fff587395447ecf.1439288957.git.lukas@wunner.de>

If no connectors with modes are found, drm_fb_helper_single_fb_probe()
will allocate a default 1024x768 fb. This becomes a problem if one of
the connectors subsequently changes to connected status: We're stuck
with the default fb even if the display allows a larger resolution
(or doesn't support 1024x768 at all) since modes larger than the
existing fb will be deemed ineligible by drm_fb_helper_hotplug_event().

One relevant use case are dual gpu laptops such as the MacBook Pro
which require either a handler to switch DDC lines, or the active gpu
to proxy the DDC/AUX communication: Both the handler and the active gpu
may register late, and consequently, the inactive gpu's eDP and LVDS
connectors may seem disconnected at startup but may later on turn out
to be connected.

Although primarily aimed at vga_switcheroo setups, the patch may come
in handy in other use cases: One example is a desktop machine that is
(inadvertantly or otherwise) booted headless, and the user later on
plugs in a display. Another example is unplugging of the display by
the user and replacement with a larger one.

Solve this by checking if there aren't any modes at all. If so, don't
constrain probed modes to the existing fb size but rather allow them
to occupy the maximum surface size. After probing, check if we found
any modes. If we did, discard the existing fb and recreate it from
scratch by invoking the driver's ->fb_probe callback by way of
drm_fb_helper_single_fb_probe().

That function is normally called only once on driver initialization.
The fb_helper is kept so ensure that it's not added once more to the
panic handler list.

Because multiple hotplug events may fire simultaneously, protect
connector probing and fb recreation with drm_modeset_lock_all().
In particular, drm_fb_helper_hotplug_event() may itself fire another
hotplug event by way of drm_fb_helper_probe_connector_modes(), which
invokes the ->fill_modes callback, which for most drivers is
drm_helper_probe_single_connector_modes(), which schedules
output_poll_execute().

Amend the ->fb_probe callback in the i915, nouveau and radeon drivers
to discard an existing fb. Because the _destroy() function is now used
further up in intel_fbdev.c, nouveau_fbcon.c and radeon_fb.c, add a
prototype for it. This is intended to ease reviewing, the prototype
shall be replaced by the body of the function before this gets merged.

Drivers other than these three will just create an additional fb under
the limited circumstances described above, which wastes memory but is
otherwise "mostly harmless".

Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=88861
Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=61115
Tested-by: Paul Hordiienko <pvt.gord@gmail.com>
    [MBP  6,2 2010  intel ILK + nvidia GT216  pre-retina]
Tested-by: William Brown <william@blackhats.net.au>
    [MBP  8,2 2011  intel SNB + amd turks     pre-retina]
Tested-by: Lukas Wunner <lukas@wunner.de>
    [MBP  9,1 2012  intel IVB + nvidia GK107  pre-retina]
Tested-by: Bruno Bierbaumer <bruno@bierbaumer.net>
    [MBP 11,3 2013  intel HSW + nvidia GK107  retina -- work in progress]

Signed-off-by: Lukas Wunner <lukas@wunner.de>
---
 drivers/gpu/drm/drm_fb_helper.c         | 41 ++++++++++++++++++++++++++-------
 drivers/gpu/drm/i915/intel_fbdev.c      | 26 ++++++++++-----------
 drivers/gpu/drm/nouveau/nouveau_fbcon.c | 10 +++++++-
 drivers/gpu/drm/radeon/radeon_fb.c      | 11 ++++++---
 4 files changed, 63 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 73f90f7..6df0ee8 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -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);
 
diff --git a/drivers/gpu/drm/i915/intel_fbdev.c b/drivers/gpu/drm/i915/intel_fbdev.c
index dd138aa..637146f 100644
--- a/drivers/gpu/drm/i915/intel_fbdev.c
+++ b/drivers/gpu/drm/i915/intel_fbdev.c
@@ -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;
 }
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
index 6751553..7b7b112 100644
--- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
@@ -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;
 }
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
index aeb6767..88b9f32 100644
--- a/drivers/gpu/drm/radeon/radeon_fb.c
+++ b/drivers/gpu/drm/radeon/radeon_fb.c
@@ -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;
 }
-- 
1.8.5.2 (Apple Git-48)

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

  reply	other threads:[~2015-08-12 11:39 UTC|newest]

Thread overview: 40+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-11 10:29 [PATCH v2 00/22] Enable gpu switching on the MacBook Pro Lukas Wunner
2012-09-07 15:22 ` [PATCH v2 01/22] vga_switcheroo: Add support for switching only the DDC Lukas Wunner
2012-09-07 15:22   ` [PATCH v2 02/22] vga_switcheroo: Add helper function to get the active client Lukas Wunner
2012-09-07 15:22     ` [PATCH v2 03/22] apple-gmux: Add switch_ddc support Lukas Wunner
2012-09-07 15:22       ` [PATCH v2 04/22] drm/edid: Switch DDC when reading the EDID Lukas Wunner
2012-12-22  2:52         ` [PATCH v2 05/22] vga_switcheroo: Lock/unlock DDC lines Lukas Wunner
2015-03-27 11:29           ` [PATCH v2 06/22] vga_switcheroo: Lock/unlock DDC lines harder Lukas Wunner
2015-04-21  8:39             ` [PATCH v2 07/22] Revert "vga_switcheroo: Add helper function to get the active client" Lukas Wunner
2015-08-02  9:06               ` [PATCH v2 08/22] Revert "vga_switcheroo: add reprobe hook for fbcon to recheck connected outputs." Lukas Wunner
2015-05-09 15:20                 ` [PATCH v2 09/22] drm/nouveau: Lock/unlock DDC lines on probe Lukas Wunner
2014-03-05 22:34                   ` [PATCH v2 10/22] apple-gmux: Assign apple_gmux_data before registering Lukas Wunner
2015-04-20 10:08                     ` [PATCH v2 11/22] vga_switcheroo: Generate hotplug event on handler and proxy registration Lukas Wunner
2015-07-15 11:57                       ` [PATCH v2 12/22] drm/i915: Preserve SSC earlier Lukas Wunner
2015-04-19 15:01                         ` [PATCH v2 13/22] drm/i915: Reprobe eDP and LVDS connectors on hotplug event Lukas Wunner
2015-06-30  9:06                           ` [PATCH v2 14/22 RESEND] drm/i915: Fix failure paths around initial fbdev allocation Lukas Wunner
2015-07-04  9:50                             ` [PATCH v2 15/22 RESEND] drm/i915: On fb alloc failure, unref gem object where it gets refed Lukas Wunner
2015-05-25 13:15                               ` Lukas Wunner [this message]
     [not found]                                 ` <afe73d5a7382f85c9bdbfc46197a52c4278c99c7.1439288957.git.lukas-JFq808J9C/izQB+pC5nmwQ@public.gmane.org>
2015-07-23 10:59                                   ` [PATCH v2 17/22] drm/nouveau/timer: Fall back to kernel timer if GPU timer read failed Lukas Wunner
2015-07-29 19:23                                     ` [PATCH v2 18/22 EXPERIMENTAL] vga_switcheroo: Allow using active client as proxy when reading DDC/AUX Lukas Wunner
2015-05-13 19:50                                       ` [PATCH v2 19/22 EXPERIMENTAL] drm: Amend struct drm_dp_aux with connector attribute Lukas Wunner
2015-05-06 12:06                                         ` [PATCH v2 20/22 EXPERIMENTAL] drm: Use vga_switcheroo active client as proxy when reading DDC/AUX Lukas Wunner
2015-07-30 11:31                                           ` [PATCH v2 21/22 EXPERIMENTAL] drm/nouveau/i2c: " Lukas Wunner
2015-06-07  9:20                                             ` [PATCH v2 22/22 EXPERIMENTAL] drm/nouveau: Use vga_switcheroo active client as proxy when probing DDC on LVDS Lukas Wunner
2015-08-31 20:23                         ` [PATCH v2 12/22] drm/i915: Preserve SSC earlier Jesse Barnes
2015-09-01  6:46                           ` Jani Nikula
2015-08-12 14:25               ` [PATCH v2 07/22] Revert "vga_switcheroo: Add helper function to get the active client" Daniel Vetter
2015-08-12 17:34                 ` Lukas Wunner
2015-08-12 21:10                   ` Daniel Vetter
2015-08-12 14:23             ` [PATCH v2 06/22] vga_switcheroo: Lock/unlock DDC lines harder Daniel Vetter
     [not found] ` <cover.1439288957.git.lukas-JFq808J9C/izQB+pC5nmwQ@public.gmane.org>
2015-08-12 14:16   ` [Intel-gfx] [PATCH v2 00/22] Enable gpu switching on the MacBook Pro Daniel Vetter
2015-08-12 23:37     ` Lukas Wunner
     [not found]       ` <20150812233711.GA6002-JFq808J9C/izQB+pC5nmwQ@public.gmane.org>
2015-08-13  6:50         ` [Intel-gfx] " Daniel Vetter
2015-08-16 19:10           ` Lukas Wunner
2015-08-25  7:36 ` Lukas Wunner
2015-08-25  8:21   ` Daniel Vetter
2015-08-26 14:01     ` Lukas Wunner
2015-08-29 14:15 ` Lukas Wunner
2015-08-31 19:15   ` Jani Nikula
2015-09-01  6:48     ` Jani Nikula
2015-09-04 14:00     ` Lukas Wunner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=afe73d5a7382f85c9bdbfc46197a52c4278c99c7.1439288957.git.lukas@wunner.de \
    --to=lukas@wunner.de \
    --cc=airlied@redhat.com \
    --cc=andreas@meetr.de \
    --cc=bruno@bierbaumer.net \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=mjg59@coreos.com \
    --cc=pvt.gord@gmail.com \
    --cc=william@blackhats.net.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.