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

gpu/vga_switcheroo: add driver control power feature. (v3)

For optimus and powerxpress muxless we really want the GPU
driver deciding when to power up/down the GPU, not userspace.

This adds the ability for a driver to dynamically power up/down
the GPU and remove the switcheroo from controlling it, the
switcheroo reports the dynamic state to userspace also.

It also adds 2 power domains, one for machine where the power
switch is controlled outside the GPU D3 state, so the powerdown
ordering is done correctly, and the second for the hdmi audio
device to make sure it can resume for PCI config space accesses.

v1.1: fix build with switcheroo off

v2: add power domain support for radeon and v1 nvidia dsms
v2.1: fix typo in off case

v3: add audio power domain for hdmi audio + misc audio fixes

v4: use PCI_SLOT macro, drop power reference on hdmi audio resume
failure also.

Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Dave Airlie and committed by
Dave Airlie
0d69704a e906d7bd

+156 -10
+1 -1
drivers/gpu/drm/i915/i915_dma.c
··· 1293 1293 1294 1294 intel_register_dsm_handler(); 1295 1295 1296 - ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops); 1296 + ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops, false); 1297 1297 if (ret) 1298 1298 goto cleanup_vga_client; 1299 1299
+1 -1
drivers/gpu/drm/nouveau/nouveau_vga.c
··· 79 79 { 80 80 struct drm_device *dev = drm->dev; 81 81 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 82 - vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops); 82 + vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, false); 83 83 } 84 84 85 85 void
+1 -1
drivers/gpu/drm/radeon/radeon_device.c
··· 1269 1269 /* this will fail for cards that aren't VGA class devices, just 1270 1270 * ignore it */ 1271 1271 vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); 1272 - vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops); 1272 + vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, false); 1273 1273 1274 1274 r = radeon_init(rdev); 1275 1275 if (r)
+142 -5
drivers/gpu/vga/vga_switcheroo.c
··· 27 27 #include <linux/pci.h> 28 28 #include <linux/console.h> 29 29 #include <linux/vga_switcheroo.h> 30 + #include <linux/pm_runtime.h> 30 31 31 32 #include <linux/vgaarb.h> 32 33 ··· 38 37 const struct vga_switcheroo_client_ops *ops; 39 38 int id; 40 39 bool active; 40 + bool driver_power_control; 41 41 struct list_head list; 42 42 }; 43 43 ··· 134 132 135 133 static int register_client(struct pci_dev *pdev, 136 134 const struct vga_switcheroo_client_ops *ops, 137 - int id, bool active) 135 + int id, bool active, bool driver_power_control) 138 136 { 139 137 struct vga_switcheroo_client *client; 140 138 ··· 147 145 client->ops = ops; 148 146 client->id = id; 149 147 client->active = active; 148 + client->driver_power_control = driver_power_control; 150 149 151 150 mutex_lock(&vgasr_mutex); 152 151 list_add_tail(&client->list, &vgasr_priv.clients); ··· 163 160 } 164 161 165 162 int vga_switcheroo_register_client(struct pci_dev *pdev, 166 - const struct vga_switcheroo_client_ops *ops) 163 + const struct vga_switcheroo_client_ops *ops, 164 + bool driver_power_control) 167 165 { 168 166 return register_client(pdev, ops, -1, 169 - pdev == vga_default_device()); 167 + pdev == vga_default_device(), driver_power_control); 170 168 } 171 169 EXPORT_SYMBOL(vga_switcheroo_register_client); 172 170 ··· 175 171 const struct vga_switcheroo_client_ops *ops, 176 172 int id, bool active) 177 173 { 178 - return register_client(pdev, ops, id | ID_BIT_AUDIO, active); 174 + return register_client(pdev, ops, id | ID_BIT_AUDIO, active, false); 179 175 } 180 176 EXPORT_SYMBOL(vga_switcheroo_register_audio_client); 181 177 ··· 262 258 int i = 0; 263 259 mutex_lock(&vgasr_mutex); 264 260 list_for_each_entry(client, &vgasr_priv.clients, list) { 265 - seq_printf(m, "%d:%s%s:%c:%s:%s\n", i, 261 + seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i, 266 262 client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", 267 263 client_is_vga(client) ? "" : "-Audio", 268 264 client->active ? '+' : ' ', 265 + client->driver_power_control ? "Dyn" : "", 269 266 client->pwr_state ? "Pwr" : "Off", 270 267 pci_name(client->pdev)); 271 268 i++; ··· 282 277 283 278 static int vga_switchon(struct vga_switcheroo_client *client) 284 279 { 280 + if (client->driver_power_control) 281 + return 0; 285 282 if (vgasr_priv.handler->power_state) 286 283 vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); 287 284 /* call the driver callback to turn on device */ ··· 294 287 295 288 static int vga_switchoff(struct vga_switcheroo_client *client) 296 289 { 290 + if (client->driver_power_control) 291 + return 0; 297 292 /* call the driver callback to turn off device */ 298 293 client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); 299 294 if (vgasr_priv.handler->power_state) ··· 411 402 list_for_each_entry(client, &vgasr_priv.clients, list) { 412 403 if (client->active || client_is_audio(client)) 413 404 continue; 405 + if (client->driver_power_control) 406 + continue; 414 407 set_audio_state(client->id, VGA_SWITCHEROO_OFF); 415 408 if (client->pwr_state == VGA_SWITCHEROO_ON) 416 409 vga_switchoff(client); ··· 423 412 if (strncmp(usercmd, "ON", 2) == 0) { 424 413 list_for_each_entry(client, &vgasr_priv.clients, list) { 425 414 if (client->active || client_is_audio(client)) 415 + continue; 416 + if (client->driver_power_control) 426 417 continue; 427 418 if (client->pwr_state == VGA_SWITCHEROO_OFF) 428 419 vga_switchon(client); ··· 578 565 return err; 579 566 } 580 567 EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); 568 + 569 + static void vga_switcheroo_power_switch(struct pci_dev *pdev, enum vga_switcheroo_state state) 570 + { 571 + struct vga_switcheroo_client *client; 572 + 573 + if (!vgasr_priv.handler->power_state) 574 + return; 575 + 576 + client = find_client_from_pci(&vgasr_priv.clients, pdev); 577 + if (!client) 578 + return; 579 + 580 + if (!client->driver_power_control) 581 + return; 582 + 583 + vgasr_priv.handler->power_state(client->id, state); 584 + } 585 + 586 + /* force a PCI device to a certain state - mainly to turn off audio clients */ 587 + 588 + void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) 589 + { 590 + struct vga_switcheroo_client *client; 591 + 592 + client = find_client_from_pci(&vgasr_priv.clients, pdev); 593 + if (!client) 594 + return; 595 + 596 + if (!client->driver_power_control) 597 + return; 598 + 599 + client->pwr_state = dynamic; 600 + set_audio_state(client->id, dynamic); 601 + } 602 + EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch); 603 + 604 + /* switcheroo power domain */ 605 + static int vga_switcheroo_runtime_suspend(struct device *dev) 606 + { 607 + struct pci_dev *pdev = to_pci_dev(dev); 608 + int ret; 609 + 610 + ret = dev->bus->pm->runtime_suspend(dev); 611 + if (ret) 612 + return ret; 613 + 614 + vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF); 615 + return 0; 616 + } 617 + 618 + static int vga_switcheroo_runtime_resume(struct device *dev) 619 + { 620 + struct pci_dev *pdev = to_pci_dev(dev); 621 + int ret; 622 + 623 + vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_ON); 624 + ret = dev->bus->pm->runtime_resume(dev); 625 + if (ret) 626 + return ret; 627 + 628 + return 0; 629 + } 630 + 631 + /* this version is for the case where the power switch is separate 632 + to the device being powered down. */ 633 + int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) 634 + { 635 + /* copy over all the bus versions */ 636 + if (dev->bus && dev->bus->pm) { 637 + domain->ops = *dev->bus->pm; 638 + domain->ops.runtime_suspend = vga_switcheroo_runtime_suspend; 639 + domain->ops.runtime_resume = vga_switcheroo_runtime_resume; 640 + 641 + dev->pm_domain = domain; 642 + return 0; 643 + } 644 + dev->pm_domain = NULL; 645 + return -EINVAL; 646 + } 647 + EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops); 648 + 649 + static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) 650 + { 651 + struct pci_dev *pdev = to_pci_dev(dev); 652 + int ret; 653 + struct vga_switcheroo_client *client, *found = NULL; 654 + 655 + /* we need to check if we have to switch back on the video 656 + device so the audio device can come back */ 657 + list_for_each_entry(client, &vgasr_priv.clients, list) { 658 + if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && client_is_vga(client)) { 659 + found = client; 660 + ret = pm_runtime_get_sync(&client->pdev->dev); 661 + if (ret) { 662 + if (ret != 1) 663 + return ret; 664 + } 665 + break; 666 + } 667 + } 668 + ret = dev->bus->pm->runtime_resume(dev); 669 + 670 + /* put the reference for the gpu */ 671 + if (found) { 672 + pm_runtime_mark_last_busy(&found->pdev->dev); 673 + pm_runtime_put_autosuspend(&found->pdev->dev); 674 + } 675 + return ret; 676 + } 677 + 678 + int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) 679 + { 680 + /* copy over all the bus versions */ 681 + if (dev->bus && dev->bus->pm) { 682 + domain->ops = *dev->bus->pm; 683 + domain->ops.runtime_resume = vga_switcheroo_runtime_resume_hdmi_audio; 684 + 685 + dev->pm_domain = domain; 686 + return 0; 687 + } 688 + dev->pm_domain = NULL; 689 + return -EINVAL; 690 + } 691 + EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio);
+11 -2
include/linux/vga_switcheroo.h
··· 45 45 #if defined(CONFIG_VGA_SWITCHEROO) 46 46 void vga_switcheroo_unregister_client(struct pci_dev *dev); 47 47 int vga_switcheroo_register_client(struct pci_dev *dev, 48 - const struct vga_switcheroo_client_ops *ops); 48 + const struct vga_switcheroo_client_ops *ops, 49 + bool driver_power_control); 49 50 int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 50 51 const struct vga_switcheroo_client_ops *ops, 51 52 int id, bool active); ··· 61 60 62 61 int vga_switcheroo_get_client_state(struct pci_dev *dev); 63 62 63 + void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic); 64 + 65 + int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain); 66 + int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain); 64 67 #else 65 68 66 69 static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} 67 70 static inline int vga_switcheroo_register_client(struct pci_dev *dev, 68 - const struct vga_switcheroo_client_ops *ops) { return 0; } 71 + const struct vga_switcheroo_client_ops *ops, bool driver_power_control) { return 0; } 69 72 static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} 70 73 static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } 71 74 static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, ··· 79 74 static inline int vga_switcheroo_process_delayed_switch(void) { return 0; } 80 75 static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; } 81 76 77 + static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) {} 78 + 79 + static inline int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; } 80 + static inline int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; } 82 81 83 82 #endif 84 83 #endif /* _LINUX_VGA_SWITCHEROO_H_ */