f34ecf
From 471289fa1dc359555ceed6302f7d9605ab6be3ea Mon Sep 17 00:00:00 2001
f34ecf
From: Dave Airlie <airlied@redhat.com>
f34ecf
Date: Mon, 2 Apr 2018 16:49:02 -0400
f34ecf
Subject: [PATCH] autobind GPUs to the screen
f34ecf
f34ecf
This is a modified version of a patch we've been carry-ing in Fedora and
f34ecf
RHEL for years now. This patch automatically adds secondary GPUs to the
f34ecf
master as output sink / offload source making e.g. the use of
f34ecf
slave-outputs just work, with requiring the user to manually run
f34ecf
"xrandr --setprovideroutputsource" before he can hookup an external
f34ecf
monitor to his hybrid graphics laptop.
f34ecf
f34ecf
There is one problem with this patch, which is why it was not upstreamed
f34ecf
before. What to do when a secondary GPU gets detected really is a policy
f34ecf
decission (e.g. one may want to autobind PCI GPUs but not USB ones) and
f34ecf
as such should be under control of the Desktop Environment.
f34ecf
f34ecf
Unconditionally adding autobinding support to the xserver will result
f34ecf
in races between the DE dealing with the hotplug of a secondary GPU
f34ecf
and the server itself dealing with it.
f34ecf
f34ecf
However we've waited for years for any Desktop Environments to actually
f34ecf
start doing some sort of autoconfiguration of secondary GPUs and there
f34ecf
is still not a single DE dealing with this, so I believe that it is
f34ecf
time to upstream this now.
f34ecf
f34ecf
To avoid potential future problems if any DEs get support for doing
f34ecf
secondary GPU configuration themselves, the new autobind functionality
f34ecf
is made optional. Since no DEs currently support doing this themselves it
f34ecf
is enabled by default. When DEs grow support for doing this themselves
f34ecf
they can disable the servers autobinding through the servers cmdline or a
f34ecf
xorg.conf snippet.
f34ecf
f34ecf
Signed-off-by: Dave Airlie <airlied@gmail.com>
f34ecf
[hdegoede@redhat.com: Make configurable, fix with nvidia, submit upstream]
f34ecf
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
f34ecf
---
f34ecf
 hw/xfree86/common/xf86Config.c      | 19 +++++++++++++++++++
f34ecf
 hw/xfree86/common/xf86Globals.c     |  2 ++
f34ecf
 hw/xfree86/common/xf86Init.c        | 20 ++++++++++++++++++++
f34ecf
 hw/xfree86/common/xf86Priv.h        |  1 +
f34ecf
 hw/xfree86/common/xf86Privstr.h     |  1 +
f34ecf
 hw/xfree86/common/xf86platformBus.c |  4 ++++
f34ecf
 hw/xfree86/man/Xorg.man             |  7 +++++++
f34ecf
 hw/xfree86/man/xorg.conf.man        |  6 ++++++
f34ecf
 randr/randrstr.h                    |  3 +++
f34ecf
 randr/rrprovider.c                  | 22 ++++++++++++++++++++++
f34ecf
 10 files changed, 85 insertions(+)
f34ecf
f34ecf
diff --git a/hw/xfree86/common/xf86Config.c b/hw/xfree86/common/xf86Config.c
f34ecf
index 2c1d335..d7d7c2e 100644
f34ecf
--- a/hw/xfree86/common/xf86Config.c
f34ecf
+++ b/hw/xfree86/common/xf86Config.c
f34ecf
@@ -643,6 +643,7 @@ typedef enum {
f34ecf
     FLAG_DRI2,
f34ecf
     FLAG_USE_SIGIO,
f34ecf
     FLAG_AUTO_ADD_GPU,
f34ecf
+    FLAG_AUTO_BIND_GPU,
f34ecf
     FLAG_MAX_CLIENTS,
f34ecf
     FLAG_IGLX,
f34ecf
     FLAG_DEBUG,
f34ecf
@@ -699,6 +700,8 @@ static OptionInfoRec FlagOptions[] = {
f34ecf
      {0}, FALSE},
f34ecf
     {FLAG_AUTO_ADD_GPU, "AutoAddGPU", OPTV_BOOLEAN,
f34ecf
      {0}, FALSE},
f34ecf
+    {FLAG_AUTO_BIND_GPU, "AutoBindGPU", OPTV_BOOLEAN,
f34ecf
+     {0}, FALSE},
f34ecf
     {FLAG_MAX_CLIENTS, "MaxClients", OPTV_INTEGER,
f34ecf
      {0}, FALSE },
f34ecf
     {FLAG_IGLX, "IndirectGLX", OPTV_BOOLEAN,
f34ecf
@@ -779,6 +782,22 @@ configServerFlags(XF86ConfFlagsPtr flagsconf, XF86OptionPtr layoutopts)
f34ecf
     }
f34ecf
     xf86Msg(from, "%sutomatically adding GPU devices\n",
f34ecf
             xf86Info.autoAddGPU ? "A" : "Not a");
f34ecf
+
f34ecf
+    if (xf86AutoBindGPUDisabled) {
f34ecf
+        xf86Info.autoBindGPU = FALSE;
f34ecf
+        from = X_CMDLINE;
f34ecf
+    }
f34ecf
+    else if (xf86IsOptionSet(FlagOptions, FLAG_AUTO_BIND_GPU)) {
f34ecf
+        xf86GetOptValBool(FlagOptions, FLAG_AUTO_BIND_GPU,
f34ecf
+                          &xf86Info.autoBindGPU);
f34ecf
+        from = X_CONFIG;
f34ecf
+    }
f34ecf
+    else {
f34ecf
+        from = X_DEFAULT;
f34ecf
+    }
f34ecf
+    xf86Msg(from, "%sutomatically binding GPU devices\n",
f34ecf
+            xf86Info.autoBindGPU ? "A" : "Not a");
f34ecf
+
f34ecf
     /*
f34ecf
      * Set things up based on the config file information.  Some of these
f34ecf
      * settings may be overridden later when the command line options are
f34ecf
diff --git a/hw/xfree86/common/xf86Globals.c b/hw/xfree86/common/xf86Globals.c
f34ecf
index e890f05..7b27b4c 100644
f34ecf
--- a/hw/xfree86/common/xf86Globals.c
f34ecf
+++ b/hw/xfree86/common/xf86Globals.c
f34ecf
@@ -131,6 +131,7 @@ xf86InfoRec xf86Info = {
f34ecf
 #else
f34ecf
     .autoAddGPU = FALSE,
f34ecf
 #endif
f34ecf
+    .autoBindGPU = TRUE,
f34ecf
 };
f34ecf
 
f34ecf
 const char *xf86ConfigFile = NULL;
f34ecf
@@ -191,6 +192,7 @@ Bool xf86FlipPixels = FALSE;
f34ecf
 Gamma xf86Gamma = { 0.0, 0.0, 0.0 };
f34ecf
 
f34ecf
 Bool xf86AllowMouseOpenFail = FALSE;
f34ecf
+Bool xf86AutoBindGPUDisabled = FALSE;
f34ecf
 
f34ecf
 #ifdef XF86VIDMODE
f34ecf
 Bool xf86VidModeDisabled = FALSE;
f34ecf
diff --git a/hw/xfree86/common/xf86Init.c b/hw/xfree86/common/xf86Init.c
f34ecf
index ea42ec9..ec255b6 100644
f34ecf
--- a/hw/xfree86/common/xf86Init.c
f34ecf
+++ b/hw/xfree86/common/xf86Init.c
f34ecf
@@ -76,6 +76,7 @@
f34ecf
 #include "xf86DDC.h"
f34ecf
 #include "xf86Xinput.h"
f34ecf
 #include "xf86InPriv.h"
f34ecf
+#include "xf86Crtc.h"
f34ecf
 #include "picturestr.h"
f34ecf
 #include "randrstr.h"
f34ecf
 #include "glxvndabi.h"
f34ecf
@@ -237,6 +238,19 @@ xf86PrivsElevated(void)
f34ecf
     return PrivsElevated();
f34ecf
 }
f34ecf
 
f34ecf
+static void
f34ecf
+xf86AutoConfigOutputDevices(void)
f34ecf
+{
f34ecf
+    int i;
f34ecf
+
f34ecf
+    if (!xf86Info.autoBindGPU)
f34ecf
+        return;
f34ecf
+
f34ecf
+    for (i = 0; i < xf86NumGPUScreens; i++)
f34ecf
+        RRProviderAutoConfigGpuScreen(xf86ScrnToScreen(xf86GPUScreens[i]),
f34ecf
+                                      xf86ScrnToScreen(xf86Screens[0]));
f34ecf
+}
f34ecf
+
f34ecf
 static void
f34ecf
 TrapSignals(void)
f34ecf
 {
f34ecf
@@ -770,6 +784,8 @@ InitOutput(ScreenInfo * pScreenInfo, int argc, char **argv)
f34ecf
     for (i = 0; i < xf86NumGPUScreens; i++)
f34ecf
         AttachUnboundGPU(xf86Screens[0]->pScreen, xf86GPUScreens[i]->pScreen);
f34ecf
 
f34ecf
+    xf86AutoConfigOutputDevices();
f34ecf
+
f34ecf
     xf86VGAarbiterWrapFunctions();
f34ecf
     if (sigio_blocked)
f34ecf
         input_unlock();
f34ecf
@@ -1278,6 +1294,10 @@ ddxProcessArgument(int argc, char **argv, int i)
f34ecf
         xf86Info.iglxFrom = X_CMDLINE;
f34ecf
         return 0;
f34ecf
     }
f34ecf
+    if (!strcmp(argv[i], "-noautoBindGPU")) {
f34ecf
+        xf86AutoBindGPUDisabled = TRUE;
f34ecf
+        return 1;
f34ecf
+    }
f34ecf
 
f34ecf
     /* OS-specific processing */
f34ecf
     return xf86ProcessArgument(argc, argv, i);
f34ecf
diff --git a/hw/xfree86/common/xf86Priv.h b/hw/xfree86/common/xf86Priv.h
f34ecf
index 4fe2b5f..6566622 100644
f34ecf
--- a/hw/xfree86/common/xf86Priv.h
f34ecf
+++ b/hw/xfree86/common/xf86Priv.h
f34ecf
@@ -46,6 +46,7 @@
f34ecf
 extern _X_EXPORT const char *xf86ConfigFile;
f34ecf
 extern _X_EXPORT const char *xf86ConfigDir;
f34ecf
 extern _X_EXPORT Bool xf86AllowMouseOpenFail;
f34ecf
+extern _X_EXPORT Bool xf86AutoBindGPUDisabled;
f34ecf
 
f34ecf
 #ifdef XF86VIDMODE
f34ecf
 extern _X_EXPORT Bool xf86VidModeDisabled;
f34ecf
diff --git a/hw/xfree86/common/xf86Privstr.h b/hw/xfree86/common/xf86Privstr.h
f34ecf
index 21c2e1f..6c71863 100644
f34ecf
--- a/hw/xfree86/common/xf86Privstr.h
f34ecf
+++ b/hw/xfree86/common/xf86Privstr.h
f34ecf
@@ -98,6 +98,7 @@ typedef struct {
f34ecf
 
f34ecf
     Bool autoAddGPU;
f34ecf
     const char *debug;
f34ecf
+    Bool autoBindGPU;
f34ecf
 } xf86InfoRec, *xf86InfoPtr;
f34ecf
 
f34ecf
 /* ISC's cc can't handle ~ of UL constants, so explicitly type cast them. */
f34ecf
diff --git a/hw/xfree86/common/xf86platformBus.c b/hw/xfree86/common/xf86platformBus.c
f34ecf
index cef47da..913a324 100644
f34ecf
--- a/hw/xfree86/common/xf86platformBus.c
f34ecf
+++ b/hw/xfree86/common/xf86platformBus.c
f34ecf
@@ -49,6 +49,7 @@
f34ecf
 #include "Pci.h"
f34ecf
 #include "xf86platformBus.h"
f34ecf
 #include "xf86Config.h"
f34ecf
+#include "xf86Crtc.h"
f34ecf
 
f34ecf
 #include "randrstr.h"
f34ecf
 int platformSlotClaimed;
f34ecf
@@ -665,6 +666,9 @@ xf86platformAddDevice(int index)
f34ecf
    }
f34ecf
    /* attach unbound to 0 protocol screen */
f34ecf
    AttachUnboundGPU(xf86Screens[0]->pScreen, xf86GPUScreens[i]->pScreen);
f34ecf
+   if (xf86Info.autoBindGPU)
f34ecf
+       RRProviderAutoConfigGpuScreen(xf86ScrnToScreen(xf86GPUScreens[i]),
f34ecf
+                                     xf86ScrnToScreen(xf86Screens[0]));
f34ecf
 
f34ecf
    RRResourcesChanged(xf86Screens[0]->pScreen);
f34ecf
    RRTellChanged(xf86Screens[0]->pScreen);
f34ecf
diff --git a/hw/xfree86/man/Xorg.man b/hw/xfree86/man/Xorg.man
f34ecf
index 13a9dc3..745f986 100644
f34ecf
--- a/hw/xfree86/man/Xorg.man
f34ecf
+++ b/hw/xfree86/man/Xorg.man
f34ecf
@@ -283,6 +283,13 @@ is a comma separated list of directories to search for
f34ecf
 server modules.  This option is only available when the server is run
f34ecf
 as root (i.e, with real-uid 0).
f34ecf
 .TP 8
f34ecf
+.B \-noautoBindGPU
f34ecf
+Disable automatically setting secondary GPUs up as output sinks and offload
f34ecf
+sources. This is equivalent to setting the
f34ecf
+.B AutoBindGPU
f34ecf
+xorg.conf(__filemansuffix__) file option. To
f34ecf
+.B false.
f34ecf
+.TP 8
f34ecf
 .B \-nosilk
f34ecf
 Disable Silken Mouse support.
f34ecf
 .TP 8
f34ecf
diff --git a/hw/xfree86/man/xorg.conf.man b/hw/xfree86/man/xorg.conf.man
f34ecf
index 9589262..8d51e06 100644
f34ecf
--- a/hw/xfree86/man/xorg.conf.man
f34ecf
+++ b/hw/xfree86/man/xorg.conf.man
f34ecf
@@ -672,6 +672,12 @@ Enabled by default.
f34ecf
 If this option is disabled, then no GPU devices will be added from the udev
f34ecf
 backend. Enabled by default. (May need to be disabled to setup Xinerama).
f34ecf
 .TP 7
f34ecf
+.BI "Option \*qAutoBindGPU\*q  \*q" boolean \*q
f34ecf
+If enabled then secondary GPUs will be automatically set up as output-sinks and
f34ecf
+offload-sources. Making e.g. laptop outputs connected only to the secondary
f34ecf
+GPU directly available for use without needing to run
f34ecf
+"xrandr --setprovideroutputsource". Enabled by default.
f34ecf
+.TP 7
f34ecf
 .BI "Option \*qLog\*q \*q" string \*q
f34ecf
 This option controls whether the log is flushed and/or synced to disk after
f34ecf
 each message.
f34ecf
diff --git a/randr/randrstr.h b/randr/randrstr.h
f34ecf
index f94174b..092d726 100644
f34ecf
--- a/randr/randrstr.h
f34ecf
+++ b/randr/randrstr.h
f34ecf
@@ -1039,6 +1039,9 @@ RRProviderLookup(XID id, RRProviderPtr *provider_p);
f34ecf
 extern _X_EXPORT void
f34ecf
 RRDeliverProviderEvent(ClientPtr client, WindowPtr pWin, RRProviderPtr provider);
f34ecf
 
f34ecf
+extern _X_EXPORT void
f34ecf
+RRProviderAutoConfigGpuScreen(ScreenPtr pScreen, ScreenPtr masterScreen);
f34ecf
+
f34ecf
 /* rrproviderproperty.c */
f34ecf
 
f34ecf
 extern _X_EXPORT void
f34ecf
diff --git a/randr/rrprovider.c b/randr/rrprovider.c
f34ecf
index e4bc2bf..e04c18f 100644
f34ecf
--- a/randr/rrprovider.c
f34ecf
+++ b/randr/rrprovider.c
f34ecf
@@ -485,3 +485,25 @@ RRDeliverProviderEvent(ClientPtr client, WindowPtr pWin, RRProviderPtr provider)
f34ecf
 
f34ecf
     WriteEventsToClient(client, 1, (xEvent *) &pe);
f34ecf
 }
f34ecf
+
f34ecf
+void
f34ecf
+RRProviderAutoConfigGpuScreen(ScreenPtr pScreen, ScreenPtr masterScreen)
f34ecf
+{
f34ecf
+    rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen);
f34ecf
+    rrScrPrivPtr masterPriv = rrGetScrPriv(masterScreen);
f34ecf
+    RRProviderPtr provider = pScrPriv->provider;
f34ecf
+    RRProviderPtr master_provider = masterPriv->provider;
f34ecf
+
f34ecf
+    if (!provider || !master_provider)
f34ecf
+        return;
f34ecf
+
f34ecf
+    if ((provider->capabilities & RR_Capability_SinkOutput) &&
f34ecf
+        (master_provider->capabilities & RR_Capability_SourceOutput)) {
f34ecf
+        pScrPriv->rrProviderSetOutputSource(pScreen, provider, master_provider);
f34ecf
+        RRInitPrimeSyncProps(pScreen);
f34ecf
+    }
f34ecf
+
f34ecf
+    if ((provider->capabilities & RR_Capability_SourceOffload) &&
f34ecf
+        (master_provider->capabilities & RR_Capability_SinkOffload))
f34ecf
+        pScrPriv->rrProviderSetOffloadSink(pScreen, provider, master_provider);
f34ecf
+}
f34ecf
-- 
f34ecf
2.16.2
f34ecf