diff mbox

[v2,11/22] vga_switcheroo: Generate hotplug event on handler and proxy registration

Message ID e14b2b0ba7d6248edbdb74935f5743a705a62bb6.1439288957.git.lukas@wunner.de (mailing list archive)
State New, archived
Headers show

Commit Message

Lukas Wunner April 20, 2015, 10:08 a.m. UTC
On laptops which require the handler to switch DDC lines, already
registered clients must reprobe their connectors if the handler
registers late. This is the case on pre-retina MacBook Pros,
which use a gmux controller as handler.

Based (loosely) on a patch by Matthew Garrett <mjg59@srcf.ucam.org>
who used an additional driver callback rather than generating a
hotplug event:
http://lists.freedesktop.org/archives/dri-devel/2014-June/060941.html

Matthew Garrett invoked the driver callback in vga_switcheroo_enable().
On pre-retina MacBook Pros, this delayed reprobing until a second gpu
registers, which may never happen if its driver is not installed or
blacklisted. The MacBook Pro 2011 in particular suffers from widespread
gpu soldering issues due to overheating, eventually rendering it
unusable with OS X. Folks are solving this by moving to Linux and
disabling the discrete gpu entirely. In those cases we can't wait
for a second gpu to register, we do need to reprobe as soon as the
handler registers:
https://ifixit.org/blog/6882/why-i-drilled-holes-in-my-macbook-pro-and-put-it-in-the-oven/#comment-26745

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/vga/vga_switcheroo.c | 34 ++++++++++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
diff mbox

Patch

diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index 8027c9f..94b0b6f 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -18,6 +18,8 @@ 
  - can_switch - check if the device is in a position to switch now
  */
 
+#include <drm/drm_crtc_helper.h>
+
 #include <linux/module.h>
 #include <linux/seq_file.h>
 #include <linux/uaccess.h>
@@ -35,6 +37,7 @@ 
 struct vga_switcheroo_client {
 	struct pci_dev *pdev;
 	struct fb_info *fb_info;
+	struct work_struct reprobe_work;
 	int pwr_state;
 	const struct vga_switcheroo_client_ops *ops;
 	int id;
@@ -106,6 +109,30 @@  static void vga_switcheroo_enable(void)
 	vgasr_priv.active = true;
 }
 
+static void vga_switcheroo_reprobe_client(struct work_struct *work)
+{
+	struct vga_switcheroo_client *client =
+		container_of(work, struct vga_switcheroo_client, reprobe_work);
+	void (*generate_hotplug_event)(struct drm_device *);
+
+	generate_hotplug_event = symbol_get(drm_kms_helper_hotplug_event);
+	if (!generate_hotplug_event)
+		return;
+
+	generate_hotplug_event(pci_get_drvdata(client->pdev));
+}
+
+static void vga_switcheroo_reprobe_inactive_clients(void)
+{
+	struct vga_switcheroo_client *client;
+
+	list_for_each_entry(client, &vgasr_priv.clients, list)
+		if (!client->active && client_is_vga(client)) {
+			cancel_work_sync(&client->reprobe_work);
+			schedule_work(&client->reprobe_work);
+		}
+}
+
 int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
 {
 	mutex_lock(&vgasr_mutex);
@@ -115,11 +142,17 @@  int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler)
 	}
 
 	vgasr_priv.handler = handler;
+
 	if (vga_switcheroo_ready()) {
 		printk(KERN_INFO "vga_switcheroo: enabled\n");
 		vga_switcheroo_enable();
 	}
 	mutex_unlock(&vgasr_mutex);
+
+	/* clients which registered before the handler must reprobe */
+	if (vgasr_priv.handler->switch_ddc)
+		vga_switcheroo_reprobe_inactive_clients();
+
 	return 0;
 }
 EXPORT_SYMBOL(vga_switcheroo_register_handler);
@@ -153,6 +186,7 @@  static int register_client(struct pci_dev *pdev,
 	client->id = id;
 	client->active = active;
 	client->driver_power_control = driver_power_control;
+	INIT_WORK(&client->reprobe_work, vga_switcheroo_reprobe_client);
 
 	mutex_lock(&vgasr_mutex);
 	list_add_tail(&client->list, &vgasr_priv.clients);