diff options
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_vga.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_vga.c | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c b/drivers/gpu/drm/nouveau/nouveau_vga.c new file mode 100644 index 000000000000..6f0ac64873df --- /dev/null +++ b/drivers/gpu/drm/nouveau/nouveau_vga.c | |||
@@ -0,0 +1,99 @@ | |||
1 | #include <linux/vgaarb.h> | ||
2 | #include <linux/vga_switcheroo.h> | ||
3 | |||
4 | #include <drm/drmP.h> | ||
5 | #include <drm/drm_crtc_helper.h> | ||
6 | |||
7 | #include "nouveau_drm.h" | ||
8 | #include "nouveau_acpi.h" | ||
9 | #include "nouveau_fbcon.h" | ||
10 | #include "nouveau_vga.h" | ||
11 | |||
12 | static unsigned int | ||
13 | nouveau_vga_set_decode(void *priv, bool state) | ||
14 | { | ||
15 | struct nouveau_device *device = nouveau_dev(priv); | ||
16 | |||
17 | if (device->chipset >= 0x40) | ||
18 | nv_wr32(device, 0x088054, state); | ||
19 | else | ||
20 | nv_wr32(device, 0x001854, state); | ||
21 | |||
22 | if (state) | ||
23 | return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM | | ||
24 | VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | ||
25 | else | ||
26 | return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | ||
27 | } | ||
28 | |||
29 | static void | ||
30 | nouveau_switcheroo_set_state(struct pci_dev *pdev, | ||
31 | enum vga_switcheroo_state state) | ||
32 | { | ||
33 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
34 | pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; | ||
35 | |||
36 | if (state == VGA_SWITCHEROO_ON) { | ||
37 | printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); | ||
38 | dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | ||
39 | nouveau_drm_resume(pdev); | ||
40 | drm_kms_helper_poll_enable(dev); | ||
41 | dev->switch_power_state = DRM_SWITCH_POWER_ON; | ||
42 | } else { | ||
43 | printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); | ||
44 | dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; | ||
45 | drm_kms_helper_poll_disable(dev); | ||
46 | nouveau_switcheroo_optimus_dsm(); | ||
47 | nouveau_drm_suspend(pdev, pmm); | ||
48 | dev->switch_power_state = DRM_SWITCH_POWER_OFF; | ||
49 | } | ||
50 | } | ||
51 | |||
52 | static void | ||
53 | nouveau_switcheroo_reprobe(struct pci_dev *pdev) | ||
54 | { | ||
55 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
56 | nouveau_fbcon_output_poll_changed(dev); | ||
57 | } | ||
58 | |||
59 | static bool | ||
60 | nouveau_switcheroo_can_switch(struct pci_dev *pdev) | ||
61 | { | ||
62 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
63 | bool can_switch; | ||
64 | |||
65 | spin_lock(&dev->count_lock); | ||
66 | can_switch = (dev->open_count == 0); | ||
67 | spin_unlock(&dev->count_lock); | ||
68 | return can_switch; | ||
69 | } | ||
70 | |||
71 | static const struct vga_switcheroo_client_ops | ||
72 | nouveau_switcheroo_ops = { | ||
73 | .set_gpu_state = nouveau_switcheroo_set_state, | ||
74 | .reprobe = nouveau_switcheroo_reprobe, | ||
75 | .can_switch = nouveau_switcheroo_can_switch, | ||
76 | }; | ||
77 | |||
78 | void | ||
79 | nouveau_vga_init(struct nouveau_drm *drm) | ||
80 | { | ||
81 | struct drm_device *dev = drm->dev; | ||
82 | vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); | ||
83 | vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops); | ||
84 | } | ||
85 | |||
86 | void | ||
87 | nouveau_vga_fini(struct nouveau_drm *drm) | ||
88 | { | ||
89 | struct drm_device *dev = drm->dev; | ||
90 | vga_switcheroo_unregister_client(dev->pdev); | ||
91 | vga_client_register(dev->pdev, NULL, NULL, NULL); | ||
92 | } | ||
93 | |||
94 | |||
95 | void | ||
96 | nouveau_vga_lastclose(struct drm_device *dev) | ||
97 | { | ||
98 | vga_switcheroo_process_delayed_switch(); | ||
99 | } | ||