aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Airlie <airlied@dhcp-40-90.bne.redhat.com>2012-09-09 22:28:36 -0400
committerDave Airlie <airlied@redhat.com>2013-08-28 23:30:21 -0400
commit0d69704ae348c03bc216b01e32a0e9a2372be419 (patch)
treecb63fed3b11746ffb688ff8fd6efa1c087ee8254
parente906d7bdd3b63ffac8b91f2f05c450775de95ef6 (diff)
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>
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c2
-rw-r--r--drivers/gpu/drm/nouveau/nouveau_vga.c2
-rw-r--r--drivers/gpu/drm/radeon/radeon_device.c2
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c147
-rw-r--r--include/linux/vga_switcheroo.h13
5 files changed, 156 insertions, 10 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 0adfe4000871..54f86242e80e 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1293,7 +1293,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
1293 1293
1294 intel_register_dsm_handler(); 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 if (ret) 1297 if (ret)
1298 goto cleanup_vga_client; 1298 goto cleanup_vga_client;
1299 1299
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 25d3495725eb..40a09f11a600 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -79,7 +79,7 @@ nouveau_vga_init(struct nouveau_drm *drm)
79{ 79{
80 struct drm_device *dev = drm->dev; 80 struct drm_device *dev = drm->dev;
81 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 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
85void 85void
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
index 82335e38ec4f..0610ca4fb6a3 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1269,7 +1269,7 @@ int radeon_device_init(struct radeon_device *rdev,
1269 /* this will fail for cards that aren't VGA class devices, just 1269 /* this will fail for cards that aren't VGA class devices, just
1270 * ignore it */ 1270 * ignore it */
1271 vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); 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 r = radeon_init(rdev); 1274 r = radeon_init(rdev);
1275 if (r) 1275 if (r)
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index cf787e1d9322..ec0ae2d1686a 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -27,6 +27,7 @@
27#include <linux/pci.h> 27#include <linux/pci.h>
28#include <linux/console.h> 28#include <linux/console.h>
29#include <linux/vga_switcheroo.h> 29#include <linux/vga_switcheroo.h>
30#include <linux/pm_runtime.h>
30 31
31#include <linux/vgaarb.h> 32#include <linux/vgaarb.h>
32 33
@@ -37,6 +38,7 @@ struct vga_switcheroo_client {
37 const struct vga_switcheroo_client_ops *ops; 38 const struct vga_switcheroo_client_ops *ops;
38 int id; 39 int id;
39 bool active; 40 bool active;
41 bool driver_power_control;
40 struct list_head list; 42 struct list_head list;
41}; 43};
42 44
@@ -132,7 +134,7 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
132 134
133static int register_client(struct pci_dev *pdev, 135static int register_client(struct pci_dev *pdev,
134 const struct vga_switcheroo_client_ops *ops, 136 const struct vga_switcheroo_client_ops *ops,
135 int id, bool active) 137 int id, bool active, bool driver_power_control)
136{ 138{
137 struct vga_switcheroo_client *client; 139 struct vga_switcheroo_client *client;
138 140
@@ -145,6 +147,7 @@ static int register_client(struct pci_dev *pdev,
145 client->ops = ops; 147 client->ops = ops;
146 client->id = id; 148 client->id = id;
147 client->active = active; 149 client->active = active;
150 client->driver_power_control = driver_power_control;
148 151
149 mutex_lock(&vgasr_mutex); 152 mutex_lock(&vgasr_mutex);
150 list_add_tail(&client->list, &vgasr_priv.clients); 153 list_add_tail(&client->list, &vgasr_priv.clients);
@@ -160,10 +163,11 @@ static int register_client(struct pci_dev *pdev,
160} 163}
161 164
162int vga_switcheroo_register_client(struct pci_dev *pdev, 165int vga_switcheroo_register_client(struct pci_dev *pdev,
163 const struct vga_switcheroo_client_ops *ops) 166 const struct vga_switcheroo_client_ops *ops,
167 bool driver_power_control)
164{ 168{
165 return register_client(pdev, ops, -1, 169 return register_client(pdev, ops, -1,
166 pdev == vga_default_device()); 170 pdev == vga_default_device(), driver_power_control);
167} 171}
168EXPORT_SYMBOL(vga_switcheroo_register_client); 172EXPORT_SYMBOL(vga_switcheroo_register_client);
169 173
@@ -171,7 +175,7 @@ int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
171 const struct vga_switcheroo_client_ops *ops, 175 const struct vga_switcheroo_client_ops *ops,
172 int id, bool active) 176 int id, bool active)
173{ 177{
174 return register_client(pdev, ops, id | ID_BIT_AUDIO, active); 178 return register_client(pdev, ops, id | ID_BIT_AUDIO, active, false);
175} 179}
176EXPORT_SYMBOL(vga_switcheroo_register_audio_client); 180EXPORT_SYMBOL(vga_switcheroo_register_audio_client);
177 181
@@ -258,10 +262,11 @@ static int vga_switcheroo_show(struct seq_file *m, void *v)
258 int i = 0; 262 int i = 0;
259 mutex_lock(&vgasr_mutex); 263 mutex_lock(&vgasr_mutex);
260 list_for_each_entry(client, &vgasr_priv.clients, list) { 264 list_for_each_entry(client, &vgasr_priv.clients, list) {
261 seq_printf(m, "%d:%s%s:%c:%s:%s\n", i, 265 seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i,
262 client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", 266 client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD",
263 client_is_vga(client) ? "" : "-Audio", 267 client_is_vga(client) ? "" : "-Audio",
264 client->active ? '+' : ' ', 268 client->active ? '+' : ' ',
269 client->driver_power_control ? "Dyn" : "",
265 client->pwr_state ? "Pwr" : "Off", 270 client->pwr_state ? "Pwr" : "Off",
266 pci_name(client->pdev)); 271 pci_name(client->pdev));
267 i++; 272 i++;
@@ -277,6 +282,8 @@ static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
277 282
278static int vga_switchon(struct vga_switcheroo_client *client) 283static int vga_switchon(struct vga_switcheroo_client *client)
279{ 284{
285 if (client->driver_power_control)
286 return 0;
280 if (vgasr_priv.handler->power_state) 287 if (vgasr_priv.handler->power_state)
281 vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); 288 vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);
282 /* call the driver callback to turn on device */ 289 /* call the driver callback to turn on device */
@@ -287,6 +294,8 @@ static int vga_switchon(struct vga_switcheroo_client *client)
287 294
288static int vga_switchoff(struct vga_switcheroo_client *client) 295static int vga_switchoff(struct vga_switcheroo_client *client)
289{ 296{
297 if (client->driver_power_control)
298 return 0;
290 /* call the driver callback to turn off device */ 299 /* call the driver callback to turn off device */
291 client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); 300 client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);
292 if (vgasr_priv.handler->power_state) 301 if (vgasr_priv.handler->power_state)
@@ -402,6 +411,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
402 list_for_each_entry(client, &vgasr_priv.clients, list) { 411 list_for_each_entry(client, &vgasr_priv.clients, list) {
403 if (client->active || client_is_audio(client)) 412 if (client->active || client_is_audio(client))
404 continue; 413 continue;
414 if (client->driver_power_control)
415 continue;
405 set_audio_state(client->id, VGA_SWITCHEROO_OFF); 416 set_audio_state(client->id, VGA_SWITCHEROO_OFF);
406 if (client->pwr_state == VGA_SWITCHEROO_ON) 417 if (client->pwr_state == VGA_SWITCHEROO_ON)
407 vga_switchoff(client); 418 vga_switchoff(client);
@@ -413,6 +424,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
413 list_for_each_entry(client, &vgasr_priv.clients, list) { 424 list_for_each_entry(client, &vgasr_priv.clients, list) {
414 if (client->active || client_is_audio(client)) 425 if (client->active || client_is_audio(client))
415 continue; 426 continue;
427 if (client->driver_power_control)
428 continue;
416 if (client->pwr_state == VGA_SWITCHEROO_OFF) 429 if (client->pwr_state == VGA_SWITCHEROO_OFF)
417 vga_switchon(client); 430 vga_switchon(client);
418 set_audio_state(client->id, VGA_SWITCHEROO_ON); 431 set_audio_state(client->id, VGA_SWITCHEROO_ON);
@@ -565,3 +578,127 @@ err:
565 return err; 578 return err;
566} 579}
567EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); 580EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
581
582static void vga_switcheroo_power_switch(struct pci_dev *pdev, enum vga_switcheroo_state state)
583{
584 struct vga_switcheroo_client *client;
585
586 if (!vgasr_priv.handler->power_state)
587 return;
588
589 client = find_client_from_pci(&vgasr_priv.clients, pdev);
590 if (!client)
591 return;
592
593 if (!client->driver_power_control)
594 return;
595
596 vgasr_priv.handler->power_state(client->id, state);
597}
598
599/* force a PCI device to a certain state - mainly to turn off audio clients */
600
601void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic)
602{
603 struct vga_switcheroo_client *client;
604
605 client = find_client_from_pci(&vgasr_priv.clients, pdev);
606 if (!client)
607 return;
608
609 if (!client->driver_power_control)
610 return;
611
612 client->pwr_state = dynamic;
613 set_audio_state(client->id, dynamic);
614}
615EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch);
616
617/* switcheroo power domain */
618static int vga_switcheroo_runtime_suspend(struct device *dev)
619{
620 struct pci_dev *pdev = to_pci_dev(dev);
621 int ret;
622
623 ret = dev->bus->pm->runtime_suspend(dev);
624 if (ret)
625 return ret;
626
627 vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_OFF);
628 return 0;
629}
630
631static int vga_switcheroo_runtime_resume(struct device *dev)
632{
633 struct pci_dev *pdev = to_pci_dev(dev);
634 int ret;
635
636 vga_switcheroo_power_switch(pdev, VGA_SWITCHEROO_ON);
637 ret = dev->bus->pm->runtime_resume(dev);
638 if (ret)
639 return ret;
640
641 return 0;
642}
643
644/* this version is for the case where the power switch is separate
645 to the device being powered down. */
646int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain)
647{
648 /* copy over all the bus versions */
649 if (dev->bus && dev->bus->pm) {
650 domain->ops = *dev->bus->pm;
651 domain->ops.runtime_suspend = vga_switcheroo_runtime_suspend;
652 domain->ops.runtime_resume = vga_switcheroo_runtime_resume;
653
654 dev->pm_domain = domain;
655 return 0;
656 }
657 dev->pm_domain = NULL;
658 return -EINVAL;
659}
660EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops);
661
662static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev)
663{
664 struct pci_dev *pdev = to_pci_dev(dev);
665 int ret;
666 struct vga_switcheroo_client *client, *found = NULL;
667
668 /* we need to check if we have to switch back on the video
669 device so the audio device can come back */
670 list_for_each_entry(client, &vgasr_priv.clients, list) {
671 if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && client_is_vga(client)) {
672 found = client;
673 ret = pm_runtime_get_sync(&client->pdev->dev);
674 if (ret) {
675 if (ret != 1)
676 return ret;
677 }
678 break;
679 }
680 }
681 ret = dev->bus->pm->runtime_resume(dev);
682
683 /* put the reference for the gpu */
684 if (found) {
685 pm_runtime_mark_last_busy(&found->pdev->dev);
686 pm_runtime_put_autosuspend(&found->pdev->dev);
687 }
688 return ret;
689}
690
691int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain)
692{
693 /* copy over all the bus versions */
694 if (dev->bus && dev->bus->pm) {
695 domain->ops = *dev->bus->pm;
696 domain->ops.runtime_resume = vga_switcheroo_runtime_resume_hdmi_audio;
697
698 dev->pm_domain = domain;
699 return 0;
700 }
701 dev->pm_domain = NULL;
702 return -EINVAL;
703}
704EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio);
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index ddb419cf4530..502073a53dd3 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -45,7 +45,8 @@ struct vga_switcheroo_client_ops {
45#if defined(CONFIG_VGA_SWITCHEROO) 45#if defined(CONFIG_VGA_SWITCHEROO)
46void vga_switcheroo_unregister_client(struct pci_dev *dev); 46void vga_switcheroo_unregister_client(struct pci_dev *dev);
47int vga_switcheroo_register_client(struct pci_dev *dev, 47int 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);
49int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 50int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
50 const struct vga_switcheroo_client_ops *ops, 51 const struct vga_switcheroo_client_ops *ops,
51 int id, bool active); 52 int id, bool active);
@@ -60,11 +61,15 @@ int vga_switcheroo_process_delayed_switch(void);
60 61
61int vga_switcheroo_get_client_state(struct pci_dev *dev); 62int vga_switcheroo_get_client_state(struct pci_dev *dev);
62 63
64void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic);
65
66int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain);
67int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain);
63#else 68#else
64 69
65static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} 70static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
66static inline int vga_switcheroo_register_client(struct pci_dev *dev, 71static inline int vga_switcheroo_register_client(struct pci_dev *dev,
67 const struct vga_switcheroo_client_ops *ops) { return 0; } 72 const struct vga_switcheroo_client_ops *ops, bool driver_power_control) { return 0; }
68static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} 73static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {}
69static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } 74static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; }
70static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 75static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
@@ -74,6 +79,10 @@ static inline void vga_switcheroo_unregister_handler(void) {}
74static inline int vga_switcheroo_process_delayed_switch(void) { return 0; } 79static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
75static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; } 80static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; }
76 81
82static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) {}
83
84static inline int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
85static inline int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
77 86
78#endif 87#endif
79#endif /* _LINUX_VGA_SWITCHEROO_H_ */ 88#endif /* _LINUX_VGA_SWITCHEROO_H_ */