diff options
author | Dave Airlie <airlied@redhat.com> | 2010-03-01 01:22:38 -0500 |
---|---|---|
committer | Dave Airlie <airlied@redhat.com> | 2010-03-01 01:22:38 -0500 |
commit | 1c62233508ef7104f8a78e571fdf5c72d0dc0200 (patch) | |
tree | 31e19cbff5c1080d3015d20b24dd43ee95f4ed8c /drivers/gpu | |
parent | 6d9c13513661c1991bf5f4e6e1363c721292d819 (diff) | |
parent | 6a9ee8af344e3bd7dbd61e67037096cdf7f83289 (diff) |
Merge branch 'gpu-switcher' of /ssd/git//linux-2.6 into drm-next-stage
* 'gpu-switcher' of /ssd/git//linux-2.6:
vga_switcheroo: initial implementation (v15)
fb: for framebuffer handover don't exit the loop early.
Conflicts:
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/radeon/Makefile
drivers/gpu/drm/radeon/radeon.h
Diffstat (limited to 'drivers/gpu')
-rw-r--r-- | drivers/gpu/drm/i915/i915_dma.c | 35 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.c | 4 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/i915_drv.h | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/i915/intel_fb.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_acpi.c | 160 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.c | 9 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drv.h | 19 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_fbcon.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_state.c | 32 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/Makefile | 2 | ||||
-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 | ||||
-rw-r--r-- | drivers/gpu/vga/Kconfig | 13 | ||||
-rw-r--r-- | drivers/gpu/vga/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpu/vga/vga_switcheroo.c | 453 |
21 files changed, 1025 insertions, 70 deletions
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c index 3e658d6a6b7d..8bfc0bbf13e6 100644 --- a/drivers/gpu/drm/i915/i915_dma.c +++ b/drivers/gpu/drm/i915/i915_dma.c | |||
@@ -37,6 +37,7 @@ | |||
37 | #include <linux/vgaarb.h> | 37 | #include <linux/vgaarb.h> |
38 | #include <linux/acpi.h> | 38 | #include <linux/acpi.h> |
39 | #include <linux/pnp.h> | 39 | #include <linux/pnp.h> |
40 | #include <linux/vga_switcheroo.h> | ||
40 | 41 | ||
41 | /* Really want an OS-independent resettable timer. Would like to have | 42 | /* Really want an OS-independent resettable timer. Would like to have |
42 | * this loop run for (eg) 3 sec, but have the timer reset every time | 43 | * this loop run for (eg) 3 sec, but have the timer reset every time |
@@ -1381,6 +1382,32 @@ static unsigned int i915_vga_set_decode(void *cookie, bool state) | |||
1381 | return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; | 1382 | return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; |
1382 | } | 1383 | } |
1383 | 1384 | ||
1385 | static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) | ||
1386 | { | ||
1387 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
1388 | pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; | ||
1389 | if (state == VGA_SWITCHEROO_ON) { | ||
1390 | printk(KERN_INFO "i915: switched off\n"); | ||
1391 | /* i915 resume handler doesn't set to D0 */ | ||
1392 | pci_set_power_state(dev->pdev, PCI_D0); | ||
1393 | i915_resume(dev); | ||
1394 | } else { | ||
1395 | printk(KERN_ERR "i915: switched off\n"); | ||
1396 | i915_suspend(dev, pmm); | ||
1397 | } | ||
1398 | } | ||
1399 | |||
1400 | static bool i915_switcheroo_can_switch(struct pci_dev *pdev) | ||
1401 | { | ||
1402 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
1403 | bool can_switch; | ||
1404 | |||
1405 | spin_lock(&dev->count_lock); | ||
1406 | can_switch = (dev->open_count == 0); | ||
1407 | spin_unlock(&dev->count_lock); | ||
1408 | return can_switch; | ||
1409 | } | ||
1410 | |||
1384 | static int i915_load_modeset_init(struct drm_device *dev, | 1411 | static int i915_load_modeset_init(struct drm_device *dev, |
1385 | unsigned long prealloc_start, | 1412 | unsigned long prealloc_start, |
1386 | unsigned long prealloc_size, | 1413 | unsigned long prealloc_size, |
@@ -1442,6 +1469,12 @@ static int i915_load_modeset_init(struct drm_device *dev, | |||
1442 | if (ret) | 1469 | if (ret) |
1443 | goto destroy_ringbuffer; | 1470 | goto destroy_ringbuffer; |
1444 | 1471 | ||
1472 | ret = vga_switcheroo_register_client(dev->pdev, | ||
1473 | i915_switcheroo_set_state, | ||
1474 | i915_switcheroo_can_switch); | ||
1475 | if (ret) | ||
1476 | goto destroy_ringbuffer; | ||
1477 | |||
1445 | intel_modeset_init(dev); | 1478 | intel_modeset_init(dev); |
1446 | 1479 | ||
1447 | ret = drm_irq_install(dev); | 1480 | ret = drm_irq_install(dev); |
@@ -1733,6 +1766,7 @@ int i915_driver_unload(struct drm_device *dev) | |||
1733 | dev_priv->child_dev_num = 0; | 1766 | dev_priv->child_dev_num = 0; |
1734 | } | 1767 | } |
1735 | drm_irq_uninstall(dev); | 1768 | drm_irq_uninstall(dev); |
1769 | vga_switcheroo_unregister_client(dev->pdev); | ||
1736 | vga_client_register(dev->pdev, NULL, NULL, NULL); | 1770 | vga_client_register(dev->pdev, NULL, NULL, NULL); |
1737 | } | 1771 | } |
1738 | 1772 | ||
@@ -1802,6 +1836,7 @@ void i915_driver_lastclose(struct drm_device * dev) | |||
1802 | 1836 | ||
1803 | if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) { | 1837 | if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) { |
1804 | drm_fb_helper_restore(); | 1838 | drm_fb_helper_restore(); |
1839 | vga_switcheroo_process_delayed_switch(); | ||
1805 | return; | 1840 | return; |
1806 | } | 1841 | } |
1807 | 1842 | ||
diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c index 85ad020125c8..1b2e95455c05 100644 --- a/drivers/gpu/drm/i915/i915_drv.c +++ b/drivers/gpu/drm/i915/i915_drv.c | |||
@@ -214,7 +214,7 @@ static int i915_drm_freeze(struct drm_device *dev) | |||
214 | return 0; | 214 | return 0; |
215 | } | 215 | } |
216 | 216 | ||
217 | static int i915_suspend(struct drm_device *dev, pm_message_t state) | 217 | int i915_suspend(struct drm_device *dev, pm_message_t state) |
218 | { | 218 | { |
219 | int error; | 219 | int error; |
220 | 220 | ||
@@ -268,7 +268,7 @@ static int i915_drm_thaw(struct drm_device *dev) | |||
268 | return error; | 268 | return error; |
269 | } | 269 | } |
270 | 270 | ||
271 | static int i915_resume(struct drm_device *dev) | 271 | int i915_resume(struct drm_device *dev) |
272 | { | 272 | { |
273 | if (pci_enable_device(dev->pdev)) | 273 | if (pci_enable_device(dev->pdev)) |
274 | return -EIO; | 274 | return -EIO; |
diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h index f97592609da4..979439cfb827 100644 --- a/drivers/gpu/drm/i915/i915_drv.h +++ b/drivers/gpu/drm/i915/i915_drv.h | |||
@@ -773,6 +773,8 @@ extern unsigned int i915_fbpercrtc; | |||
773 | extern unsigned int i915_powersave; | 773 | extern unsigned int i915_powersave; |
774 | extern unsigned int i915_lvds_downclock; | 774 | extern unsigned int i915_lvds_downclock; |
775 | 775 | ||
776 | extern int i915_suspend(struct drm_device *dev, pm_message_t state); | ||
777 | extern int i915_resume(struct drm_device *dev); | ||
776 | extern void i915_save_display(struct drm_device *dev); | 778 | extern void i915_save_display(struct drm_device *dev); |
777 | extern void i915_restore_display(struct drm_device *dev); | 779 | extern void i915_restore_display(struct drm_device *dev); |
778 | extern int i915_master_create(struct drm_device *dev, struct drm_master *master); | 780 | extern int i915_master_create(struct drm_device *dev, struct drm_master *master); |
diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c index aaabbcbe5905..8cd791dc5b29 100644 --- a/drivers/gpu/drm/i915/intel_fb.c +++ b/drivers/gpu/drm/i915/intel_fb.c | |||
@@ -35,6 +35,7 @@ | |||
35 | #include <linux/delay.h> | 35 | #include <linux/delay.h> |
36 | #include <linux/fb.h> | 36 | #include <linux/fb.h> |
37 | #include <linux/init.h> | 37 | #include <linux/init.h> |
38 | #include <linux/vga_switcheroo.h> | ||
38 | 39 | ||
39 | #include "drmP.h" | 40 | #include "drmP.h" |
40 | #include "drm.h" | 41 | #include "drm.h" |
@@ -235,6 +236,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width, | |||
235 | obj_priv->gtt_offset, fbo); | 236 | obj_priv->gtt_offset, fbo); |
236 | 237 | ||
237 | mutex_unlock(&dev->struct_mutex); | 238 | mutex_unlock(&dev->struct_mutex); |
239 | vga_switcheroo_client_fb_set(dev->pdev, info); | ||
238 | return 0; | 240 | return 0; |
239 | 241 | ||
240 | out_unpin: | 242 | out_unpin: |
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 | } | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c index 874adf55a43f..30cc09e8a709 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.c +++ b/drivers/gpu/drm/nouveau/nouveau_drv.c | |||
@@ -135,7 +135,7 @@ nouveau_pci_remove(struct pci_dev *pdev) | |||
135 | drm_put_dev(dev); | 135 | drm_put_dev(dev); |
136 | } | 136 | } |
137 | 137 | ||
138 | static int | 138 | int |
139 | nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) | 139 | nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) |
140 | { | 140 | { |
141 | struct drm_device *dev = pci_get_drvdata(pdev); | 141 | struct drm_device *dev = pci_get_drvdata(pdev); |
@@ -233,7 +233,7 @@ out_abort: | |||
233 | return ret; | 233 | return ret; |
234 | } | 234 | } |
235 | 235 | ||
236 | static int | 236 | int |
237 | nouveau_pci_resume(struct pci_dev *pdev) | 237 | nouveau_pci_resume(struct pci_dev *pdev) |
238 | { | 238 | { |
239 | struct drm_device *dev = pci_get_drvdata(pdev); | 239 | struct drm_device *dev = pci_get_drvdata(pdev); |
@@ -402,8 +402,10 @@ static int __init nouveau_init(void) | |||
402 | nouveau_modeset = 1; | 402 | nouveau_modeset = 1; |
403 | } | 403 | } |
404 | 404 | ||
405 | if (nouveau_modeset == 1) | 405 | if (nouveau_modeset == 1) { |
406 | driver.driver_features |= DRIVER_MODESET; | 406 | driver.driver_features |= DRIVER_MODESET; |
407 | nouveau_register_dsm_handler(); | ||
408 | } | ||
407 | 409 | ||
408 | return drm_init(&driver); | 410 | return drm_init(&driver); |
409 | } | 411 | } |
@@ -411,6 +413,7 @@ static int __init nouveau_init(void) | |||
411 | static void __exit nouveau_exit(void) | 413 | static void __exit nouveau_exit(void) |
412 | { | 414 | { |
413 | drm_exit(&driver); | 415 | drm_exit(&driver); |
416 | nouveau_unregister_dsm_handler(); | ||
414 | } | 417 | } |
415 | 418 | ||
416 | module_init(nouveau_init); | 419 | module_init(nouveau_init); |
diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h index 2f8ce42f0725..a2e24f252e84 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drv.h +++ b/drivers/gpu/drm/nouveau/nouveau_drv.h | |||
@@ -622,7 +622,6 @@ struct drm_nouveau_private { | |||
622 | } susres; | 622 | } susres; |
623 | 623 | ||
624 | struct backlight_device *backlight; | 624 | struct backlight_device *backlight; |
625 | bool acpi_dsm; | ||
626 | 625 | ||
627 | struct nouveau_channel *evo; | 626 | struct nouveau_channel *evo; |
628 | 627 | ||
@@ -690,6 +689,9 @@ extern int nouveau_ignorelid; | |||
690 | extern int nouveau_nofbaccel; | 689 | extern int nouveau_nofbaccel; |
691 | extern int nouveau_noaccel; | 690 | extern int nouveau_noaccel; |
692 | 691 | ||
692 | extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); | ||
693 | extern int nouveau_pci_resume(struct pci_dev *pdev); | ||
694 | |||
693 | /* nouveau_state.c */ | 695 | /* nouveau_state.c */ |
694 | extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); | 696 | extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); |
695 | extern int nouveau_load(struct drm_device *, unsigned long flags); | 697 | extern int nouveau_load(struct drm_device *, unsigned long flags); |
@@ -850,19 +852,8 @@ extern int nouveau_dma_init(struct nouveau_channel *); | |||
850 | extern int nouveau_dma_wait(struct nouveau_channel *, int slots, int size); | 852 | extern int nouveau_dma_wait(struct nouveau_channel *, int slots, int size); |
851 | 853 | ||
852 | /* nouveau_acpi.c */ | 854 | /* nouveau_acpi.c */ |
853 | #ifdef CONFIG_ACPI | 855 | void nouveau_register_dsm_handler(void); |
854 | extern int nouveau_hybrid_setup(struct drm_device *dev); | 856 | void nouveau_unregister_dsm_handler(void); |
855 | extern bool nouveau_dsm_probe(struct drm_device *dev); | ||
856 | #else | ||
857 | static inline int nouveau_hybrid_setup(struct drm_device *dev) | ||
858 | { | ||
859 | return 0; | ||
860 | } | ||
861 | static inline bool nouveau_dsm_probe(struct drm_device *dev) | ||
862 | { | ||
863 | return false; | ||
864 | } | ||
865 | #endif | ||
866 | 857 | ||
867 | /* nouveau_backlight.c */ | 858 | /* nouveau_backlight.c */ |
868 | #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT | 859 | #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT |
diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c index d48c59cdefe4..68cedd9194fe 100644 --- a/drivers/gpu/drm/nouveau/nouveau_fbcon.c +++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c | |||
@@ -36,6 +36,7 @@ | |||
36 | #include <linux/fb.h> | 36 | #include <linux/fb.h> |
37 | #include <linux/init.h> | 37 | #include <linux/init.h> |
38 | #include <linux/screen_info.h> | 38 | #include <linux/screen_info.h> |
39 | #include <linux/vga_switcheroo.h> | ||
39 | 40 | ||
40 | #include "drmP.h" | 41 | #include "drmP.h" |
41 | #include "drm.h" | 42 | #include "drm.h" |
@@ -370,6 +371,7 @@ nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width, | |||
370 | nvbo->bo.offset, nvbo); | 371 | nvbo->bo.offset, nvbo); |
371 | 372 | ||
372 | mutex_unlock(&dev->struct_mutex); | 373 | mutex_unlock(&dev->struct_mutex); |
374 | vga_switcheroo_client_fb_set(dev->pdev, info); | ||
373 | return 0; | 375 | return 0; |
374 | 376 | ||
375 | out_unref: | 377 | out_unref: |
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c index 516a8d36cb10..eb8f084d5f53 100644 --- a/drivers/gpu/drm/nouveau/nouveau_state.c +++ b/drivers/gpu/drm/nouveau/nouveau_state.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include "drm_sarea.h" | 29 | #include "drm_sarea.h" |
30 | #include "drm_crtc_helper.h" | 30 | #include "drm_crtc_helper.h" |
31 | #include <linux/vgaarb.h> | 31 | #include <linux/vgaarb.h> |
32 | #include <linux/vga_switcheroo.h> | ||
32 | 33 | ||
33 | #include "nouveau_drv.h" | 34 | #include "nouveau_drv.h" |
34 | #include "nouveau_drm.h" | 35 | #include "nouveau_drm.h" |
@@ -371,6 +372,30 @@ out_err: | |||
371 | return ret; | 372 | return ret; |
372 | } | 373 | } |
373 | 374 | ||
375 | static void nouveau_switcheroo_set_state(struct pci_dev *pdev, | ||
376 | enum vga_switcheroo_state state) | ||
377 | { | ||
378 | pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; | ||
379 | if (state == VGA_SWITCHEROO_ON) { | ||
380 | printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); | ||
381 | nouveau_pci_resume(pdev); | ||
382 | } else { | ||
383 | printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); | ||
384 | nouveau_pci_suspend(pdev, pmm); | ||
385 | } | ||
386 | } | ||
387 | |||
388 | static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev) | ||
389 | { | ||
390 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
391 | bool can_switch; | ||
392 | |||
393 | spin_lock(&dev->count_lock); | ||
394 | can_switch = (dev->open_count == 0); | ||
395 | spin_unlock(&dev->count_lock); | ||
396 | return can_switch; | ||
397 | } | ||
398 | |||
374 | int | 399 | int |
375 | nouveau_card_init(struct drm_device *dev) | 400 | nouveau_card_init(struct drm_device *dev) |
376 | { | 401 | { |
@@ -384,6 +409,8 @@ nouveau_card_init(struct drm_device *dev) | |||
384 | return 0; | 409 | return 0; |
385 | 410 | ||
386 | vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); | 411 | vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); |
412 | vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state, | ||
413 | nouveau_switcheroo_can_switch); | ||
387 | 414 | ||
388 | /* Initialise internal driver API hooks */ | 415 | /* Initialise internal driver API hooks */ |
389 | ret = nouveau_init_engine_ptrs(dev); | 416 | ret = nouveau_init_engine_ptrs(dev); |
@@ -618,11 +645,6 @@ int nouveau_load(struct drm_device *dev, unsigned long flags) | |||
618 | NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", | 645 | NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", |
619 | dev->pci_vendor, dev->pci_device, dev->pdev->class); | 646 | dev->pci_vendor, dev->pci_device, dev->pdev->class); |
620 | 647 | ||
621 | dev_priv->acpi_dsm = nouveau_dsm_probe(dev); | ||
622 | |||
623 | if (dev_priv->acpi_dsm) | ||
624 | nouveau_hybrid_setup(dev); | ||
625 | |||
626 | dev_priv->wq = create_workqueue("nouveau"); | 648 | dev_priv->wq = create_workqueue("nouveau"); |
627 | if (!dev_priv->wq) | 649 | if (!dev_priv->wq) |
628 | return -EINVAL; | 650 | return -EINVAL; |
diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile index b46f115d1c25..0a4d526e4f44 100644 --- a/drivers/gpu/drm/radeon/Makefile +++ b/drivers/gpu/drm/radeon/Makefile | |||
@@ -60,7 +60,7 @@ radeon-y += radeon_device.o radeon_kms.o \ | |||
60 | rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ | 60 | rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ |
61 | r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ | 61 | r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ |
62 | r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \ | 62 | r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \ |
63 | evergreen.o | 63 | evergreen.o radeon_atpx_handler.o |
64 | 64 | ||
65 | radeon-$(CONFIG_COMPAT) += radeon_ioc32.o | 65 | radeon-$(CONFIG_COMPAT) += radeon_ioc32.o |
66 | 66 | ||
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h index 2434d553bbbc..ad9d55f94398 100644 --- a/drivers/gpu/drm/radeon/radeon.h +++ b/drivers/gpu/drm/radeon/radeon.h | |||
@@ -119,6 +119,10 @@ struct radeon_device; | |||
119 | /* | 119 | /* |
120 | * BIOS. | 120 | * BIOS. |
121 | */ | 121 | */ |
122 | #define ATRM_BIOS_PAGE 4096 | ||
123 | |||
124 | bool radeon_atrm_supported(struct pci_dev *pdev); | ||
125 | int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len); | ||
122 | bool radeon_get_bios(struct radeon_device *rdev); | 126 | bool radeon_get_bios(struct radeon_device *rdev); |
123 | 127 | ||
124 | 128 | ||
@@ -957,6 +961,8 @@ struct radeon_device { | |||
957 | int audio_bits_per_sample; | 961 | int audio_bits_per_sample; |
958 | uint8_t audio_status_bits; | 962 | uint8_t audio_status_bits; |
959 | uint8_t audio_category_code; | 963 | uint8_t audio_category_code; |
964 | |||
965 | bool powered_down; | ||
960 | }; | 966 | }; |
961 | 967 | ||
962 | int radeon_device_init(struct radeon_device *rdev, | 968 | int radeon_device_init(struct radeon_device *rdev, |
@@ -1167,6 +1173,8 @@ extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); | |||
1167 | extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); | 1173 | extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); |
1168 | extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base); | 1174 | extern void radeon_vram_location(struct radeon_device *rdev, struct radeon_mc *mc, u64 base); |
1169 | extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); | 1175 | extern void radeon_gtt_location(struct radeon_device *rdev, struct radeon_mc *mc); |
1176 | extern int radeon_resume_kms(struct drm_device *dev); | ||
1177 | extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state); | ||
1170 | 1178 | ||
1171 | /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ | 1179 | /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ |
1172 | struct r100_mc_save { | 1180 | 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 26856ed8d972..557240460526 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 91a9b966238e..e28e4ed5f720 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" |
@@ -655,6 +656,36 @@ void radeon_check_arguments(struct radeon_device *rdev) | |||
655 | } | 656 | } |
656 | } | 657 | } |
657 | 658 | ||
659 | static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) | ||
660 | { | ||
661 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
662 | struct radeon_device *rdev = dev->dev_private; | ||
663 | pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; | ||
664 | if (state == VGA_SWITCHEROO_ON) { | ||
665 | printk(KERN_INFO "radeon: switched on\n"); | ||
666 | /* don't suspend or resume card normally */ | ||
667 | rdev->powered_down = false; | ||
668 | radeon_resume_kms(dev); | ||
669 | } else { | ||
670 | printk(KERN_INFO "radeon: switched off\n"); | ||
671 | radeon_suspend_kms(dev, pmm); | ||
672 | /* don't suspend or resume card normally */ | ||
673 | rdev->powered_down = true; | ||
674 | } | ||
675 | } | ||
676 | |||
677 | static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) | ||
678 | { | ||
679 | struct drm_device *dev = pci_get_drvdata(pdev); | ||
680 | bool can_switch; | ||
681 | |||
682 | spin_lock(&dev->count_lock); | ||
683 | can_switch = (dev->open_count == 0); | ||
684 | spin_unlock(&dev->count_lock); | ||
685 | return can_switch; | ||
686 | } | ||
687 | |||
688 | |||
658 | int radeon_device_init(struct radeon_device *rdev, | 689 | int radeon_device_init(struct radeon_device *rdev, |
659 | struct drm_device *ddev, | 690 | struct drm_device *ddev, |
660 | struct pci_dev *pdev, | 691 | struct pci_dev *pdev, |
@@ -737,6 +768,9 @@ int radeon_device_init(struct radeon_device *rdev, | |||
737 | /* this will fail for cards that aren't VGA class devices, just | 768 | /* this will fail for cards that aren't VGA class devices, just |
738 | * ignore it */ | 769 | * ignore it */ |
739 | vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); | 770 | vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); |
771 | vga_switcheroo_register_client(rdev->pdev, | ||
772 | radeon_switcheroo_set_state, | ||
773 | radeon_switcheroo_can_switch); | ||
740 | 774 | ||
741 | r = radeon_init(rdev); | 775 | r = radeon_init(rdev); |
742 | if (r) | 776 | if (r) |
@@ -768,6 +802,7 @@ void radeon_device_fini(struct radeon_device *rdev) | |||
768 | rdev->shutdown = true; | 802 | rdev->shutdown = true; |
769 | radeon_fini(rdev); | 803 | radeon_fini(rdev); |
770 | destroy_workqueue(rdev->wq); | 804 | destroy_workqueue(rdev->wq); |
805 | vga_switcheroo_unregister_client(rdev->pdev); | ||
771 | vga_client_register(rdev->pdev, NULL, NULL, NULL); | 806 | vga_client_register(rdev->pdev, NULL, NULL, NULL); |
772 | iounmap(rdev->rmmio); | 807 | iounmap(rdev->rmmio); |
773 | rdev->rmmio = NULL; | 808 | rdev->rmmio = NULL; |
@@ -791,6 +826,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state) | |||
791 | } | 826 | } |
792 | rdev = dev->dev_private; | 827 | rdev = dev->dev_private; |
793 | 828 | ||
829 | if (rdev->powered_down) | ||
830 | return 0; | ||
794 | /* unpin the front buffers */ | 831 | /* unpin the front buffers */ |
795 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 832 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
796 | struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); | 833 | struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); |
@@ -836,6 +873,9 @@ int radeon_resume_kms(struct drm_device *dev) | |||
836 | { | 873 | { |
837 | struct radeon_device *rdev = dev->dev_private; | 874 | struct radeon_device *rdev = dev->dev_private; |
838 | 875 | ||
876 | if (rdev->powered_down) | ||
877 | return 0; | ||
878 | |||
839 | acquire_console_sem(); | 879 | acquire_console_sem(); |
840 | pci_set_power_state(dev->pdev, PCI_D0); | 880 | pci_set_power_state(dev->pdev, PCI_D0); |
841 | pci_restore_state(dev->pdev); | 881 | pci_restore_state(dev->pdev); |
diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index be99d4e55a34..f6456e02d1fe 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c | |||
@@ -343,6 +343,7 @@ static int __init radeon_init(void) | |||
343 | driver = &kms_driver; | 343 | driver = &kms_driver; |
344 | driver->driver_features |= DRIVER_MODESET; | 344 | driver->driver_features |= DRIVER_MODESET; |
345 | driver->num_ioctls = radeon_max_kms_ioctl; | 345 | driver->num_ioctls = radeon_max_kms_ioctl; |
346 | radeon_register_atpx_handler(); | ||
346 | } | 347 | } |
347 | /* if the vga console setting is enabled still | 348 | /* if the vga console setting is enabled still |
348 | * let modprobe override it */ | 349 | * let modprobe override it */ |
@@ -352,6 +353,7 @@ static int __init radeon_init(void) | |||
352 | static void __exit radeon_exit(void) | 353 | static void __exit radeon_exit(void) |
353 | { | 354 | { |
354 | drm_exit(driver); | 355 | drm_exit(driver); |
356 | radeon_unregister_atpx_handler(); | ||
355 | } | 357 | } |
356 | 358 | ||
357 | module_init(radeon_init); | 359 | module_init(radeon_init); |
diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h index cf911859eac2..4fe16461bb1b 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.h +++ b/drivers/gpu/drm/radeon/radeon_drv.h | |||
@@ -462,6 +462,9 @@ extern void r600_blit_swap(struct drm_device *dev, | |||
462 | int sx, int sy, int dx, int dy, | 462 | int sx, int sy, int dx, int dy, |
463 | int w, int h, int src_pitch, int dst_pitch, int cpp); | 463 | int w, int h, int src_pitch, int dst_pitch, int cpp); |
464 | 464 | ||
465 | /* atpx handler */ | ||
466 | void radeon_register_atpx_handler(void); | ||
467 | void radeon_unregister_atpx_handler(void); | ||
465 | /* Flags for stats.boxes | 468 | /* Flags for stats.boxes |
466 | */ | 469 | */ |
467 | #define RADEON_BOX_DMA_IDLE 0x1 | 470 | #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 cda112cc7a6c..8fccbf29235e 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; |
@@ -286,6 +288,7 @@ int radeonfb_create(struct drm_device *dev, | |||
286 | rfbdev->rdev = rdev; | 288 | rfbdev->rdev = rdev; |
287 | 289 | ||
288 | mutex_unlock(&rdev->ddev->struct_mutex); | 290 | mutex_unlock(&rdev->ddev->struct_mutex); |
291 | vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); | ||
289 | return 0; | 292 | return 0; |
290 | 293 | ||
291 | out_unref: | 294 | out_unref: |
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c index 3c5002ea3f8f..20ec276e7596 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) |
diff --git a/drivers/gpu/vga/Kconfig b/drivers/gpu/vga/Kconfig index 790e675b13eb..6116a0196214 100644 --- a/drivers/gpu/vga/Kconfig +++ b/drivers/gpu/vga/Kconfig | |||
@@ -8,3 +8,16 @@ config VGA_ARB | |||
8 | are accessed at same time they need some kind of coordination. Please | 8 | are accessed at same time they need some kind of coordination. Please |
9 | see Documentation/vgaarbiter.txt for more details. Select this to | 9 | see Documentation/vgaarbiter.txt for more details. Select this to |
10 | enable VGA arbiter. | 10 | enable VGA arbiter. |
11 | |||
12 | config VGA_SWITCHEROO | ||
13 | bool "Laptop Hybrid Grapics - GPU switching support" | ||
14 | default y | ||
15 | depends on X86 | ||
16 | depends on ACPI | ||
17 | help | ||
18 | Many laptops released in 2008/9/10 have two gpus with a multiplxer | ||
19 | to switch between them. This adds support for dynamic switching when | ||
20 | X isn't running and delayed switching until the next logoff. This | ||
21 | features is called hybrid graphics, ATI PowerXpress, and Nvidia | ||
22 | HybridPower. | ||
23 | |||
diff --git a/drivers/gpu/vga/Makefile b/drivers/gpu/vga/Makefile index 7cc8c1ed645b..14ca30b75d0a 100644 --- a/drivers/gpu/vga/Makefile +++ b/drivers/gpu/vga/Makefile | |||
@@ -1 +1,2 @@ | |||
1 | obj-$(CONFIG_VGA_ARB) += vgaarb.o | 1 | obj-$(CONFIG_VGA_ARB) += vgaarb.o |
2 | obj-$(CONFIG_VGA_SWITCHEROO) += vga_switcheroo.o | ||
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c new file mode 100644 index 000000000000..a3f587a0aba9 --- /dev/null +++ b/drivers/gpu/vga/vga_switcheroo.c | |||
@@ -0,0 +1,453 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2010 Red Hat Inc. | ||
3 | * Author : Dave Airlie <airlied@redhat.com> | ||
4 | * | ||
5 | * | ||
6 | * Licensed under GPLv2 | ||
7 | * | ||
8 | * vga_switcheroo.c - Support for laptop with dual GPU using one set of outputs | ||
9 | |||
10 | Switcher interface - methods require for ATPX and DCM | ||
11 | - switchto - this throws the output MUX switch | ||
12 | - discrete_set_power - sets the power state for the discrete card | ||
13 | |||
14 | GPU driver interface | ||
15 | - set_gpu_state - this should do the equiv of s/r for the card | ||
16 | - this should *not* set the discrete power state | ||
17 | - switch_check - check if the device is in a position to switch now | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/dmi.h> | ||
22 | #include <linux/seq_file.h> | ||
23 | #include <linux/uaccess.h> | ||
24 | #include <linux/fs.h> | ||
25 | #include <linux/debugfs.h> | ||
26 | #include <linux/fb.h> | ||
27 | |||
28 | #include <acpi/acpi.h> | ||
29 | #include <acpi/acpi_bus.h> | ||
30 | |||
31 | #include <linux/pci.h> | ||
32 | #include <linux/vga_switcheroo.h> | ||
33 | |||
34 | struct vga_switcheroo_client { | ||
35 | struct pci_dev *pdev; | ||
36 | struct fb_info *fb_info; | ||
37 | int pwr_state; | ||
38 | void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state); | ||
39 | bool (*can_switch)(struct pci_dev *pdev); | ||
40 | int id; | ||
41 | bool active; | ||
42 | }; | ||
43 | |||
44 | static DEFINE_MUTEX(vgasr_mutex); | ||
45 | |||
46 | struct vgasr_priv { | ||
47 | |||
48 | bool active; | ||
49 | bool delayed_switch_active; | ||
50 | enum vga_switcheroo_client_id delayed_client_id; | ||
51 | |||
52 | struct dentry *debugfs_root; | ||
53 | struct dentry *switch_file; | ||
54 | |||
55 | int registered_clients; | ||
56 | struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS]; | ||
57 | |||
58 | struct vga_switcheroo_handler *handler; | ||
59 | }; | ||
60 | |||
61 | static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); | ||
62 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); | ||
63 | |||
64 | /* only one switcheroo per system */ | ||
65 | static struct vgasr_priv vgasr_priv; | ||
66 | |||
67 | int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) | ||
68 | { | ||
69 | mutex_lock(&vgasr_mutex); | ||
70 | if (vgasr_priv.handler) { | ||
71 | mutex_unlock(&vgasr_mutex); | ||
72 | return -EINVAL; | ||
73 | } | ||
74 | |||
75 | vgasr_priv.handler = handler; | ||
76 | mutex_unlock(&vgasr_mutex); | ||
77 | return 0; | ||
78 | } | ||
79 | EXPORT_SYMBOL(vga_switcheroo_register_handler); | ||
80 | |||
81 | void vga_switcheroo_unregister_handler(void) | ||
82 | { | ||
83 | mutex_lock(&vgasr_mutex); | ||
84 | vgasr_priv.handler = NULL; | ||
85 | mutex_unlock(&vgasr_mutex); | ||
86 | } | ||
87 | EXPORT_SYMBOL(vga_switcheroo_unregister_handler); | ||
88 | |||
89 | static void vga_switcheroo_enable(void) | ||
90 | { | ||
91 | int i; | ||
92 | int ret; | ||
93 | /* call the handler to init */ | ||
94 | vgasr_priv.handler->init(); | ||
95 | |||
96 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
97 | ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev); | ||
98 | if (ret < 0) | ||
99 | return; | ||
100 | |||
101 | vgasr_priv.clients[i].id = ret; | ||
102 | } | ||
103 | vga_switcheroo_debugfs_init(&vgasr_priv); | ||
104 | vgasr_priv.active = true; | ||
105 | } | ||
106 | |||
107 | int vga_switcheroo_register_client(struct pci_dev *pdev, | ||
108 | void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state), | ||
109 | bool (*can_switch)(struct pci_dev *pdev)) | ||
110 | { | ||
111 | int index; | ||
112 | |||
113 | mutex_lock(&vgasr_mutex); | ||
114 | /* don't do IGD vs DIS here */ | ||
115 | if (vgasr_priv.registered_clients & 1) | ||
116 | index = 1; | ||
117 | else | ||
118 | index = 0; | ||
119 | |||
120 | vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON; | ||
121 | vgasr_priv.clients[index].pdev = pdev; | ||
122 | vgasr_priv.clients[index].set_gpu_state = set_gpu_state; | ||
123 | vgasr_priv.clients[index].can_switch = can_switch; | ||
124 | vgasr_priv.clients[index].id = -1; | ||
125 | if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) | ||
126 | vgasr_priv.clients[index].active = true; | ||
127 | |||
128 | vgasr_priv.registered_clients |= (1 << index); | ||
129 | |||
130 | /* if we get two clients + handler */ | ||
131 | if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) { | ||
132 | printk(KERN_INFO "vga_switcheroo: enabled\n"); | ||
133 | vga_switcheroo_enable(); | ||
134 | } | ||
135 | mutex_unlock(&vgasr_mutex); | ||
136 | return 0; | ||
137 | } | ||
138 | EXPORT_SYMBOL(vga_switcheroo_register_client); | ||
139 | |||
140 | void vga_switcheroo_unregister_client(struct pci_dev *pdev) | ||
141 | { | ||
142 | int i; | ||
143 | |||
144 | mutex_lock(&vgasr_mutex); | ||
145 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
146 | if (vgasr_priv.clients[i].pdev == pdev) { | ||
147 | vgasr_priv.registered_clients &= ~(1 << i); | ||
148 | break; | ||
149 | } | ||
150 | } | ||
151 | |||
152 | printk(KERN_INFO "vga_switcheroo: disabled\n"); | ||
153 | vga_switcheroo_debugfs_fini(&vgasr_priv); | ||
154 | vgasr_priv.active = false; | ||
155 | mutex_unlock(&vgasr_mutex); | ||
156 | } | ||
157 | EXPORT_SYMBOL(vga_switcheroo_unregister_client); | ||
158 | |||
159 | void vga_switcheroo_client_fb_set(struct pci_dev *pdev, | ||
160 | struct fb_info *info) | ||
161 | { | ||
162 | int i; | ||
163 | |||
164 | mutex_lock(&vgasr_mutex); | ||
165 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
166 | if (vgasr_priv.clients[i].pdev == pdev) { | ||
167 | vgasr_priv.clients[i].fb_info = info; | ||
168 | break; | ||
169 | } | ||
170 | } | ||
171 | mutex_unlock(&vgasr_mutex); | ||
172 | } | ||
173 | EXPORT_SYMBOL(vga_switcheroo_client_fb_set); | ||
174 | |||
175 | static int vga_switcheroo_show(struct seq_file *m, void *v) | ||
176 | { | ||
177 | int i; | ||
178 | mutex_lock(&vgasr_mutex); | ||
179 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
180 | seq_printf(m, "%d:%c:%s:%s\n", i, | ||
181 | vgasr_priv.clients[i].active ? '+' : ' ', | ||
182 | vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off", | ||
183 | pci_name(vgasr_priv.clients[i].pdev)); | ||
184 | } | ||
185 | mutex_unlock(&vgasr_mutex); | ||
186 | return 0; | ||
187 | } | ||
188 | |||
189 | static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file) | ||
190 | { | ||
191 | return single_open(file, vga_switcheroo_show, NULL); | ||
192 | } | ||
193 | |||
194 | static int vga_switchon(struct vga_switcheroo_client *client) | ||
195 | { | ||
196 | int ret; | ||
197 | |||
198 | ret = vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); | ||
199 | /* call the driver callback to turn on device */ | ||
200 | client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); | ||
201 | client->pwr_state = VGA_SWITCHEROO_ON; | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | static int vga_switchoff(struct vga_switcheroo_client *client) | ||
206 | { | ||
207 | /* call the driver callback to turn off device */ | ||
208 | client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); | ||
209 | vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); | ||
210 | client->pwr_state = VGA_SWITCHEROO_OFF; | ||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static int vga_switchto(struct vga_switcheroo_client *new_client) | ||
215 | { | ||
216 | int ret; | ||
217 | int i; | ||
218 | struct vga_switcheroo_client *active = NULL; | ||
219 | |||
220 | if (new_client->active == true) | ||
221 | return 0; | ||
222 | |||
223 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
224 | if (vgasr_priv.clients[i].active == true) { | ||
225 | active = &vgasr_priv.clients[i]; | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | if (!active) | ||
230 | return 0; | ||
231 | |||
232 | /* power up the first device */ | ||
233 | ret = pci_enable_device(new_client->pdev); | ||
234 | if (ret) | ||
235 | return ret; | ||
236 | |||
237 | if (new_client->pwr_state == VGA_SWITCHEROO_OFF) | ||
238 | vga_switchon(new_client); | ||
239 | |||
240 | /* swap shadow resource to denote boot VGA device has changed so X starts on new device */ | ||
241 | active->active = false; | ||
242 | |||
243 | active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW; | ||
244 | new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; | ||
245 | |||
246 | if (new_client->fb_info) { | ||
247 | struct fb_event event; | ||
248 | event.info = new_client->fb_info; | ||
249 | fb_notifier_call_chain(FB_EVENT_REMAP_ALL_CONSOLE, &event); | ||
250 | } | ||
251 | |||
252 | ret = vgasr_priv.handler->switchto(new_client->id); | ||
253 | if (ret) | ||
254 | return ret; | ||
255 | |||
256 | if (active->pwr_state == VGA_SWITCHEROO_ON) | ||
257 | vga_switchoff(active); | ||
258 | |||
259 | new_client->active = true; | ||
260 | return 0; | ||
261 | } | ||
262 | |||
263 | static ssize_t | ||
264 | vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | ||
265 | size_t cnt, loff_t *ppos) | ||
266 | { | ||
267 | char usercmd[64]; | ||
268 | const char *pdev_name; | ||
269 | int i, ret; | ||
270 | bool delay = false, can_switch; | ||
271 | int client_id = -1; | ||
272 | struct vga_switcheroo_client *client = NULL; | ||
273 | |||
274 | if (cnt > 63) | ||
275 | cnt = 63; | ||
276 | |||
277 | if (copy_from_user(usercmd, ubuf, cnt)) | ||
278 | return -EFAULT; | ||
279 | |||
280 | mutex_lock(&vgasr_mutex); | ||
281 | |||
282 | if (!vgasr_priv.active) | ||
283 | return -EINVAL; | ||
284 | |||
285 | /* pwr off the device not in use */ | ||
286 | if (strncmp(usercmd, "OFF", 3) == 0) { | ||
287 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
288 | if (vgasr_priv.clients[i].active) | ||
289 | continue; | ||
290 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON) | ||
291 | vga_switchoff(&vgasr_priv.clients[i]); | ||
292 | } | ||
293 | goto out; | ||
294 | } | ||
295 | /* pwr on the device not in use */ | ||
296 | if (strncmp(usercmd, "ON", 2) == 0) { | ||
297 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
298 | if (vgasr_priv.clients[i].active) | ||
299 | continue; | ||
300 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF) | ||
301 | vga_switchon(&vgasr_priv.clients[i]); | ||
302 | } | ||
303 | goto out; | ||
304 | } | ||
305 | |||
306 | /* request a delayed switch - test can we switch now */ | ||
307 | if (strncmp(usercmd, "DIGD", 4) == 0) { | ||
308 | client_id = VGA_SWITCHEROO_IGD; | ||
309 | delay = true; | ||
310 | } | ||
311 | |||
312 | if (strncmp(usercmd, "DDIS", 4) == 0) { | ||
313 | client_id = VGA_SWITCHEROO_DIS; | ||
314 | delay = true; | ||
315 | } | ||
316 | |||
317 | if (strncmp(usercmd, "IGD", 3) == 0) | ||
318 | client_id = VGA_SWITCHEROO_IGD; | ||
319 | |||
320 | if (strncmp(usercmd, "DIS", 3) == 0) | ||
321 | client_id = VGA_SWITCHEROO_DIS; | ||
322 | |||
323 | if (client_id == -1) | ||
324 | goto out; | ||
325 | |||
326 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
327 | if (vgasr_priv.clients[i].id == client_id) { | ||
328 | client = &vgasr_priv.clients[i]; | ||
329 | break; | ||
330 | } | ||
331 | } | ||
332 | |||
333 | vgasr_priv.delayed_switch_active = false; | ||
334 | /* okay we want a switch - test if devices are willing to switch */ | ||
335 | can_switch = true; | ||
336 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
337 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
338 | if (can_switch == false) { | ||
339 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
340 | break; | ||
341 | } | ||
342 | } | ||
343 | |||
344 | if (can_switch == false && delay == false) | ||
345 | goto out; | ||
346 | |||
347 | if (can_switch == true) { | ||
348 | pdev_name = pci_name(client->pdev); | ||
349 | ret = vga_switchto(client); | ||
350 | if (ret) | ||
351 | printk(KERN_ERR "vga_switcheroo: switching failed %d\n", ret); | ||
352 | } else { | ||
353 | printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id); | ||
354 | vgasr_priv.delayed_switch_active = true; | ||
355 | vgasr_priv.delayed_client_id = client_id; | ||
356 | |||
357 | /* we should at least power up the card to | ||
358 | make the switch faster */ | ||
359 | if (client->pwr_state == VGA_SWITCHEROO_OFF) | ||
360 | vga_switchon(client); | ||
361 | } | ||
362 | |||
363 | out: | ||
364 | mutex_unlock(&vgasr_mutex); | ||
365 | return cnt; | ||
366 | } | ||
367 | |||
368 | static const struct file_operations vga_switcheroo_debugfs_fops = { | ||
369 | .owner = THIS_MODULE, | ||
370 | .open = vga_switcheroo_debugfs_open, | ||
371 | .write = vga_switcheroo_debugfs_write, | ||
372 | .read = seq_read, | ||
373 | .llseek = seq_lseek, | ||
374 | .release = single_release, | ||
375 | }; | ||
376 | |||
377 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv) | ||
378 | { | ||
379 | if (priv->switch_file) { | ||
380 | debugfs_remove(priv->switch_file); | ||
381 | priv->switch_file = NULL; | ||
382 | } | ||
383 | if (priv->debugfs_root) { | ||
384 | debugfs_remove(priv->debugfs_root); | ||
385 | priv->debugfs_root = NULL; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv) | ||
390 | { | ||
391 | /* already initialised */ | ||
392 | if (priv->debugfs_root) | ||
393 | return 0; | ||
394 | priv->debugfs_root = debugfs_create_dir("vgaswitcheroo", NULL); | ||
395 | |||
396 | if (!priv->debugfs_root) { | ||
397 | printk(KERN_ERR "vga_switcheroo: Cannot create /sys/kernel/debug/vgaswitcheroo\n"); | ||
398 | goto fail; | ||
399 | } | ||
400 | |||
401 | priv->switch_file = debugfs_create_file("switch", 0644, | ||
402 | priv->debugfs_root, NULL, &vga_switcheroo_debugfs_fops); | ||
403 | if (!priv->switch_file) { | ||
404 | printk(KERN_ERR "vga_switcheroo: cannot create /sys/kernel/debug/vgaswitcheroo/switch\n"); | ||
405 | goto fail; | ||
406 | } | ||
407 | return 0; | ||
408 | fail: | ||
409 | vga_switcheroo_debugfs_fini(priv); | ||
410 | return -1; | ||
411 | } | ||
412 | |||
413 | int vga_switcheroo_process_delayed_switch(void) | ||
414 | { | ||
415 | struct vga_switcheroo_client *client = NULL; | ||
416 | const char *pdev_name; | ||
417 | bool can_switch = true; | ||
418 | int i; | ||
419 | int ret; | ||
420 | int err = -EINVAL; | ||
421 | |||
422 | mutex_lock(&vgasr_mutex); | ||
423 | if (!vgasr_priv.delayed_switch_active) | ||
424 | goto err; | ||
425 | |||
426 | printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); | ||
427 | |||
428 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
429 | if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id) | ||
430 | client = &vgasr_priv.clients[i]; | ||
431 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
432 | if (can_switch == false) { | ||
433 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
434 | break; | ||
435 | } | ||
436 | } | ||
437 | |||
438 | if (can_switch == false || client == NULL) | ||
439 | goto err; | ||
440 | |||
441 | pdev_name = pci_name(client->pdev); | ||
442 | ret = vga_switchto(client); | ||
443 | if (ret) | ||
444 | printk(KERN_ERR "vga_switcheroo: delayed switching failed %d\n", ret); | ||
445 | |||
446 | vgasr_priv.delayed_switch_active = false; | ||
447 | err = 0; | ||
448 | err: | ||
449 | mutex_unlock(&vgasr_mutex); | ||
450 | return err; | ||
451 | } | ||
452 | EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); | ||
453 | |||