aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/nouveau/nouveau_acpi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_acpi.c')
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_acpi.c105
1 files changed, 68 insertions, 37 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index db76b94e6e26..f2ad17aa33f0 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -45,6 +45,8 @@
45static struct nouveau_dsm_priv { 45static struct nouveau_dsm_priv {
46 bool dsm_detected; 46 bool dsm_detected;
47 bool optimus_detected; 47 bool optimus_detected;
48 bool optimus_flags_detected;
49 bool optimus_skip_dsm;
48 acpi_handle dhandle; 50 acpi_handle dhandle;
49 acpi_handle rom_handle; 51 acpi_handle rom_handle;
50} nouveau_dsm_priv; 52} nouveau_dsm_priv;
@@ -57,9 +59,6 @@ bool nouveau_is_v1_dsm(void) {
57 return nouveau_dsm_priv.dsm_detected; 59 return nouveau_dsm_priv.dsm_detected;
58} 60}
59 61
60#define NOUVEAU_DSM_HAS_MUX 0x1
61#define NOUVEAU_DSM_HAS_OPT 0x2
62
63#ifdef CONFIG_VGA_SWITCHEROO 62#ifdef CONFIG_VGA_SWITCHEROO
64static const char nouveau_dsm_muid[] = { 63static const char nouveau_dsm_muid[] = {
65 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 64 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
@@ -110,7 +109,7 @@ static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *
110 * requirements on the fourth parameter, so a private implementation 109 * requirements on the fourth parameter, so a private implementation
111 * instead of using acpi_check_dsm(). 110 * instead of using acpi_check_dsm().
112 */ 111 */
113static int nouveau_check_optimus_dsm(acpi_handle handle) 112static int nouveau_dsm_get_optimus_functions(acpi_handle handle)
114{ 113{
115 int result; 114 int result;
116 115
@@ -125,7 +124,9 @@ static int nouveau_check_optimus_dsm(acpi_handle handle)
125 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported. 124 * ACPI Spec v4 9.14.1: if bit 0 is zero, no function is supported.
126 * If the n-th bit is enabled, function n is supported 125 * If the n-th bit is enabled, function n is supported
127 */ 126 */
128 return result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS); 127 if (result & 1 && result & (1 << NOUVEAU_DSM_OPTIMUS_CAPS))
128 return result;
129 return 0;
129} 130}
130 131
131static int nouveau_dsm(acpi_handle handle, int func, int arg) 132static int nouveau_dsm(acpi_handle handle, int func, int arg)
@@ -212,26 +213,55 @@ static const struct vga_switcheroo_handler nouveau_dsm_handler = {
212 .get_client_id = nouveau_dsm_get_client_id, 213 .get_client_id = nouveau_dsm_get_client_id,
213}; 214};
214 215
215static int nouveau_dsm_pci_probe(struct pci_dev *pdev) 216/*
217 * Firmware supporting Windows 8 or later do not use _DSM to put the device into
218 * D3cold, they instead rely on disabling power resources on the parent.
219 */
220static bool nouveau_pr3_present(struct pci_dev *pdev)
221{
222 struct pci_dev *parent_pdev = pci_upstream_bridge(pdev);
223 struct acpi_device *parent_adev;
224
225 if (!parent_pdev)
226 return false;
227
228 parent_adev = ACPI_COMPANION(&parent_pdev->dev);
229 if (!parent_adev)
230 return false;
231
232 return acpi_has_method(parent_adev->handle, "_PR3");
233}
234
235static void nouveau_dsm_pci_probe(struct pci_dev *pdev, acpi_handle *dhandle_out,
236 bool *has_mux, bool *has_opt,
237 bool *has_opt_flags, bool *has_pr3)
216{ 238{
217 acpi_handle dhandle; 239 acpi_handle dhandle;
218 int retval = 0; 240 bool supports_mux;
241 int optimus_funcs;
219 242
220 dhandle = ACPI_HANDLE(&pdev->dev); 243 dhandle = ACPI_HANDLE(&pdev->dev);
221 if (!dhandle) 244 if (!dhandle)
222 return false; 245 return;
223 246
224 if (!acpi_has_method(dhandle, "_DSM")) 247 if (!acpi_has_method(dhandle, "_DSM"))
225 return false; 248 return;
249
250 supports_mux = acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102,
251 1 << NOUVEAU_DSM_POWER);
252 optimus_funcs = nouveau_dsm_get_optimus_functions(dhandle);
226 253
227 if (acpi_check_dsm(dhandle, nouveau_dsm_muid, 0x00000102, 254 /* Does not look like a Nvidia device. */
228 1 << NOUVEAU_DSM_POWER)) 255 if (!supports_mux && !optimus_funcs)
229 retval |= NOUVEAU_DSM_HAS_MUX; 256 return;
230 257
231 if (nouveau_check_optimus_dsm(dhandle)) 258 *dhandle_out = dhandle;
232 retval |= NOUVEAU_DSM_HAS_OPT; 259 *has_mux = supports_mux;
260 *has_opt = !!optimus_funcs;
261 *has_opt_flags = optimus_funcs & (1 << NOUVEAU_DSM_OPTIMUS_FLAGS);
262 *has_pr3 = false;
233 263
234 if (retval & NOUVEAU_DSM_HAS_OPT) { 264 if (optimus_funcs) {
235 uint32_t result; 265 uint32_t result;
236 nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0, 266 nouveau_optimus_dsm(dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 0,
237 &result); 267 &result);
@@ -239,11 +269,9 @@ static int nouveau_dsm_pci_probe(struct pci_dev *pdev)
239 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled", 269 (result & OPTIMUS_ENABLED) ? "enabled" : "disabled",
240 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "", 270 (result & OPTIMUS_DYNAMIC_PWR_CAP) ? "dynamic power, " : "",
241 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : ""); 271 (result & OPTIMUS_HDA_CODEC_MASK) ? "hda bios codec supported" : "");
242 }
243 if (retval)
244 nouveau_dsm_priv.dhandle = dhandle;
245 272
246 return retval; 273 *has_pr3 = nouveau_pr3_present(pdev);
274 }
247} 275}
248 276
249static bool nouveau_dsm_detect(void) 277static bool nouveau_dsm_detect(void)
@@ -251,11 +279,13 @@ static bool nouveau_dsm_detect(void)
251 char acpi_method_name[255] = { 0 }; 279 char acpi_method_name[255] = { 0 };
252 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 280 struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name};
253 struct pci_dev *pdev = NULL; 281 struct pci_dev *pdev = NULL;
254 int has_dsm = 0; 282 acpi_handle dhandle = NULL;
255 int has_optimus = 0; 283 bool has_mux = false;
284 bool has_optimus = false;
285 bool has_optimus_flags = false;
286 bool has_power_resources = false;
256 int vga_count = 0; 287 int vga_count = 0;
257 bool guid_valid; 288 bool guid_valid;
258 int retval;
259 bool ret = false; 289 bool ret = false;
260 290
261 /* lookup the MXM GUID */ 291 /* lookup the MXM GUID */
@@ -268,32 +298,32 @@ static bool nouveau_dsm_detect(void)
268 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 298 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) {
269 vga_count++; 299 vga_count++;
270 300
271 retval = nouveau_dsm_pci_probe(pdev); 301 nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
272 if (retval & NOUVEAU_DSM_HAS_MUX) 302 &has_optimus_flags, &has_power_resources);
273 has_dsm |= 1;
274 if (retval & NOUVEAU_DSM_HAS_OPT)
275 has_optimus = 1;
276 } 303 }
277 304
278 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) { 305 while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_3D << 8, pdev)) != NULL) {
279 vga_count++; 306 vga_count++;
280 307
281 retval = nouveau_dsm_pci_probe(pdev); 308 nouveau_dsm_pci_probe(pdev, &dhandle, &has_mux, &has_optimus,
282 if (retval & NOUVEAU_DSM_HAS_MUX) 309 &has_optimus_flags, &has_power_resources);
283 has_dsm |= 1;
284 if (retval & NOUVEAU_DSM_HAS_OPT)
285 has_optimus = 1;
286 } 310 }
287 311
288 /* find the optimus DSM or the old v1 DSM */ 312 /* find the optimus DSM or the old v1 DSM */
289 if (has_optimus == 1) { 313 if (has_optimus) {
314 nouveau_dsm_priv.dhandle = dhandle;
290 acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 315 acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
291 &buffer); 316 &buffer);
292 printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n", 317 printk(KERN_INFO "VGA switcheroo: detected Optimus DSM method %s handle\n",
293 acpi_method_name); 318 acpi_method_name);
319 if (has_power_resources)
320 pr_info("nouveau: detected PR support, will not use DSM\n");
294 nouveau_dsm_priv.optimus_detected = true; 321 nouveau_dsm_priv.optimus_detected = true;
322 nouveau_dsm_priv.optimus_flags_detected = has_optimus_flags;
323 nouveau_dsm_priv.optimus_skip_dsm = has_power_resources;
295 ret = true; 324 ret = true;
296 } else if (vga_count == 2 && has_dsm && guid_valid) { 325 } else if (vga_count == 2 && has_mux && guid_valid) {
326 nouveau_dsm_priv.dhandle = dhandle;
297 acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, 327 acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME,
298 &buffer); 328 &buffer);
299 printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 329 printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n",
@@ -321,11 +351,12 @@ void nouveau_register_dsm_handler(void)
321void nouveau_switcheroo_optimus_dsm(void) 351void nouveau_switcheroo_optimus_dsm(void)
322{ 352{
323 u32 result = 0; 353 u32 result = 0;
324 if (!nouveau_dsm_priv.optimus_detected) 354 if (!nouveau_dsm_priv.optimus_detected || nouveau_dsm_priv.optimus_skip_dsm)
325 return; 355 return;
326 356
327 nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS, 357 if (nouveau_dsm_priv.optimus_flags_detected)
328 0x3, &result); 358 nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_FLAGS,
359 0x3, &result);
329 360
330 nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS, 361 nouveau_optimus_dsm(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_OPTIMUS_CAPS,
331 NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result); 362 NOUVEAU_DSM_OPTIMUS_SET_POWERDOWN, &result);