Commit d099230cc355c485e556121c034b1fca5a5fd18b

Authored by Peter Lekensteyn
Committed by Dave Airlie
1 parent 9075e85f46

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 }