Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Peter Lekensteyn and committed by
Dave Airlie
d099230c 9075e85f

+42 -5
+39 -5
drivers/gpu/drm/nouveau/nouveau_acpi.c
··· 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; ··· 58 56 struct acpi_object_list input; 59 57 union acpi_object params[4]; 60 58 union acpi_object *obj; 61 - int err; 59 + int i, err; 60 + char args_buff[4]; 62 61 63 62 input.count = 4; 64 63 input.pointer = params; ··· 71 68 params[2].type = ACPI_TYPE_INTEGER; 72 69 params[2].integer.value = func; 73 70 params[3].type = ACPI_TYPE_BUFFER; 74 - params[3].buffer.length = 0; 71 + params[3].buffer.length = 4; 72 + /* ACPI is little endian, AABBCCDD becomes {DD,CC,BB,AA} */ 73 + for (i = 0; i < 4; i++) 74 + args_buff[i] = (arg >> i * 8) & 0xFF; 75 + params[3].buffer.pointer = args_buff; 75 76 76 77 err = acpi_evaluate_object(handle, "_DSM", &input, &output); 77 78 if (err) { ··· 187 180 188 181 static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 189 182 { 183 + /* perhaps the _DSM functions are mutually exclusive, but prepare for 184 + * the future */ 185 + if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) 186 + return 0; 190 187 if (id == VGA_SWITCHEROO_IGD) 191 188 return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 192 189 else ··· 201 190 enum vga_switcheroo_state state) 202 191 { 203 192 if (id == VGA_SWITCHEROO_IGD) 193 + return 0; 194 + 195 + /* Optimus laptops have the card already disabled in 196 + * nouveau_switcheroo_set_state */ 197 + if (!nouveau_dsm_priv.dsm_detected && nouveau_dsm_priv.optimus_detected) 204 198 return 0; 205 199 206 200 return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); ··· 294 278 } 295 279 296 280 if (vga_count == 2 && has_dsm && guid_valid) { 297 - acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); 281 + acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 282 + &buffer); 298 283 printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 299 - acpi_method_name); 284 + acpi_method_name); 300 285 nouveau_dsm_priv.dsm_detected = true; 301 286 ret = true; 302 287 } 303 288 304 - if (has_optimus == 1) 289 + if (has_optimus == 1) { 290 + acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 291 + &buffer); 292 + printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", 293 + acpi_method_name); 305 294 nouveau_dsm_priv.optimus_detected = true; 295 + ret = true; 296 + } 306 297 307 298 return ret; 308 299 } ··· 323 300 return; 324 301 325 302 vga_switcheroo_register_handler(&nouveau_dsm_handler); 303 + } 304 + 305 + /* Must be called for Optimus models before the card can be turned off */ 306 + void nouveau_switcheroo_optimus_dsm(void) 307 + { 308 + u32 result = 0; 309 + if (!nouveau_dsm_priv.optimus_detected) 310 + return; 311 + 312 + nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FN, 313 + NOUVEAU_DSM_OPTIMUS_ARGS, &result); 326 314 } 327 315 328 316 void nouveau_unregister_dsm_handler(void)
+2
drivers/gpu/drm/nouveau/nouveau_drv.h
··· 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; }
+1
drivers/gpu/drm/nouveau/nouveau_state.c
··· 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 }