summarylogtreecommitdiffstats
path: root/bbswitch-dkms-git-zephyrus14.patch
blob: 83c4d58b59b80864394fc008cd9f905cb56969dd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
--- a/bbswitch.c	2020-09-04 21:07:14.085307177 +0200
+++ b/bbswitch.c	2020-09-04 21:07:36.584532874 +0200
@@ -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};
@@ -199,114 +220,130 @@
     return 0;
 }
 
-static int bbswitch_acpi_off(void) {
-    if (dsm_type == DSM_TYPE_NVIDIA) {
-        char args[] = {2, 0, 0, 0};
-        u32 result = 0;
+static void get_dis_dev(void){
+    struct pci_dev *pdev = 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);
+    if ((pdev = pci_get_device(vendor, device, pdev)) != NULL) {
+        dis_dev = pdev;
     }
+}
+
+static int bbswitch_acpi_off(void) {
+	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+    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;
+    struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+
+    acpi_status err = (acpi_status) 0x0000;
+    acpi_handle hnd;
+
+    acpi_get_handle(NULL, (acpi_string) "\\_SB.PCI0.GPP0.PEGP", &hnd);
+    err = acpi_evaluate_object(hnd,"SGST", NULL, &buffer);
+    int gpustatus = ((union acpi_object *)buffer.pointer)->integer.value > 0 ? 0 : 1;
+    if(gpustatus == 0){
+        get_dis_dev();
+        if(dis_dev == NULL){
+            // Card is still powering on.
+            gpustatus = -1;
+        }
+    }
+
+    kfree(buffer.pointer);
+
+    return gpustatus;
 }
 
 static void bbswitch_off(void) {
-    if (is_card_disabled())
+    if (is_card_disabled() == 1){
+        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) {
-    if (!is_card_disabled())
+    if (is_card_disabled() < 1)
         return;
 
     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(dis_dev == NULL){
+        msleep(500);
+        i++;
+        get_dis_dev();
+        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(is_card_disabled() < 1){
+        int i = 0;
+        while(dis_dev == NULL){
+            msleep(500);
+            i++;
+            get_dis_dev();
+            if(i > 4){
+                if(dis_dev != NULL){
+                    break;
+                }
+                else{
+                    return;
+                }
+            }
+        }
+        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->bus && dis_dev->bus->self)
-        pm_runtime_put_sync(&dis_dev->bus->self->dev);
+    if(is_card_disabled() == 0){
+        if (dis_dev->bus && dis_dev->bus->self)
+            pm_runtime_put_sync(&dis_dev->bus->self->dev);
+    }
 }
 
 static ssize_t bbswitch_proc_write(struct file *fp, const char __user *buff,
@@ -335,8 +372,8 @@
 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),
-             is_card_disabled() ? "OFF" : "ON");
+    seq_printf(seqfp, "%s %s\n", dis_dev_name,
+             is_card_disabled() > 0 ? "OFF" : "ON");
     dis_dev_put();
     return 0;
 }
@@ -349,20 +386,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 +456,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 +471,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 +508,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;
@@ -477,19 +523,20 @@
 
     dis_dev_get();
 
-    if (!is_card_disabled()) {
+    if (is_card_disabled() < 1) {
         /* 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() > 0 ? "off" : "on");
 
     dis_dev_put();
 
@@ -509,7 +556,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() > 0 ? "off" : "on");
 
     dis_dev_put();