Initial G14 patch
parent
9169ff4023
commit
fad5d02f3d
@ -0,0 +1,335 @@
|
|||||||
|
diff --unified --recursive --text src.orig/bbswitch/bbswitch.c src/bbswitch/bbswitch.c
|
||||||
|
--- src.orig/bbswitch/bbswitch.c 2020-08-11 17:50:30.348599901 +0000
|
||||||
|
+++ src/bbswitch/bbswitch.c 2020-08-12 12:35:48.309972312 +0000
|
||||||
|
@@ -28,6 +28,7 @@
|
||||||
|
*/
|
||||||
|
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||||
|
|
||||||
|
+#include <linux/delay.h>
|
||||||
|
#include <linux/pci.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
@@ -35,9 +36,11 @@
|
||||||
|
#include <linux/suspend.h>
|
||||||
|
#include <linux/seq_file.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
+#include <linux/pm_domain.h>
|
||||||
|
#include <linux/proc_fs.h>
|
||||||
|
#include <linux/version.h>
|
||||||
|
|
||||||
|
+
|
||||||
|
#define BBSWITCH_VERSION "0.8"
|
||||||
|
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
@@ -91,6 +94,12 @@
|
||||||
|
static struct pci_dev *dis_dev;
|
||||||
|
static acpi_handle dis_handle;
|
||||||
|
|
||||||
|
+static char dis_dev_name[16];
|
||||||
|
+unsigned int vendor;
|
||||||
|
+unsigned int device;
|
||||||
|
+
|
||||||
|
+static struct dev_pm_domain pm_domain;
|
||||||
|
+
|
||||||
|
/* whether the card was off before suspend or not; on: 0, off: 1 */
|
||||||
|
static int dis_before_suspend_disabled;
|
||||||
|
|
||||||
|
@@ -184,6 +193,18 @@
|
||||||
|
return result & 1 && result & (1 << sfnc);
|
||||||
|
}
|
||||||
|
|
||||||
|
+static int handle_has_dsm_func(acpi_handle handle, const char muid[16], int revid, int sfnc) {
|
||||||
|
+ u32 result = 0;
|
||||||
|
+
|
||||||
|
+ // fail if the _DSM call failed
|
||||||
|
+ if (acpi_call_dsm(handle, muid, revid, 0, 0, &result))
|
||||||
|
+ return 0;
|
||||||
|
+
|
||||||
|
+ // ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. If
|
||||||
|
+ // the n-th bit is enabled, function n is supported
|
||||||
|
+ return result & 1 && result & (1 << sfnc);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int bbswitch_optimus_dsm(void) {
|
||||||
|
if (dsm_type == DSM_TYPE_OPTIMUS) {
|
||||||
|
char args[] = {1, 0, 0, 3};
|
||||||
|
@@ -200,86 +221,61 @@
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bbswitch_acpi_off(void) {
|
||||||
|
- if (dsm_type == DSM_TYPE_NVIDIA) {
|
||||||
|
- char args[] = {2, 0, 0, 0};
|
||||||
|
- u32 result = 0;
|
||||||
|
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
|
||||||
|
- if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args,
|
||||||
|
- &result)) {
|
||||||
|
- // failure
|
||||||
|
- return 1;
|
||||||
|
- }
|
||||||
|
- pr_debug("Result of _DSM call for OFF: %08X\n", result);
|
||||||
|
- }
|
||||||
|
+ acpi_status err = (acpi_status) 0x0;
|
||||||
|
+ acpi_handle hnd;
|
||||||
|
+ err = acpi_get_handle(NULL, (acpi_string) "\\_SB.PCI0.GPP0.PG00", &hnd);
|
||||||
|
+ err = acpi_evaluate_object(hnd,"_OFF", NULL, &buffer);
|
||||||
|
+ kfree(buffer.pointer);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int bbswitch_acpi_on(void) {
|
||||||
|
- if (dsm_type == DSM_TYPE_NVIDIA) {
|
||||||
|
- char args[] = {1, 0, 0, 0};
|
||||||
|
- u32 result = 0;
|
||||||
|
+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||||
|
+
|
||||||
|
+ acpi_status err = (acpi_status) 0x0000;
|
||||||
|
+ acpi_handle hnd;
|
||||||
|
+ err = acpi_get_handle(NULL, (acpi_string) "\\_SB.PCI0.GPP0.PG00", &hnd);
|
||||||
|
+ err = acpi_evaluate_object(hnd,"_ON", NULL, &buffer);
|
||||||
|
+ kfree(buffer.pointer);
|
||||||
|
|
||||||
|
- if (acpi_call_dsm(dis_handle, acpi_nvidia_dsm_muid, 0x102, 0x3, args,
|
||||||
|
- &result)) {
|
||||||
|
- // failure
|
||||||
|
- return 1;
|
||||||
|
- }
|
||||||
|
- pr_debug("Result of _DSM call for ON: %08X\n", result);
|
||||||
|
- }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns 1 if the card is disabled, 0 if enabled
|
||||||
|
+
|
||||||
|
+// NOTE: With a fully disabling PCI device(disappears from 'lspci'),
|
||||||
|
+// you must check that this is '0' anytime you're wanting to interact with dis_dev.
|
||||||
|
+// Otherwise, you will segfault.
|
||||||
|
static int is_card_disabled(void) {
|
||||||
|
- u32 cfg_word;
|
||||||
|
- // read first config word which contains Vendor and Device ID. If all bits
|
||||||
|
- // are enabled, the device is assumed to be off
|
||||||
|
- pci_read_config_dword(dis_dev, 0, &cfg_word);
|
||||||
|
- // if one of the bits is not enabled (the card is enabled), the inverted
|
||||||
|
- // result will be non-zero and hence logical not will make it 0 ("false")
|
||||||
|
- return !~cfg_word;
|
||||||
|
+ // MAYBE: use the SGST power ACPI call here instead? (returns 0x0 if powered off)
|
||||||
|
+ struct pci_dev *pdev = NULL;
|
||||||
|
+
|
||||||
|
+ while ((pdev = pci_get_device(vendor, device, pdev)) != NULL) {
|
||||||
|
+ dis_dev = pdev;
|
||||||
|
+ return 0;
|
||||||
|
+ }
|
||||||
|
+ return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bbswitch_off(void) {
|
||||||
|
- if (is_card_disabled())
|
||||||
|
+ if (is_card_disabled()){
|
||||||
|
+ pr_info("discrete graphics already disabled");
|
||||||
|
return;
|
||||||
|
-
|
||||||
|
- // to prevent the system from possibly locking up, don't disable the device
|
||||||
|
- // if it's still in use by a driver (i.e. nouveau or nvidia)
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (dis_dev->driver) {
|
||||||
|
pr_warn("device %s is in use by driver '%s', refusing OFF\n",
|
||||||
|
- dev_name(&dis_dev->dev), dis_dev->driver->name);
|
||||||
|
+ dis_dev_name, dis_dev->driver->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
-
|
||||||
|
+
|
||||||
|
pr_info("disabling discrete graphics\n");
|
||||||
|
|
||||||
|
- if (bbswitch_optimus_dsm()) {
|
||||||
|
- pr_warn("Optimus ACPI call failed, the device is not disabled\n");
|
||||||
|
- return;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- pci_save_state(dis_dev);
|
||||||
|
- pci_clear_master(dis_dev);
|
||||||
|
- pci_disable_device(dis_dev);
|
||||||
|
- do {
|
||||||
|
- struct acpi_device *ad = NULL;
|
||||||
|
- int r;
|
||||||
|
-
|
||||||
|
- r = acpi_bus_get_device(dis_handle, &ad);
|
||||||
|
- if (r || !ad) {
|
||||||
|
- pr_warn("Cannot get ACPI device for PCI device\n");
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
- if (ad->power.state == ACPI_STATE_UNKNOWN) {
|
||||||
|
- pr_debug("ACPI power state is unknown, forcing D0\n");
|
||||||
|
- ad->power.state = ACPI_STATE_D0;
|
||||||
|
- }
|
||||||
|
- } while (0);
|
||||||
|
- pci_set_power_state(dis_dev, PCI_D3cold);
|
||||||
|
-
|
||||||
|
if (bbswitch_acpi_off())
|
||||||
|
- pr_warn("The discrete card could not be disabled by a _DSM call\n");
|
||||||
|
+ pr_warn("The discrete card could not be disabled by an _OFF call\n");
|
||||||
|
+ dis_dev = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bbswitch_on(void) {
|
||||||
|
@@ -289,22 +285,38 @@
|
||||||
|
pr_info("enabling discrete graphics\n");
|
||||||
|
|
||||||
|
if (bbswitch_acpi_on())
|
||||||
|
- pr_warn("The discrete card could not be enabled by a _DSM call\n");
|
||||||
|
-
|
||||||
|
- pci_set_power_state(dis_dev, PCI_D0);
|
||||||
|
- pci_restore_state(dis_dev);
|
||||||
|
- if (pci_enable_device(dis_dev))
|
||||||
|
- pr_warn("failed to enable %s\n", dev_name(&dis_dev->dev));
|
||||||
|
- pci_set_master(dis_dev);
|
||||||
|
+ pr_warn("The discrete card could not be enabled by an _ON call\n");
|
||||||
|
+
|
||||||
|
+ int i = 0;
|
||||||
|
+ while(is_card_disabled()){
|
||||||
|
+ i++;
|
||||||
|
+ msleep(500);
|
||||||
|
+ if(i > 4){
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* power bus so we can read PCI configuration space */
|
||||||
|
static void dis_dev_get(void) {
|
||||||
|
- if (dis_dev->bus && dis_dev->bus->self)
|
||||||
|
- pm_runtime_get_sync(&dis_dev->bus->self->dev);
|
||||||
|
+ if(dis_dev == NULL || is_card_disabled()){
|
||||||
|
+ struct pci_dev *pdev = NULL;
|
||||||
|
+ while ((pdev = pci_get_device(vendor, device, pdev)) != NULL){
|
||||||
|
+ dis_dev = pdev;
|
||||||
|
+ if (dis_dev->bus && dis_dev->bus->self)
|
||||||
|
+ pm_runtime_get_sync(&dis_dev->bus->self->dev);
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }else{
|
||||||
|
+ if (dis_dev->bus && dis_dev->bus->self)
|
||||||
|
+ pm_runtime_get_sync(&dis_dev->bus->self->dev);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
|
||||||
|
static void dis_dev_put(void) {
|
||||||
|
+ if(dis_dev == NULL || is_card_disabled()){
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
if (dis_dev->bus && dis_dev->bus->self)
|
||||||
|
pm_runtime_put_sync(&dis_dev->bus->self->dev);
|
||||||
|
}
|
||||||
|
@@ -335,7 +347,7 @@
|
||||||
|
static int bbswitch_proc_show(struct seq_file *seqfp, void *p) {
|
||||||
|
// show the card state. Example output: 0000:01:00:00 ON
|
||||||
|
dis_dev_get();
|
||||||
|
- seq_printf(seqfp, "%s %s\n", dev_name(&dis_dev->dev),
|
||||||
|
+ seq_printf(seqfp, "%s %s\n", dis_dev_name,
|
||||||
|
is_card_disabled() ? "OFF" : "ON");
|
||||||
|
dis_dev_put();
|
||||||
|
return 0;
|
||||||
|
@@ -349,20 +361,24 @@
|
||||||
|
switch (event_type) {
|
||||||
|
case PM_HIBERNATION_PREPARE:
|
||||||
|
case PM_SUSPEND_PREPARE:
|
||||||
|
+ pr_debug("Detected suspend");
|
||||||
|
dis_dev_get();
|
||||||
|
dis_before_suspend_disabled = is_card_disabled();
|
||||||
|
// enable the device before suspend to avoid the PCI config space from
|
||||||
|
// being saved incorrectly
|
||||||
|
if (dis_before_suspend_disabled)
|
||||||
|
+ pr_info("Enabling GPU for suspend");
|
||||||
|
bbswitch_on();
|
||||||
|
dis_dev_put();
|
||||||
|
break;
|
||||||
|
case PM_POST_HIBERNATION:
|
||||||
|
case PM_POST_SUSPEND:
|
||||||
|
case PM_POST_RESTORE:
|
||||||
|
+ pr_debug("Detected restore");
|
||||||
|
// after suspend, the card is on, but if it was off before suspend,
|
||||||
|
// disable it again
|
||||||
|
if (dis_before_suspend_disabled) {
|
||||||
|
+ pr_info("Restoring GPU to off");
|
||||||
|
dis_dev_get();
|
||||||
|
bbswitch_off();
|
||||||
|
dis_dev_put();
|
||||||
|
@@ -415,13 +431,8 @@
|
||||||
|
pci_class != PCI_CLASS_DISPLAY_3D)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
-#ifdef ACPI_HANDLE
|
||||||
|
- /* since Linux 3.8 */
|
||||||
|
handle = ACPI_HANDLE(&pdev->dev);
|
||||||
|
-#else
|
||||||
|
- /* removed since Linux 3.13 */
|
||||||
|
- handle = DEVICE_ACPI_HANDLE(&pdev->dev);
|
||||||
|
-#endif
|
||||||
|
+
|
||||||
|
if (!handle) {
|
||||||
|
pr_warn("cannot find ACPI handle for VGA device %s\n",
|
||||||
|
dev_name(&pdev->dev));
|
||||||
|
@@ -435,10 +446,19 @@
|
||||||
|
pr_info("Found integrated VGA device %s: %s\n",
|
||||||
|
dev_name(&pdev->dev), (char *)buf.pointer);
|
||||||
|
} else {
|
||||||
|
- dis_dev = pdev;
|
||||||
|
- dis_handle = handle;
|
||||||
|
- pr_info("Found discrete VGA device %s: %s\n",
|
||||||
|
- dev_name(&pdev->dev), (char *)buf.pointer);
|
||||||
|
+ if(handle && handle_has_dsm_func(handle,acpi_optimus_dsm_muid, 0x100, 0x1A)){
|
||||||
|
+ dis_dev = pdev;
|
||||||
|
+ strlcpy(dis_dev_name, dev_name(&pdev->dev), sizeof(dis_dev_name));
|
||||||
|
+ dis_handle = handle;
|
||||||
|
+ vendor = pdev->vendor;
|
||||||
|
+ device = pdev->device;
|
||||||
|
+ pr_info("Found discrete VGA device %s: %s\n",
|
||||||
|
+ dis_dev_name, (char *)buf.pointer);
|
||||||
|
+ }else{
|
||||||
|
+ igd_handle = handle;
|
||||||
|
+ pr_info("Found non-intel integrated VGA device %s: %s\n",
|
||||||
|
+ dev_name(&pdev->dev), (char *)buf.pointer);
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
kfree(buf.pointer);
|
||||||
|
}
|
||||||
|
@@ -463,6 +483,7 @@
|
||||||
|
dsm_type = DSM_TYPE_NVIDIA;
|
||||||
|
pr_info("detected a nVidia _DSM function on the"
|
||||||
|
" integrated video card\n");
|
||||||
|
+
|
||||||
|
} else {
|
||||||
|
pr_err("No suitable _DSM call found.\n");
|
||||||
|
return -ENODEV;
|
||||||
|
@@ -480,16 +501,17 @@
|
||||||
|
if (!is_card_disabled()) {
|
||||||
|
/* We think the card is enabled, so ensure the kernel does as well */
|
||||||
|
if (pci_enable_device(dis_dev))
|
||||||
|
- pr_warn("failed to enable %s\n", dev_name(&dis_dev->dev));
|
||||||
|
+ pr_warn("failed to enable %s\n", dis_dev_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (load_state == CARD_ON)
|
||||||
|
+ if (load_state == CARD_ON){
|
||||||
|
bbswitch_on();
|
||||||
|
- else if (load_state == CARD_OFF)
|
||||||
|
+ } else if (load_state == CARD_OFF){
|
||||||
|
bbswitch_off();
|
||||||
|
+ }
|
||||||
|
|
||||||
|
pr_info("Succesfully loaded. Discrete card %s is %s\n",
|
||||||
|
- dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
|
||||||
|
+ dis_dev_name, is_card_disabled() ? "off" : "on");
|
||||||
|
|
||||||
|
dis_dev_put();
|
||||||
|
|
||||||
|
@@ -509,7 +531,7 @@
|
||||||
|
bbswitch_off();
|
||||||
|
|
||||||
|
pr_info("Unloaded. Discrete card %s is %s\n",
|
||||||
|
- dev_name(&dis_dev->dev), is_card_disabled() ? "off" : "on");
|
||||||
|
+ dis_dev_name, is_card_disabled() ? "off" : "on");
|
||||||
|
|
||||||
|
dis_dev_put();
|
||||||
|
|
Loading…
Reference in New Issue