diff options
author | Dave Airlie <airlied@linux.ie> | 2010-02-01 00:38:10 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-03-01 01:20:37 -0500 |
commit | 6a9ee8af344e3bd7dbd61e67037096cdf7f83289 (patch) | |
tree | 07cdb493a790cf45bc473f2fc2ea1b9a166d5191 /drivers/gpu/drm/nouveau/nouveau_acpi.c | |
parent | 9fd1de52945e06ed88a440c99ca92dab74b9b33c (diff) |
vga_switcheroo: initial implementation (v15)
Many new laptops now come with 2 gpus, one to be used for low power
modes and one for gaming/on-ac applications. These GPUs are typically
wired to the laptop panel and VGA ports via a multiplexer unit which
is controlled via ACPI methods.
4 combinations of systems typically exist - with 2 ACPI methods.
Intel/ATI - Lenovo W500/T500 - use ATPX ACPI method
ATI/ATI - some ASUS - use ATPX ACPI Method
Intel/Nvidia - - use _DSM ACPI method
Nvidia/Nvidia - - use _DSM ACPI method.
TODO:
This patch adds support for the ATPX method and initial bits
for the _DSM methods that need to written by someone with
access to the hardware.
Add a proper non-debugfs interface - need to get some proper
testing first.
v2: add power up/down support for both devices
on W500 puts i915/radeon into D3 and cuts power to radeon.
v3: redo probing methods, no DMI list, drm devices call to
register with switcheroo, it tries to find an ATPX method on
any device and once there is two devices + ATPX it inits the
switcher.
v4: ATPX msg handling using buffers - should work on more machines
v5: rearchitect after more mjg59 discussion - move ATPX handling to
radeon driver.
v6: add file headers + initial nouveau bits (to be filled out).
v7: merge delayed switcher code.
v8: avoid suspend/resume of gpu that is off
v9: rearchitect - mjg59 is always right. - move all ATPX code to
radeon, should allow simpler DSM also proper ATRM handling
v10: add ATRM support for radeon BIOS, add mutex to lock vgasr_priv
v11: fix bug in resuming Intel for 2nd time.
v12: start fixing up nvidia code blindly.
v13: blindly guess at finishing nvidia code
v14: remove radeon audio hacks - fix up intel resume more like upstream
v15: clean up printks + remove unnecessary igd/dis pointers
mount debugfs
/sys/kernel/debug/vgaswitcheroo/switch - should exist if ATPX detected
+ 2 cards.
DIS - immediate change to discrete
IGD - immediate change to IGD
DDIS - delayed change to discrete
DIGD - delayed change to IGD
ON - turn on not in use
OFF - turn off not in use
Tested on W500 (Intel/ATI) and T500 (Intel/ATI)
Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/nouveau/nouveau_acpi.c')
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_acpi.c | 160 |
1 files changed, 120 insertions, 40 deletions
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index 48227e744753..0e0730a53137 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c | |||
@@ -11,6 +11,8 @@ | |||
11 | #include "nouveau_drm.h" | 11 | #include "nouveau_drm.h" |
12 | #include "nv50_display.h" | 12 | #include "nv50_display.h" |
13 | 13 | ||
14 | #include <linux/vga_switcheroo.h> | ||
15 | |||
14 | #define NOUVEAU_DSM_SUPPORTED 0x00 | 16 | #define NOUVEAU_DSM_SUPPORTED 0x00 |
15 | #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 | 17 | #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 |
16 | 18 | ||
@@ -28,31 +30,30 @@ | |||
28 | #define NOUVEAU_DSM_POWER_SPEED 0x01 | 30 | #define NOUVEAU_DSM_POWER_SPEED 0x01 |
29 | #define NOUVEAU_DSM_POWER_STAMINA 0x02 | 31 | #define NOUVEAU_DSM_POWER_STAMINA 0x02 |
30 | 32 | ||
31 | static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) | 33 | static struct nouveau_dsm_priv { |
32 | { | 34 | bool dsm_detected; |
33 | static char muid[] = { | 35 | acpi_handle dhandle; |
34 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, | 36 | acpi_handle dsm_handle; |
35 | 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, | 37 | } nouveau_dsm_priv; |
36 | }; | 38 | |
39 | static const char nouveau_dsm_muid[] = { | ||
40 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, | ||
41 | 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, | ||
42 | }; | ||
37 | 43 | ||
38 | struct pci_dev *pdev = dev->pdev; | 44 | static int nouveau_dsm(acpi_handle handle, int func, int arg, int *result) |
39 | struct acpi_handle *handle; | 45 | { |
40 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 46 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
41 | struct acpi_object_list input; | 47 | struct acpi_object_list input; |
42 | union acpi_object params[4]; | 48 | union acpi_object params[4]; |
43 | union acpi_object *obj; | 49 | union acpi_object *obj; |
44 | int err; | 50 | int err; |
45 | 51 | ||
46 | handle = DEVICE_ACPI_HANDLE(&pdev->dev); | ||
47 | |||
48 | if (!handle) | ||
49 | return -ENODEV; | ||
50 | |||
51 | input.count = 4; | 52 | input.count = 4; |
52 | input.pointer = params; | 53 | input.pointer = params; |
53 | params[0].type = ACPI_TYPE_BUFFER; | 54 | params[0].type = ACPI_TYPE_BUFFER; |
54 | params[0].buffer.length = sizeof(muid); | 55 | params[0].buffer.length = sizeof(nouveau_dsm_muid); |
55 | params[0].buffer.pointer = (char *)muid; | 56 | params[0].buffer.pointer = (char *)nouveau_dsm_muid; |
56 | params[1].type = ACPI_TYPE_INTEGER; | 57 | params[1].type = ACPI_TYPE_INTEGER; |
57 | params[1].integer.value = 0x00000102; | 58 | params[1].integer.value = 0x00000102; |
58 | params[2].type = ACPI_TYPE_INTEGER; | 59 | params[2].type = ACPI_TYPE_INTEGER; |
@@ -62,7 +63,7 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) | |||
62 | 63 | ||
63 | err = acpi_evaluate_object(handle, "_DSM", &input, &output); | 64 | err = acpi_evaluate_object(handle, "_DSM", &input, &output); |
64 | if (err) { | 65 | if (err) { |
65 | NV_INFO(dev, "failed to evaluate _DSM: %d\n", err); | 66 | printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); |
66 | return err; | 67 | return err; |
67 | } | 68 | } |
68 | 69 | ||
@@ -86,40 +87,119 @@ static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) | |||
86 | return 0; | 87 | return 0; |
87 | } | 88 | } |
88 | 89 | ||
89 | int nouveau_hybrid_setup(struct drm_device *dev) | 90 | static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) |
90 | { | 91 | { |
91 | int result; | 92 | return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); |
92 | 93 | } | |
93 | if (nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STATE, | 94 | |
94 | &result)) | 95 | static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) |
95 | return -ENODEV; | 96 | { |
96 | 97 | int arg; | |
97 | NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result); | 98 | if (state == VGA_SWITCHEROO_ON) |
98 | 99 | arg = NOUVEAU_DSM_POWER_SPEED; | |
99 | if (result) { /* Ensure that the external GPU is enabled */ | 100 | else |
100 | nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL); | 101 | arg = NOUVEAU_DSM_POWER_STAMINA; |
101 | nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED, | 102 | nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); |
102 | NULL); | 103 | return 0; |
103 | } else { /* Stamina mode - disable the external GPU */ | 104 | } |
104 | nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA, | 105 | |
105 | NULL); | 106 | static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) |
106 | nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA, | 107 | { |
107 | NULL); | 108 | if (id == VGA_SWITCHEROO_IGD) |
108 | } | 109 | return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_STAMINA); |
110 | else | ||
111 | return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_SPEED); | ||
112 | } | ||
109 | 113 | ||
114 | static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, | ||
115 | enum vga_switcheroo_state state) | ||
116 | { | ||
117 | if (id == VGA_SWITCHEROO_IGD) | ||
118 | return 0; | ||
119 | |||
120 | return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dsm_handle, state); | ||
121 | } | ||
122 | |||
123 | static int nouveau_dsm_init(void) | ||
124 | { | ||
110 | return 0; | 125 | return 0; |
111 | } | 126 | } |
112 | 127 | ||
113 | bool nouveau_dsm_probe(struct drm_device *dev) | 128 | static int nouveau_dsm_get_client_id(struct pci_dev *pdev) |
114 | { | 129 | { |
115 | int support = 0; | 130 | if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) |
131 | return VGA_SWITCHEROO_IGD; | ||
132 | else | ||
133 | return VGA_SWITCHEROO_DIS; | ||
134 | } | ||
135 | |||
136 | static struct vga_switcheroo_handler nouveau_dsm_handler = { | ||
137 | .switchto = nouveau_dsm_switchto, | ||
138 | .power_state = nouveau_dsm_power_state, | ||
139 | .init = nouveau_dsm_init, | ||
140 | .get_client_id = nouveau_dsm_get_client_id, | ||
141 | }; | ||
116 | 142 | ||
117 | if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED, | 143 | static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) |
118 | NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support)) | 144 | { |
145 | acpi_handle dhandle, nvidia_handle; | ||
146 | acpi_status status; | ||
147 | int ret; | ||
148 | uint32_t result; | ||
149 | |||
150 | dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); | ||
151 | if (!dhandle) | ||
152 | return false; | ||
153 | status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); | ||
154 | if (ACPI_FAILURE(status)) { | ||
119 | return false; | 155 | return false; |
156 | } | ||
120 | 157 | ||
121 | if (!support) | 158 | ret= nouveau_dsm(nvidia_handle, NOUVEAU_DSM_SUPPORTED, |
159 | NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); | ||
160 | if (ret < 0) | ||
122 | return false; | 161 | return false; |
123 | 162 | ||
163 | nouveau_dsm_priv.dhandle = dhandle; | ||
164 | nouveau_dsm_priv.dsm_handle = nvidia_handle; | ||
124 | return true; | 165 | return true; |
125 | } | 166 | } |
167 | |||
168 | static bool nouveau_dsm_detect(void) | ||
169 | { | ||
170 | char acpi_method_name[255] = { 0 }; | ||
171 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; | ||
172 | struct pci_dev *pdev = NULL; | ||
173 | int has_dsm = 0; | ||
174 | int vga_count = 0; | ||
175 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { | ||
176 | vga_count++; | ||
177 | |||
178 | has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); | ||
179 | } | ||
180 | |||
181 | if (vga_count == 2 && has_dsm) { | ||
182 | acpi_get_name(nouveau_dsm_priv.dsm_handle, ACPI_FULL_PATHNAME, &buffer); | ||
183 | printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", | ||
184 | acpi_method_name); | ||
185 | nouveau_dsm_priv.dsm_detected = true; | ||
186 | return true; | ||
187 | } | ||
188 | return false; | ||
189 | } | ||
190 | |||
191 | void nouveau_register_dsm_handler(void) | ||
192 | { | ||
193 | bool r; | ||
194 | |||
195 | r = nouveau_dsm_detect(); | ||
196 | if (!r) | ||
197 | return; | ||
198 | |||
199 | vga_switcheroo_register_handler(&nouveau_dsm_handler); | ||
200 | } | ||
201 | |||
202 | void nouveau_unregister_dsm_handler(void) | ||
203 | { | ||
204 | vga_switcheroo_unregister_handler(); | ||
205 | } | ||