diff options
25 files changed, 1103 insertions, 71 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 | |||
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 3681c6a88212..b0a3fa00706d 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c | |||
| @@ -3025,6 +3025,20 @@ static int fbcon_fb_unregistered(struct fb_info *info) | |||
| 3025 | return 0; | 3025 | return 0; |
| 3026 | } | 3026 | } |
| 3027 | 3027 | ||
| 3028 | static void fbcon_remap_all(int idx) | ||
| 3029 | { | ||
| 3030 | int i; | ||
| 3031 | for (i = first_fb_vc; i <= last_fb_vc; i++) | ||
| 3032 | set_con2fb_map(i, idx, 0); | ||
| 3033 | |||
| 3034 | if (con_is_bound(&fb_con)) { | ||
| 3035 | printk(KERN_INFO "fbcon: Remapping primary device, " | ||
| 3036 | "fb%i, to tty %i-%i\n", idx, | ||
| 3037 | first_fb_vc + 1, last_fb_vc + 1); | ||
| 3038 | info_idx = idx; | ||
| 3039 | } | ||
| 3040 | } | ||
| 3041 | |||
| 3028 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY | 3042 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY |
| 3029 | static void fbcon_select_primary(struct fb_info *info) | 3043 | static void fbcon_select_primary(struct fb_info *info) |
| 3030 | { | 3044 | { |
| @@ -3225,6 +3239,10 @@ static int fbcon_event_notify(struct notifier_block *self, | |||
| 3225 | caps = event->data; | 3239 | caps = event->data; |
| 3226 | fbcon_get_requirement(info, caps); | 3240 | fbcon_get_requirement(info, caps); |
| 3227 | break; | 3241 | break; |
| 3242 | case FB_EVENT_REMAP_ALL_CONSOLE: | ||
| 3243 | idx = info->node; | ||
| 3244 | fbcon_remap_all(idx); | ||
| 3245 | break; | ||
| 3228 | } | 3246 | } |
| 3229 | done: | 3247 | done: |
| 3230 | return ret; | 3248 | return ret; |
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c index 99bbd282ce63..a15b44e9c003 100644 --- a/drivers/video/fbmem.c +++ b/drivers/video/fbmem.c | |||
| @@ -1513,7 +1513,6 @@ register_framebuffer(struct fb_info *fb_info) | |||
| 1513 | fb_info->fix.id, | 1513 | fb_info->fix.id, |
| 1514 | registered_fb[i]->fix.id); | 1514 | registered_fb[i]->fix.id); |
| 1515 | unregister_framebuffer(registered_fb[i]); | 1515 | unregister_framebuffer(registered_fb[i]); |
| 1516 | break; | ||
| 1517 | } | 1516 | } |
| 1518 | } | 1517 | } |
| 1519 | } | 1518 | } |
diff --git a/include/linux/fb.h b/include/linux/fb.h index 369767bd873e..c10163b4c40e 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h | |||
| @@ -543,6 +543,8 @@ struct fb_cursor_user { | |||
| 543 | #define FB_EVENT_GET_REQ 0x0D | 543 | #define FB_EVENT_GET_REQ 0x0D |
| 544 | /* Unbind from the console if possible */ | 544 | /* Unbind from the console if possible */ |
| 545 | #define FB_EVENT_FB_UNBIND 0x0E | 545 | #define FB_EVENT_FB_UNBIND 0x0E |
| 546 | /* CONSOLE-SPECIFIC: remap all consoles to new fb - for vga switcheroo */ | ||
| 547 | #define FB_EVENT_REMAP_ALL_CONSOLE 0x0F | ||
| 546 | 548 | ||
| 547 | struct fb_event { | 549 | struct fb_event { |
| 548 | struct fb_info *info; | 550 | struct fb_info *info; |
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h new file mode 100644 index 000000000000..4b58ab1e8612 --- /dev/null +++ b/include/linux/vga_switcheroo.h | |||
| @@ -0,0 +1,58 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2010 Red Hat Inc. | ||
| 3 | * Author : Dave Airlie <airlied@redhat.com> | ||
| 4 | * | ||
| 5 | * Licensed under GPLv2 | ||
| 6 | * | ||
| 7 | * vga_switcheroo.h - Support for laptop with dual GPU using one set of outputs | ||
| 8 | */ | ||
| 9 | |||
| 10 | #include <acpi/acpi.h> | ||
| 11 | #include <linux/fb.h> | ||
| 12 | |||
| 13 | enum vga_switcheroo_state { | ||
| 14 | VGA_SWITCHEROO_OFF, | ||
| 15 | VGA_SWITCHEROO_ON, | ||
| 16 | }; | ||
| 17 | |||
| 18 | enum vga_switcheroo_client_id { | ||
| 19 | VGA_SWITCHEROO_IGD, | ||
| 20 | VGA_SWITCHEROO_DIS, | ||
| 21 | VGA_SWITCHEROO_MAX_CLIENTS, | ||
| 22 | }; | ||
| 23 | |||
| 24 | struct vga_switcheroo_handler { | ||
| 25 | int (*switchto)(enum vga_switcheroo_client_id id); | ||
| 26 | int (*power_state)(enum vga_switcheroo_client_id id, | ||
| 27 | enum vga_switcheroo_state state); | ||
| 28 | int (*init)(void); | ||
| 29 | int (*get_client_id)(struct pci_dev *pdev); | ||
| 30 | }; | ||
| 31 | |||
| 32 | |||
| 33 | #if defined(CONFIG_VGA_SWITCHEROO) | ||
| 34 | void vga_switcheroo_unregister_client(struct pci_dev *dev); | ||
| 35 | int vga_switcheroo_register_client(struct pci_dev *dev, | ||
| 36 | void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state), | ||
| 37 | bool (*can_switch)(struct pci_dev *dev)); | ||
| 38 | |||
| 39 | void vga_switcheroo_client_fb_set(struct pci_dev *dev, | ||
| 40 | struct fb_info *info); | ||
| 41 | |||
| 42 | int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler); | ||
| 43 | void vga_switcheroo_unregister_handler(void); | ||
| 44 | |||
| 45 | int vga_switcheroo_process_delayed_switch(void); | ||
| 46 | |||
| 47 | #else | ||
| 48 | |||
| 49 | static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {} | ||
| 50 | static inline int vga_switcheroo_register_client(struct pci_dev *dev, | ||
| 51 | void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state), | ||
| 52 | bool (*can_switch)(struct pci_dev *dev)) { return 0; } | ||
| 53 | static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} | ||
| 54 | static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } | ||
| 55 | static inline void vga_switcheroo_unregister_handler(void) {} | ||
| 56 | static inline int vga_switcheroo_process_delayed_switch(void) { return 0; } | ||
| 57 | |||
| 58 | #endif | ||
