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/radeon | |
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/radeon')
-rw-r--r-- | drivers/gpu/drm/radeon/Makefile | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon.h | 8 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_atpx_handler.c | 258 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_bios.c | 44 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_device.c | 40 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.h | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_fb.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_kms.c | 3 |
9 files changed, 358 insertions, 6 deletions
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index 1cc7b937b1ea..8e62fe13e31c 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile | |||
@@ -54,7 +54,8 @@ radeon-y += radeon_device.o radeon_kms.o \ | |||
54 | radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ | 54 | radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ |
55 | rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ | 55 | rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ |
56 | r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ | 56 | r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ |
57 | r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o | 57 | r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \ |
58 | radeon_atpx_handler.o | ||
58 | 59 | ||
59 | radeon-$(CONFIG_COMPAT) += radeon_ioc32.o | 60 | radeon-$(CONFIG_COMPAT) += radeon_ioc32.o |
60 | 61 | ||
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index c0356bb193e5..a5dfb1557d3e 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h | |||
@@ -118,6 +118,10 @@ struct radeon_device; | |||
118 | /* | 118 | /* |
119 | * BIOS. | 119 | * BIOS. |
120 | */ | 120 | */ |
121 | #define ATRM_BIOS_PAGE 4096 | ||
122 | |||
123 | bool radeon_atrm_supported(struct pci_dev *pdev); | ||
124 | int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len); | ||
121 | bool radeon_get_bios(struct radeon_device *rdev); | 125 | bool radeon_get_bios(struct radeon_device *rdev); |
122 | 126 | ||
123 | 127 | ||
@@ -838,6 +842,8 @@ struct radeon_device { | |||
838 | int audio_bits_per_sample; | 842 | int audio_bits_per_sample; |
839 | uint8_t audio_status_bits; | 843 | uint8_t audio_status_bits; |
840 | uint8_t audio_category_code; | 844 | uint8_t audio_category_code; |
845 | |||
846 | bool powered_down; | ||
841 | }; | 847 | }; |
842 | 848 | ||
843 | int radeon_device_init(struct radeon_device *rdev, | 849 | int radeon_device_init(struct radeon_device *rdev, |
@@ -1042,6 +1048,8 @@ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enabl | |||
1042 | extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); | 1048 | extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); |
1043 | extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); | 1049 | extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); |
1044 | extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); | 1050 | extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); |
1051 | extern int radeon_resume_kms(struct drm_device *dev); | ||
1052 | extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state); | ||
1045 | 1053 | ||
1046 | /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ | 1054 | /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ |
1047 | struct r100_mc_save { | 1055 | struct r100_mc_save { |
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c b/drivers/gpu/drm/radeon/radeon_atpx_handler.c new file mode 100644 index 000000000000..0ae52f19071d --- /dev/null +++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c | |||
@@ -0,0 +1,258 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Red Hat Inc. | ||
3 | * Author : Dave Airlie <airlied@redhat.com> | ||
4 | * | ||
5 | * Licensed under GPLv2 | ||
6 | * | ||
7 | * ATPX support for both Intel/ATI | ||
8 | */ | ||
9 | |||
10 | #include <linux/vga_switcheroo.h> | ||
11 | #include <acpi/acpi.h> | ||
12 | #include <acpi/acpi_bus.h> | ||
13 | #include <linux/pci.h> | ||
14 | |||
15 | #define ATPX_VERSION 0 | ||
16 | #define ATPX_GPU_PWR 2 | ||
17 | #define ATPX_MUX_SELECT 3 | ||
18 | |||
19 | #define ATPX_INTEGRATED 0 | ||
20 | #define ATPX_DISCRETE 1 | ||
21 | |||
22 | #define ATPX_MUX_IGD 0 | ||
23 | #define ATPX_MUX_DISCRETE 1 | ||
24 | |||
25 | static struct radeon_atpx_priv { | ||
26 | bool atpx_detected; | ||
27 | /* handle for device - and atpx */ | ||
28 | acpi_handle dhandle; | ||
29 | acpi_handle atpx_handle; | ||
30 | acpi_handle atrm_handle; | ||
31 | } radeon_atpx_priv; | ||
32 | |||
33 | /* retrieve the ROM in 4k blocks */ | ||
34 | static int radeon_atrm_call(acpi_handle atrm_handle, uint8_t *bios, | ||
35 | int offset, int len) | ||
36 | { | ||
37 | acpi_status status; | ||
38 | union acpi_object atrm_arg_elements[2], *obj; | ||
39 | struct acpi_object_list atrm_arg; | ||
40 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL}; | ||
41 | |||
42 | atrm_arg.count = 2; | ||
43 | atrm_arg.pointer = &atrm_arg_elements[0]; | ||
44 | |||
45 | atrm_arg_elements[0].type = ACPI_TYPE_INTEGER; | ||
46 | atrm_arg_elements[0].integer.value = offset; | ||
47 | |||
48 | atrm_arg_elements[1].type = ACPI_TYPE_INTEGER; | ||
49 | atrm_arg_elements[1].integer.value = len; | ||
50 | |||
51 | status = acpi_evaluate_object(atrm_handle, NULL, &atrm_arg, &buffer); | ||
52 | if (ACPI_FAILURE(status)) { | ||
53 | printk("failed to evaluate ATRM got %s\n", acpi_format_exception(status)); | ||
54 | return -ENODEV; | ||
55 | } | ||
56 | |||
57 | obj = (union acpi_object *)buffer.pointer; | ||
58 | memcpy(bios+offset, obj->buffer.pointer, len); | ||
59 | kfree(buffer.pointer); | ||
60 | return len; | ||
61 | } | ||
62 | |||
63 | bool radeon_atrm_supported(struct pci_dev *pdev) | ||
64 | { | ||
65 | /* get the discrete ROM only via ATRM */ | ||
66 | if (!radeon_atpx_priv.atpx_detected) | ||
67 | return false; | ||
68 | |||
69 | if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) | ||
70 | return false; | ||
71 | return true; | ||
72 | } | ||
73 | |||
74 | |||
75 | int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len) | ||
76 | { | ||
77 | return radeon_atrm_call(radeon_atpx_priv.atrm_handle, bios, offset, len); | ||
78 | } | ||
79 | |||
80 | static int radeon_atpx_get_version(acpi_handle handle) | ||
81 | { | ||
82 | acpi_status status; | ||
83 | union acpi_object atpx_arg_elements[2], *obj; | ||
84 | struct acpi_object_list atpx_arg; | ||
85 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
86 | |||
87 | atpx_arg.count = 2; | ||
88 | atpx_arg.pointer = &atpx_arg_elements[0]; | ||
89 | |||
90 | atpx_arg_elements[0].type = ACPI_TYPE_INTEGER; | ||
91 | atpx_arg_elements[0].integer.value = ATPX_VERSION; | ||
92 | |||
93 | atpx_arg_elements[1].type = ACPI_TYPE_INTEGER; | ||
94 | atpx_arg_elements[1].integer.value = ATPX_VERSION; | ||
95 | |||
96 | status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer); | ||
97 | if (ACPI_FAILURE(status)) { | ||
98 | printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status)); | ||
99 | return -ENOSYS; | ||
100 | } | ||
101 | obj = (union acpi_object *)buffer.pointer; | ||
102 | if (obj && (obj->type == ACPI_TYPE_BUFFER)) | ||
103 | printk(KERN_INFO "radeon atpx: version is %d\n", *((u8 *)(obj->buffer.pointer) + 2)); | ||
104 | kfree(buffer.pointer); | ||
105 | return 0; | ||
106 | } | ||
107 | |||
108 | static int radeon_atpx_execute(acpi_handle handle, int cmd_id, u16 value) | ||
109 | { | ||
110 | acpi_status status; | ||
111 | union acpi_object atpx_arg_elements[2]; | ||
112 | struct acpi_object_list atpx_arg; | ||
113 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
114 | uint8_t buf[4] = {0}; | ||
115 | |||
116 | if (!handle) | ||
117 | return -EINVAL; | ||
118 | |||
119 | atpx_arg.count = 2; | ||
120 | atpx_arg.pointer = &atpx_arg_elements[0]; | ||
121 | |||
122 | atpx_arg_elements[0].type = ACPI_TYPE_INTEGER; | ||
123 | atpx_arg_elements[0].integer.value = cmd_id; | ||
124 | |||
125 | buf[2] = value & 0xff; | ||
126 | buf[3] = (value >> 8) & 0xff; | ||
127 | |||
128 | atpx_arg_elements[1].type = ACPI_TYPE_BUFFER; | ||
129 | atpx_arg_elements[1].buffer.length = 4; | ||
130 | atpx_arg_elements[1].buffer.pointer = buf; | ||
131 | |||
132 | status = acpi_evaluate_object(handle, NULL, &atpx_arg, &buffer); | ||
133 | if (ACPI_FAILURE(status)) { | ||
134 | printk("%s: failed to call ATPX: %s\n", __func__, acpi_format_exception(status)); | ||
135 | return -ENOSYS; | ||
136 | } | ||
137 | kfree(buffer.pointer); | ||
138 | |||
139 | return 0; | ||
140 | } | ||
141 | |||
142 | static int radeon_atpx_set_discrete_state(acpi_handle handle, int state) | ||
143 | { | ||
144 | return radeon_atpx_execute(handle, ATPX_GPU_PWR, state); | ||
145 | } | ||
146 | |||
147 | static int radeon_atpx_switch_mux(acpi_handle handle, int mux_id) | ||
148 | { | ||
149 | return radeon_atpx_execute(handle, ATPX_MUX_SELECT, mux_id); | ||
150 | } | ||
151 | |||
152 | |||
153 | static int radeon_atpx_switchto(enum vga_switcheroo_client_id id) | ||
154 | { | ||
155 | if (id == VGA_SWITCHEROO_IGD) | ||
156 | radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 0); | ||
157 | else | ||
158 | radeon_atpx_switch_mux(radeon_atpx_priv.atpx_handle, 1); | ||
159 | return 0; | ||
160 | } | ||
161 | |||
162 | static int radeon_atpx_power_state(enum vga_switcheroo_client_id id, | ||
163 | enum vga_switcheroo_state state) | ||
164 | { | ||
165 | /* on w500 ACPI can't change intel gpu state */ | ||
166 | if (id == VGA_SWITCHEROO_IGD) | ||
167 | return 0; | ||
168 | |||
169 | radeon_atpx_set_discrete_state(radeon_atpx_priv.atpx_handle, state); | ||
170 | return 0; | ||
171 | } | ||
172 | |||
173 | static bool radeon_atpx_pci_probe_handle(struct pci_dev *pdev) | ||
174 | { | ||
175 | acpi_handle dhandle, atpx_handle, atrm_handle; | ||
176 | acpi_status status; | ||
177 | |||
178 | dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); | ||
179 | if (!dhandle) | ||
180 | return false; | ||
181 | |||
182 | status = acpi_get_handle(dhandle, "ATPX", &atpx_handle); | ||
183 | if (ACPI_FAILURE(status)) | ||
184 | return false; | ||
185 | |||
186 | status = acpi_get_handle(dhandle, "ATRM", &atrm_handle); | ||
187 | if (ACPI_FAILURE(status)) | ||
188 | return false; | ||
189 | |||
190 | radeon_atpx_priv.dhandle = dhandle; | ||
191 | radeon_atpx_priv.atpx_handle = atpx_handle; | ||
192 | radeon_atpx_priv.atrm_handle = atrm_handle; | ||
193 | return true; | ||
194 | } | ||
195 | |||
196 | static int radeon_atpx_init(void) | ||
197 | { | ||
198 | /* set up the ATPX handle */ | ||
199 | |||
200 | radeon_atpx_get_version(radeon_atpx_priv.atpx_handle); | ||
201 | return 0; | ||
202 | } | ||
203 | |||
204 | static int radeon_atpx_get_client_id(struct pci_dev *pdev) | ||
205 | { | ||
206 | if (radeon_atpx_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) | ||
207 | return VGA_SWITCHEROO_IGD; | ||
208 | else | ||
209 | return VGA_SWITCHEROO_DIS; | ||
210 | } | ||
211 | |||
212 | static struct vga_switcheroo_handler radeon_atpx_handler = { | ||
213 | .switchto = radeon_atpx_switchto, | ||
214 | .power_state = radeon_atpx_power_state, | ||
215 | .init = radeon_atpx_init, | ||
216 | .get_client_id = radeon_atpx_get_client_id, | ||
217 | }; | ||
218 | |||
219 | static bool radeon_atpx_detect(void) | ||
220 | { | ||
221 | char acpi_method_name[255] = { 0 }; | ||
222 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; | ||
223 | struct pci_dev *pdev = NULL; | ||
224 | bool has_atpx = false; | ||
225 | int vga_count = 0; | ||
226 | |||
227 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { | ||
228 | vga_count++; | ||
229 | |||
230 | has_atpx |= (radeon_atpx_pci_probe_handle(pdev) == true); | ||
231 | } | ||
232 | |||
233 | if (has_atpx && vga_count == 2) { | ||
234 | acpi_get_name(radeon_atpx_priv.atpx_handle, ACPI_FULL_PATHNAME, &buffer); | ||
235 | printk(KERN_INFO "VGA switcheroo: detected switching method %s handle\n", | ||
236 | acpi_method_name); | ||
237 | radeon_atpx_priv.atpx_detected = true; | ||
238 | return true; | ||
239 | } | ||
240 | return false; | ||
241 | } | ||
242 | |||
243 | void radeon_register_atpx_handler(void) | ||
244 | { | ||
245 | bool r; | ||
246 | |||
247 | /* detect if we have any ATPX + 2 VGA in the system */ | ||
248 | r = radeon_atpx_detect(); | ||
249 | if (!r) | ||
250 | return; | ||
251 | |||
252 | vga_switcheroo_register_handler(&radeon_atpx_handler); | ||
253 | } | ||
254 | |||
255 | void radeon_unregister_atpx_handler(void) | ||
256 | { | ||
257 | vga_switcheroo_unregister_handler(); | ||
258 | } | ||
diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c index 906921740c60..a34b909485b8 100644 --- a/drivers/gpu/drm/radeon/radeon_bios.c +++ b/drivers/gpu/drm/radeon/radeon_bios.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "radeon.h" | 30 | #include "radeon.h" |
31 | #include "atom.h" | 31 | #include "atom.h" |
32 | 32 | ||
33 | #include <linux/vga_switcheroo.h> | ||
33 | /* | 34 | /* |
34 | * BIOS. | 35 | * BIOS. |
35 | */ | 36 | */ |
@@ -62,7 +63,7 @@ static bool igp_read_bios_from_vram(struct radeon_device *rdev) | |||
62 | iounmap(bios); | 63 | iounmap(bios); |
63 | return false; | 64 | return false; |
64 | } | 65 | } |
65 | memcpy(rdev->bios, bios, size); | 66 | memcpy_fromio(rdev->bios, bios, size); |
66 | iounmap(bios); | 67 | iounmap(bios); |
67 | return true; | 68 | return true; |
68 | } | 69 | } |
@@ -93,6 +94,38 @@ static bool radeon_read_bios(struct radeon_device *rdev) | |||
93 | return true; | 94 | return true; |
94 | } | 95 | } |
95 | 96 | ||
97 | /* ATRM is used to get the BIOS on the discrete cards in | ||
98 | * dual-gpu systems. | ||
99 | */ | ||
100 | static bool radeon_atrm_get_bios(struct radeon_device *rdev) | ||
101 | { | ||
102 | int ret; | ||
103 | int size = 64 * 1024; | ||
104 | int i; | ||
105 | |||
106 | if (!radeon_atrm_supported(rdev->pdev)) | ||
107 | return false; | ||
108 | |||
109 | rdev->bios = kmalloc(size, GFP_KERNEL); | ||
110 | if (!rdev->bios) { | ||
111 | DRM_ERROR("Unable to allocate bios\n"); | ||
112 | return false; | ||
113 | } | ||
114 | |||
115 | for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { | ||
116 | ret = radeon_atrm_get_bios_chunk(rdev->bios, | ||
117 | (i * ATRM_BIOS_PAGE), | ||
118 | ATRM_BIOS_PAGE); | ||
119 | if (ret <= 0) | ||
120 | break; | ||
121 | } | ||
122 | |||
123 | if (i == 0 || rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { | ||
124 | kfree(rdev->bios); | ||
125 | return false; | ||
126 | } | ||
127 | return true; | ||
128 | } | ||
96 | static bool r700_read_disabled_bios(struct radeon_device *rdev) | 129 | static bool r700_read_disabled_bios(struct radeon_device *rdev) |
97 | { | 130 | { |
98 | uint32_t viph_control; | 131 | uint32_t viph_control; |
@@ -388,16 +421,16 @@ static bool radeon_read_disabled_bios(struct radeon_device *rdev) | |||
388 | return legacy_read_disabled_bios(rdev); | 421 | return legacy_read_disabled_bios(rdev); |
389 | } | 422 | } |
390 | 423 | ||
424 | |||
391 | bool radeon_get_bios(struct radeon_device *rdev) | 425 | bool radeon_get_bios(struct radeon_device *rdev) |
392 | { | 426 | { |
393 | bool r; | 427 | bool r; |
394 | uint16_t tmp; | 428 | uint16_t tmp; |
395 | 429 | ||
396 | if (rdev->flags & RADEON_IS_IGP) { | 430 | r = radeon_atrm_get_bios(rdev); |
431 | if (r == false) | ||
397 | r = igp_read_bios_from_vram(rdev); | 432 | r = igp_read_bios_from_vram(rdev); |
398 | if (r == false) | 433 | if (r == false) |
399 | r = radeon_read_bios(rdev); | ||
400 | } else | ||
401 | r = radeon_read_bios(rdev); | 434 | r = radeon_read_bios(rdev); |
402 | if (r == false) { | 435 | if (r == false) { |
403 | r = radeon_read_disabled_bios(rdev); | 436 | r = radeon_read_disabled_bios(rdev); |
@@ -408,6 +441,7 @@ bool radeon_get_bios(struct radeon_device *rdev) | |||
408 | return false; | 441 | return false; |
409 | } | 442 | } |
410 | if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { | 443 | if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { |
444 | printk("BIOS signature incorrect %x %x\n", rdev->bios[0], rdev->bios[1]); | ||
411 | goto free_bios; | 445 | goto free_bios; |
412 | } | 446 | } |
413 | 447 | ||
diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c index 768b1509fa03..cb8d9a1dd69c 100644 --- a/drivers/gpu/drm/radeon/radeon_device.c +++ b/drivers/gpu/drm/radeon/radeon_device.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include <drm/drm_crtc_helper.h> | 30 | #include <drm/drm_crtc_helper.h> |
31 | #include <drm/radeon_drm.h> | 31 | #include <drm/radeon_drm.h> |
32 | #include <linux/vgaarb.h> | 32 | #include <linux/vgaarb.h> |
33 | #include <linux/vga_switcheroo.h> | ||
33 | #include "radeon_reg.h" | 34 | #include "radeon_reg.h" |
34 | #include "radeon.h" | 35 | #include "radeon.h" |
35 | #include "radeon_asic.h" | 36 | #include "radeon_asic.h" |
@@ -613,6 +614,36 @@ void radeon_check_arguments(struct radeon_device *rdev) | |||
613 | } | 614 | } |
614 | } | 615 | } |
615 | 616 | ||
617 | static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) | ||
618 | { | ||
619 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
620 | struct radeon_device *rdev = dev->dev_private; | ||
621 | pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; | ||
622 | if (state == VGA_SWITCHEROO_ON) { | ||
623 | printk(KERN_INFO "radeon: switched on\n"); | ||
624 | /* don't suspend or resume card normally */ | ||
625 | rdev->powered_down = false; | ||
626 | radeon_resume_kms(dev); | ||
627 | } else { | ||
628 | printk(KERN_INFO "radeon: switched off\n"); | ||
629 | radeon_suspend_kms(dev, pmm); | ||
630 | /* don't suspend or resume card normally */ | ||
631 | rdev->powered_down = true; | ||
632 | } | ||
633 | } | ||
634 | |||
635 | static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) | ||
636 | { | ||
637 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
638 | bool can_switch; | ||
639 | |||
640 | spin_lock(&dev->count_lock); | ||
641 | can_switch = (dev->open_count == 0); | ||
642 | spin_unlock(&dev->count_lock); | ||
643 | return can_switch; | ||
644 | } | ||
645 | |||
646 | |||
616 | int radeon_device_init(struct radeon_device *rdev, | 647 | int radeon_device_init(struct radeon_device *rdev, |
617 | struct drm_device *ddev, | 648 | struct drm_device *ddev, |
618 | struct pci_dev *pdev, | 649 | struct pci_dev *pdev, |
@@ -692,6 +723,9 @@ int radeon_device_init(struct radeon_device *rdev, | |||
692 | /* this will fail for cards that aren't VGA class devices, just | 723 | /* this will fail for cards that aren't VGA class devices, just |
693 | * ignore it */ | 724 | * ignore it */ |
694 | vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); | 725 | vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); |
726 | vga_switcheroo_register_client(rdev->pdev, | ||
727 | radeon_switcheroo_set_state, | ||
728 | radeon_switcheroo_can_switch); | ||
695 | 729 | ||
696 | r = radeon_init(rdev); | 730 | r = radeon_init(rdev); |
697 | if (r) | 731 | if (r) |
@@ -723,6 +757,7 @@ void radeon_device_fini(struct radeon_device *rdev) | |||
723 | rdev->shutdown = true; | 757 | rdev->shutdown = true; |
724 | radeon_fini(rdev); | 758 | radeon_fini(rdev); |
725 | destroy_workqueue(rdev->wq); | 759 | destroy_workqueue(rdev->wq); |
760 | vga_switcheroo_unregister_client(rdev->pdev); | ||
726 | vga_client_register(rdev->pdev, NULL, NULL, NULL); | 761 | vga_client_register(rdev->pdev, NULL, NULL, NULL); |
727 | iounmap(rdev->rmmio); | 762 | iounmap(rdev->rmmio); |
728 | rdev->rmmio = NULL; | 763 | rdev->rmmio = NULL; |
@@ -746,6 +781,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) | |||
746 | } | 781 | } |
747 | rdev = dev->dev_private; | 782 | rdev = dev->dev_private; |
748 | 783 | ||
784 | if (rdev->powered_down) | ||
785 | return 0; | ||
749 | /* unpin the front buffers */ | 786 | /* unpin the front buffers */ |
750 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 787 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
751 | struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); | 788 | struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); |
@@ -791,6 +828,9 @@ int radeon_resume_kms(struct drm_device *dev) | |||
791 | { | 828 | { |
792 | struct radeon_device *rdev = dev->dev_private; | 829 | struct radeon_device *rdev = dev->dev_private; |
793 | 830 | ||
831 | if (rdev->powered_down) | ||
832 | return 0; | ||
833 | |||
794 | acquire_console_sem(); | 834 | acquire_console_sem(); |
795 | pci_set_power_state(dev->pdev, PCI_D0); | 835 | pci_set_power_state(dev->pdev, PCI_D0); |
796 | pci_restore_state(dev->pdev); | 836 | pci_restore_state(dev->pdev); |
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 8ba3de7994d4..4ab53aa163b2 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c | |||
@@ -339,6 +339,7 @@ static int __init radeon_init(void) | |||
339 | driver = &kms_driver; | 339 | driver = &kms_driver; |
340 | driver->driver_features |= DRIVER_MODESET; | 340 | driver->driver_features |= DRIVER_MODESET; |
341 | driver->num_ioctls = radeon_max_kms_ioctl; | 341 | driver->num_ioctls = radeon_max_kms_ioctl; |
342 | radeon_register_atpx_handler(); | ||
342 | } | 343 | } |
343 | /* if the vga console setting is enabled still | 344 | /* if the vga console setting is enabled still |
344 | * let modprobe override it */ | 345 | * let modprobe override it */ |
@@ -348,6 +349,7 @@ static int __init radeon_init(void) | |||
348 | static void __exit radeon_exit(void) | 349 | static void __exit radeon_exit(void) |
349 | { | 350 | { |
350 | drm_exit(driver); | 351 | drm_exit(driver); |
352 | radeon_unregister_atpx_handler(); | ||
351 | } | 353 | } |
352 | 354 | ||
353 | module_init(radeon_init); | 355 | module_init(radeon_init); |
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index c57ad606504d..736237195143 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h | |||
@@ -455,6 +455,9 @@ extern void r600_blit_swap(struct drm_device *dev, | |||
455 | int sx, int sy, int dx, int dy, | 455 | int sx, int sy, int dx, int dy, |
456 | int w, int h, int src_pitch, int dst_pitch, int cpp); | 456 | int w, int h, int src_pitch, int dst_pitch, int cpp); |
457 | 457 | ||
458 | /* atpx handler */ | ||
459 | void radeon_register_atpx_handler(void); | ||
460 | void radeon_unregister_atpx_handler(void); | ||
458 | /* Flags for stats.boxes | 461 | /* Flags for stats.boxes |
459 | */ | 462 | */ |
460 | #define RADEON_BOX_DMA_IDLE 0x1 | 463 | #define RADEON_BOX_DMA_IDLE 0x1 |
diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c index d71e346e9ab5..561719223988 100644 --- a/drivers/gpu/drm/radeon/radeon_fb.c +++ b/drivers/gpu/drm/radeon/radeon_fb.c | |||
@@ -39,6 +39,8 @@ | |||
39 | 39 | ||
40 | #include "drm_fb_helper.h" | 40 | #include "drm_fb_helper.h" |
41 | 41 | ||
42 | #include <linux/vga_switcheroo.h> | ||
43 | |||
42 | struct radeon_fb_device { | 44 | struct radeon_fb_device { |
43 | struct drm_fb_helper helper; | 45 | struct drm_fb_helper helper; |
44 | struct radeon_framebuffer *rfb; | 46 | struct radeon_framebuffer *rfb; |
@@ -291,6 +293,7 @@ int radeonfb_create(struct drm_device *dev, | |||
291 | rfbdev->rdev = rdev; | 293 | rfbdev->rdev = rdev; |
292 | 294 | ||
293 | mutex_unlock(&rdev->ddev->struct_mutex); | 295 | mutex_unlock(&rdev->ddev->struct_mutex); |
296 | vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); | ||
294 | return 0; | 297 | return 0; |
295 | 298 | ||
296 | out_unref: | 299 | out_unref: |
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index f23b05606eb5..5db7af6b91f4 100644 --- a/drivers/gpu/drm/radeon/radeon_kms.c +++ b/drivers/gpu/drm/radeon/radeon_kms.c | |||
@@ -30,6 +30,8 @@ | |||
30 | #include "radeon.h" | 30 | #include "radeon.h" |
31 | #include "radeon_drm.h" | 31 | #include "radeon_drm.h" |
32 | 32 | ||
33 | #include <linux/vga_switcheroo.h> | ||
34 | |||
33 | int radeon_driver_unload_kms(struct drm_device *dev) | 35 | int radeon_driver_unload_kms(struct drm_device *dev) |
34 | { | 36 | { |
35 | struct radeon_device *rdev = dev->dev_private; | 37 | struct radeon_device *rdev = dev->dev_private; |
@@ -136,6 +138,7 @@ int radeon_driver_firstopen_kms(struct drm_device *dev) | |||
136 | 138 | ||
137 | void radeon_driver_lastclose_kms(struct drm_device *dev) | 139 | void radeon_driver_lastclose_kms(struct drm_device *dev) |
138 | { | 140 | { |
141 | vga_switcheroo_process_delayed_switch(); | ||
139 | } | 142 | } |
140 | 143 | ||
141 | int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) | 144 | int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) |