Commit d099230cc355c485e556121c034b1fca5a5fd18b
Committed by
Dave Airlie
1 parent
9075e85f46
Exists in
master
and in
38 other branches
nouveau: Support Optimus models for vga_switcheroo
Newer nVidia cards with Optimus do not support/use the DSM switching functions. Instead, it require a DSM function to be called prior to bringing a device into D3 state. No other _DSM calls are necessary before/after enabling/disabling a device. Switching between discrete and integrated GPU is not supported by this Optimus _DSM call, therefore return on the switching method. Signed-off-by: Peter Lekensteyn <lekensteyn@gmail.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Showing 3 changed files with 42 additions and 5 deletions Side-by-side Diff
drivers/gpu/drm/nouveau/nouveau_acpi.c
... | ... | @@ -30,6 +30,8 @@ |
30 | 30 | #define NOUVEAU_DSM_POWER_STAMINA 0x02 |
31 | 31 | |
32 | 32 | #define NOUVEAU_DSM_OPTIMUS_FN 0x1A |
33 | +#define NOUVEAU_DSM_OPTIMUS_ARGS 0x03000001 | |
34 | + | |
33 | 35 | static struct nouveau_dsm_priv { |
34 | 36 | bool dsm_detected; |
35 | 37 | bool optimus_detected; |
... | ... | @@ -56,7 +58,8 @@ |
56 | 58 | struct acpi_object_list input; |
57 | 59 | union acpi_object params[4]; |
58 | 60 | union acpi_object *obj; |
59 | - int err; | |
61 | + int i, err; | |
62 | + char args_buff[4]; | |
60 | 63 | |
61 | 64 | input.count = 4; |
62 | 65 | input.pointer = params; |
... | ... | @@ -68,7 +71,11 @@ |
68 | 71 | params[2].type = ACPI_TYPE_INTEGER; |
69 | 72 | params[2].integer.value = func; |
70 | 73 | params[3].type = ACPI_TYPE_BUFFER; |
71 | - params[3].buffer.length = 0; | |
74 | + params[3].buffer.length = 4; | |
75 | + /* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */ | |
76 | + for (i = 0; i < 4; i++) | |
77 | + args_buff[i] = (arg >> i * 8) & 0xFF; | |
78 | + params[3].buffer.pointer = args_buff; | |
72 | 79 | |
73 | 80 | err = acpi_evaluate_object(handle, "_DSM", &input, &output); |
74 | 81 | if (err) { |
... | ... | @@ -180,6 +187,10 @@ |
180 | 187 | |
181 | 188 | static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) |
182 | 189 | { |
190 | + /* perhaps the _DSM functions are mutually exclusive, but prepare for | |
191 | + * the future */ | |
192 | + if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) | |
193 | + return 0; | |
183 | 194 | if (id == VGA_SWITCHEROO_IGD) |
184 | 195 | return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); |
185 | 196 | else |
... | ... | @@ -192,6 +203,11 @@ |
192 | 203 | if (id == VGA_SWITCHEROO_IGD) |
193 | 204 | return 0; |
194 | 205 | |
206 | + /* Optimus laptops have the card already disabled in | |
207 | + * nouveau_switcheroo_set_state */ | |
208 | + if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) | |
209 | + return 0; | |
210 | + | |
195 | 211 | return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); |
196 | 212 | } |
197 | 213 | |
198 | 214 | |
199 | 215 | |
200 | 216 | |
... | ... | @@ -278,15 +294,22 @@ |
278 | 294 | } |
279 | 295 | |
280 | 296 | if (vga_count == 2 && has_dsm && guid_valid) { |
281 | - acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); | |
297 | + acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, | |
298 | + &buffer); | |
282 | 299 | printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", |
283 | - acpi_method_name); | |
300 | + acpi_method_name); | |
284 | 301 | nouveau_dsm_priv.dsm_detected = true; |
285 | 302 | ret = true; |
286 | 303 | } |
287 | 304 | |
288 | - if (has_optimus == 1) | |
305 | + if (has_optimus == 1) { | |
306 | + acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, | |
307 | + &buffer); | |
308 | + printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", | |
309 | + acpi_method_name); | |
289 | 310 | nouveau_dsm_priv.optimus_detected = true; |
311 | + ret = true; | |
312 | + } | |
290 | 313 | |
291 | 314 | return ret; |
292 | 315 | } |
... | ... | @@ -300,6 +323,17 @@ |
300 | 323 | return; |
301 | 324 | |
302 | 325 | vga_switcheroo_register_handler(&nouveau_dsm_handler); |
326 | +} | |
327 | + | |
328 | +/* Must be called for Optimus models before the card can be turned off */ | |
329 | +void nouveau_switcheroo_optimus_dsm(void) | |
330 | +{ | |
331 | + u32 result = 0; | |
332 | + if (!nouveau_dsm_priv.optimus_detected) | |
333 | + return; | |
334 | + | |
335 | + nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FN, | |
336 | + NOUVEAU_DSM_OPTIMUS_ARGS, &result); | |
303 | 337 | } |
304 | 338 | |
305 | 339 | void nouveau_unregister_dsm_handler(void) |
drivers/gpu/drm/nouveau/nouveau_drv.h
... | ... | @@ -1055,12 +1055,14 @@ |
1055 | 1055 | #if defined(CONFIG_ACPI) |
1056 | 1056 | void nouveau_register_dsm_handler(void); |
1057 | 1057 | void nouveau_unregister_dsm_handler(void); |
1058 | +void nouveau_switcheroo_optimus_dsm(void); | |
1058 | 1059 | int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); |
1059 | 1060 | bool nouveau_acpi_rom_supported(struct pci_dev *pdev); |
1060 | 1061 | int nouveau_acpi_edid(struct drm_device *, struct drm_connector *); |
1061 | 1062 | #else |
1062 | 1063 | static inline void nouveau_register_dsm_handler(void) {} |
1063 | 1064 | static inline void nouveau_unregister_dsm_handler(void) {} |
1065 | +static inline void nouveau_switcheroo_optimus_dsm(void) {} | |
1064 | 1066 | static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; } |
1065 | 1067 | static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; } |
1066 | 1068 | static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return -EINVAL; } |
drivers/gpu/drm/nouveau/nouveau_state.c
... | ... | @@ -525,6 +525,7 @@ |
525 | 525 | printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); |
526 | 526 | dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; |
527 | 527 | drm_kms_helper_poll_disable(dev); |
528 | + nouveau_switcheroo_optimus_dsm(); | |
528 | 529 | nouveau_pci_suspend(pdev, pmm); |
529 | 530 | dev->switch_power_state = DRM_SWITCH_POWER_OFF; |
530 | 531 | } |