diff options
author | Peter Lekensteyn <lekensteyn@gmail.com> | 2011-12-17 06:54:04 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2012-01-13 04:09:15 -0500 |
commit | d099230cc355c485e556121c034b1fca5a5fd18b (patch) | |
tree | b7bba1a92527e4cee4c95db21b2cb9f01ed40aa0 /drivers/gpu | |
parent | 9075e85f46c598e4dfc852b16b256a32e2fb396f (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.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_state.c | 1 |
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 | |||
33 | static struct nouveau_dsm_priv { | 35 | static 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 | ||
181 | static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) | 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 | 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 */ | ||
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); | ||
337 | } | ||
338 | |||
305 | void nouveau_unregister_dsm_handler(void) | 339 | void 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) |
1056 | void nouveau_register_dsm_handler(void); | 1056 | void nouveau_register_dsm_handler(void); |
1057 | void nouveau_unregister_dsm_handler(void); | 1057 | void nouveau_unregister_dsm_handler(void); |
1058 | void nouveau_switcheroo_optimus_dsm(void); | ||
1058 | int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); | 1059 | int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len); |
1059 | bool nouveau_acpi_rom_supported(struct pci_dev *pdev); | 1060 | bool nouveau_acpi_rom_supported(struct pci_dev *pdev); |
1060 | int nouveau_acpi_edid(struct drm_device *, struct drm_connector *); | 1061 | int nouveau_acpi_edid(struct drm_device *, struct drm_connector *); |
1061 | #else | 1062 | #else |
1062 | static inline void nouveau_register_dsm_handler(void) {} | 1063 | static inline void nouveau_register_dsm_handler(void) {} |
1063 | static inline void nouveau_unregister_dsm_handler(void) {} | 1064 | static inline void nouveau_unregister_dsm_handler(void) {} |
1065 | static inline void nouveau_switcheroo_optimus_dsm(void) {} | ||
1064 | static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; } | 1066 | static inline bool nouveau_acpi_rom_supported(struct pci_dev *pdev) { return false; } |
1065 | static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; } | 1067 | static inline int nouveau_acpi_get_bios_chunk(uint8_t *bios, int offset, int len) { return -EINVAL; } |
1066 | static inline int nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector) { return -EINVAL; } | 1068 | static 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 | } |