Skip to content

Commit 0d69704

Browse files
Dave Airlieairlied
authored andcommitted
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 <[email protected]>
1 parent e906d7b commit 0d69704

File tree

5 files changed

+156
-10
lines changed

5 files changed

+156
-10
lines changed

drivers/gpu/drm/i915/i915_dma.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1293,7 +1293,7 @@ static int i915_load_modeset_init(struct drm_device *dev)
12931293

12941294
intel_register_dsm_handler();
12951295

1296-
ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops);
1296+
ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops, false);
12971297
if (ret)
12981298
goto cleanup_vga_client;
12991299

drivers/gpu/drm/nouveau/nouveau_vga.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ nouveau_vga_init(struct nouveau_drm *drm)
7979
{
8080
struct drm_device *dev = drm->dev;
8181
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);
8383
}
8484

8585
void

drivers/gpu/drm/radeon/radeon_device.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,7 @@ int radeon_device_init(struct radeon_device *rdev,
12691269
/* this will fail for cards that aren't VGA class devices, just
12701270
* ignore it */
12711271
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);
12731273

12741274
r = radeon_init(rdev);
12751275
if (r)

drivers/gpu/vga/vga_switcheroo.c

Lines changed: 142 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <linux/pci.h>
2828
#include <linux/console.h>
2929
#include <linux/vga_switcheroo.h>
30+
#include <linux/pm_runtime.h>
3031

3132
#include <linux/vgaarb.h>
3233

@@ -37,6 +38,7 @@ struct vga_switcheroo_client {
3738
const struct vga_switcheroo_client_ops *ops;
3839
int id;
3940
bool active;
41+
bool driver_power_control;
4042
struct list_head list;
4143
};
4244

@@ -132,7 +134,7 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
132134

133135
static int register_client(struct pci_dev *pdev,
134136
const struct vga_switcheroo_client_ops *ops,
135-
int id, bool active)
137+
int id, bool active, bool driver_power_control)
136138
{
137139
struct vga_switcheroo_client *client;
138140

@@ -145,6 +147,7 @@ static int register_client(struct pci_dev *pdev,
145147
client->ops = ops;
146148
client->id = id;
147149
client->active = active;
150+
client->driver_power_control = driver_power_control;
148151

149152
mutex_lock(&vgasr_mutex);
150153
list_add_tail(&client->list, &vgasr_priv.clients);
@@ -160,18 +163,19 @@ static int register_client(struct pci_dev *pdev,
160163
}
161164

162165
int 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)
164168
{
165169
return register_client(pdev, ops, -1,
166-
pdev == vga_default_device());
170+
pdev == vga_default_device(), driver_power_control);
167171
}
168172
EXPORT_SYMBOL(vga_switcheroo_register_client);
169173

170174
int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
171175
const struct vga_switcheroo_client_ops *ops,
172176
int id, bool active)
173177
{
174-
return register_client(pdev, ops, id | ID_BIT_AUDIO, active);
178+
return register_client(pdev, ops, id | ID_BIT_AUDIO, active, false);
175179
}
176180
EXPORT_SYMBOL(vga_switcheroo_register_audio_client);
177181

@@ -258,10 +262,11 @@ static int vga_switcheroo_show(struct seq_file *m, void *v)
258262
int i = 0;
259263
mutex_lock(&vgasr_mutex);
260264
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,
262266
client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD",
263267
client_is_vga(client) ? "" : "-Audio",
264268
client->active ? '+' : ' ',
269+
client->driver_power_control ? "Dyn" : "",
265270
client->pwr_state ? "Pwr" : "Off",
266271
pci_name(client->pdev));
267272
i++;
@@ -277,6 +282,8 @@ static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
277282

278283
static int vga_switchon(struct vga_switcheroo_client *client)
279284
{
285+
if (client->driver_power_control)
286+
return 0;
280287
if (vgasr_priv.handler->power_state)
281288
vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);
282289
/* call the driver callback to turn on device */
@@ -287,6 +294,8 @@ static int vga_switchon(struct vga_switcheroo_client *client)
287294

288295
static int vga_switchoff(struct vga_switcheroo_client *client)
289296
{
297+
if (client->driver_power_control)
298+
return 0;
290299
/* call the driver callback to turn off device */
291300
client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);
292301
if (vgasr_priv.handler->power_state)
@@ -402,6 +411,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
402411
list_for_each_entry(client, &vgasr_priv.clients, list) {
403412
if (client->active || client_is_audio(client))
404413
continue;
414+
if (client->driver_power_control)
415+
continue;
405416
set_audio_state(client->id, VGA_SWITCHEROO_OFF);
406417
if (client->pwr_state == VGA_SWITCHEROO_ON)
407418
vga_switchoff(client);
@@ -413,6 +424,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
413424
list_for_each_entry(client, &vgasr_priv.clients, list) {
414425
if (client->active || client_is_audio(client))
415426
continue;
427+
if (client->driver_power_control)
428+
continue;
416429
if (client->pwr_state == VGA_SWITCHEROO_OFF)
417430
vga_switchon(client);
418431
set_audio_state(client->id, VGA_SWITCHEROO_ON);
@@ -565,3 +578,127 @@ int vga_switcheroo_process_delayed_switch(void)
565578
return err;
566579
}
567580
EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
581+
582+
static 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+
601+
void 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+
}
615+
EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch);
616+
617+
/* switcheroo power domain */
618+
static 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+
631+
static 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. */
646+
int 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+
}
660+
EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_ops);
661+
662+
static 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+
691+
int 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+
}
704+
EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio);

include/linux/vga_switcheroo.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ struct vga_switcheroo_client_ops {
4545
#if defined(CONFIG_VGA_SWITCHEROO)
4646
void vga_switcheroo_unregister_client(struct pci_dev *dev);
4747
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);
4950
int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
5051
const struct vga_switcheroo_client_ops *ops,
5152
int id, bool active);
@@ -60,11 +61,15 @@ int vga_switcheroo_process_delayed_switch(void);
6061

6162
int vga_switcheroo_get_client_state(struct pci_dev *dev);
6263

64+
void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic);
65+
66+
int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain);
67+
int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain);
6368
#else
6469

6570
static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
6671
static 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; }
6873
static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {}
6974
static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; }
7075
static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
@@ -74,6 +79,10 @@ static inline void vga_switcheroo_unregister_handler(void) {}
7479
static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
7580
static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; }
7681

82+
static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) {}
83+
84+
static inline int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
85+
static inline int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
7786

7887
#endif
7988
#endif /* _LINUX_VGA_SWITCHEROO_H_ */

0 commit comments

Comments
 (0)