aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu
diff options
context:
space:
mode:
authorPeter Lekensteyn <lekensteyn@gmail.com>2011-12-17 06:54:04 -0500
committerDave Airlie <airlied@redhat.com>2012-01-13 04:09:15 -0500
commitd099230cc355c485e556121c034b1fca5a5fd18b (patch)
treeb7bba1a92527e4cee4c95db21b2cb9f01ed40aa0 /drivers/gpu
parent9075e85f46c598e4dfc852b16b256a32e2fb396f (diff)
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>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c44
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_drv.h2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_state.c1
3 files changed, 42 insertions, 5 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 96756d0d6411..7814a760c164 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -30,6 +30,8 @@
30#define NOUVEAU_DSM_POWER_STAMINA 0x02 30#define NOUVEAU_DSM_POWER_STAMINA 0x02
31 31
32#define NOUVEAU_DSM_OPTIMUS_FN 0x1A 32#define NOUVEAU_DSM_OPTIMUS_FN 0x1A
33#define NOUVEAU_DSM_OPTIMUS_ARGS 0x03000001
34
33static struct nouveau_dsm_priv { 35static struct nouveau_dsm_priv {
34 bool dsm_detected; 36 bool dsm_detected;
35 bool optimus_detected; 37 bool optimus_detected;
@@ -56,7 +58,8 @@ static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *
56 struct acpi_object_list input; 58 struct acpi_object_list input;
57 union acpi_object params[4]; 59 union acpi_object params[4];
58 union acpi_object *obj; 60 union acpi_object *obj;
59 int err; 61 int i, err;
62 char args_buff[4];
60 63
61 input.count = 4; 64 input.count = 4;
62 input.pointer = params; 65 input.pointer = params;
@@ -68,7 +71,11 @@ static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *
68 params[2].type = ACPI_TYPE_INTEGER; 71 params[2].type = ACPI_TYPE_INTEGER;
69 params[2].integer.value = func; 72 params[2].integer.value = func;
70 params[3].type = ACPI_TYPE_BUFFER; 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 err = acpi_evaluate_object(handle, "_DSM", &input, &output); 80 err = acpi_evaluate_object(handle, "_DSM", &input, &output);
74 if (err) { 81 if (err) {
@@ -180,6 +187,10 @@ static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switchero
180 187
181static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 188static 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 if (id == VGA_SWITCHEROO_IGD) 194 if (id == VGA_SWITCHEROO_IGD)
184 return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA); 195 return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_STAMINA);
185 else 196 else
@@ -192,6 +203,11 @@ static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id,
192 if (id == VGA_SWITCHEROO_IGD) 203 if (id == VGA_SWITCHEROO_IGD)
193 return 0; 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 return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); 211 return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state);
196} 212}
197 213
@@ -278,15 +294,22 @@ static bool nouveau_dsm_detect(void)
278 } 294 }
279 295
280 if (vga_count == 2 && has_dsm && guid_valid) { 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 printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 299 printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
283 acpi_method_name); 300 acpi_method_name);
284 nouveau_dsm_priv.dsm_detected = true; 301 nouveau_dsm_priv.dsm_detected = true;
285 ret = true; 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 nouveau_dsm_priv.optimus_detected = true; 310 nouveau_dsm_priv.optimus_detected = true;
311 ret = true;
312 }
290 313
291 return ret; 314 return ret;
292} 315}
@@ -302,6 +325,17 @@ void nouveau_register_dsm_handler(void)
302 vga_switcheroo_register_handler(&nouveau_dsm_handler); 325 vga_switcheroo_register_handler(&nouveau_dsm_handler);
303} 326}
304 327
328/* Must be called for Optimus models before the card can be turned off */
329void 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);
337}
338
305void nouveau_unregister_dsm_handler(void) 339void nouveau_unregister_dsm_handler(void)
306{ 340{
307 vga_switcheroo_unregister_handler(); 341 vga_switcheroo_unregister_handler();
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
index 38134a9c7578..b82709828931 100644
--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
+++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
@@ -1055,12 +1055,14 @@ extern int nouveau_dma_wait(struct nouveau_channel *, int slots, int size);
1055#if defined(CONFIG_ACPI) 1055#if defined(CONFIG_ACPI)
1056void nouveau_register_dsm_handler(void); 1056void nouveau_register_dsm_handler(void);
1057void nouveau_unregister_dsm_handler(void); 1057void nouveau_unregister_dsm_handler(void);
1058void nouveau_switcheroo_optimus_dsm(void);
1058int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); 1059int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len);
1059bool nouveau_acpi_rom_supported(struct pci_dev *pdev); 1060bool nouveau_acpi_rom_supported(struct pci_dev *pdev);
1060int nouveau_acpi_edid(struct drm_device *, struct drm_connector *); 1061int nouveau_acpi_edid(struct drm_device *, struct drm_connector *);
1061#else 1062#else
1062static inline void nouveau_register_dsm_handler(void) {} 1063static inline void nouveau_register_dsm_handler(void) {}
1063static inline void nouveau_unregister_dsm_handler(void) {} 1064static inline void nouveau_unregister_dsm_handler(void) {}
1065static inline void nouveau_switcheroo_optimus_dsm(void) {}
1064static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; } 1066static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; }
1065static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; } 1067static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; }
1066static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return -EINVAL; } 1068static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return -EINVAL; }
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
index f5e98910d17f..f80c5e0762ff 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -525,6 +525,7 @@ static void nouveau_switcheroo_set_state(struct pci_dev *pdev,
525 printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); 525 printk(KERN_ERR "VGA switcheroo: switched nouveau off\n");
526 dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; 526 dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
527 drm_kms_helper_poll_disable(dev); 527 drm_kms_helper_poll_disable(dev);
528 nouveau_switcheroo_optimus_dsm();
528 nouveau_pci_suspend(pdev, pmm); 529 nouveau_pci_suspend(pdev, pmm);
529 dev->switch_power_state = DRM_SWITCH_POWER_OFF; 530 dev->switch_power_state = DRM_SWITCH_POWER_OFF;
530 } 531 }