Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vga_switcheroo: initial implementation (v15)

Many new laptops now come with 2 gpus, one to be used for low power
modes and one for gaming/on-ac applications. These GPUs are typically
wired to the laptop panel and VGA ports via a multiplexer unit which
is controlled via ACPI methods.

4 combinations of systems typically exist - with 2 ACPI methods.
Intel/ATI - Lenovo W500/T500 - use ATPX ACPI method
ATI/ATI - some ASUS - use ATPX ACPI Method
Intel/Nvidia - - use _DSM ACPI method
Nvidia/Nvidia - - use _DSM ACPI method.

TODO:
This patch adds support for the ATPX method and initial bits
for the _DSM methods that need to written by someone with
access to the hardware.
Add a proper non-debugfs interface - need to get some proper
testing first.

v2: add power up/down support for both devices
on W500 puts i915/radeon into D3 and cuts power to radeon.

v3: redo probing methods, no DMI list, drm devices call to
register with switcheroo, it tries to find an ATPX method on
any device and once there is two devices + ATPX it inits the
switcher.

v4: ATPX msg handling using buffers - should work on more machines

v5: rearchitect after more mjg59 discussion - move ATPX handling to
radeon driver.

v6: add file headers + initial nouveau bits (to be filled out).

v7: merge delayed switcher code.

v8: avoid suspend/resume of gpu that is off

v9: rearchitect - mjg59 is always right. - move all ATPX code to
radeon, should allow simpler DSM also proper ATRM handling

v10: add ATRM support for radeon BIOS, add mutex to lock vgasr_priv

v11: fix bug in resuming Intel for 2nd time.

v12: start fixing up nvidia code blindly.

v13: blindly guess at finishing nvidia code

v14: remove radeon audio hacks - fix up intel resume more like upstream

v15: clean up printks + remove unnecessary igd/dis pointers

mount debugfs

/sys/kernel/debug/vgaswitcheroo/switch - should exist if ATPX detected
+ 2 cards.

DIS - immediate change to discrete
IGD - immediate change to IGD
DDIS - delayed change to discrete
DIGD - delayed change to IGD
ON - turn on not in use
OFF - turn off not in use

Tested on W500 (Intel/ATI) and T500 (Intel/ATI)

Signed-off-by: Dave Airlie <airlied@redhat.com>

authored by

Dave Airlie and committed by
Dave Airlie
6a9ee8af 9fd1de52

+1106 -72
+35
drivers/gpu/drm/i915/i915_dma.c
··· 35 35 #include "i915_drv.h" 36 36 #include "i915_trace.h" 37 37 #include <linux/vgaarb.h> 38 + #include <linux/vga_switcheroo.h> 38 39 39 40 /* Really want an OS-independent resettable timer. Would like to have 40 41 * this loop run for (eg) 3 sec, but have the timer reset every time ··· 1200 1199 return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM; 1201 1200 } 1202 1201 1202 + static void i915_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) 1203 + { 1204 + struct drm_device *dev = pci_get_drvdata(pdev); 1205 + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; 1206 + if (state == VGA_SWITCHEROO_ON) { 1207 + printk(KERN_INFO "i915: switched off\n"); 1208 + /* i915 resume handler doesn't set to D0 */ 1209 + pci_set_power_state(dev->pdev, PCI_D0); 1210 + i915_resume(dev); 1211 + } else { 1212 + printk(KERN_ERR "i915: switched off\n"); 1213 + i915_suspend(dev, pmm); 1214 + } 1215 + } 1216 + 1217 + static bool i915_switcheroo_can_switch(struct pci_dev *pdev) 1218 + { 1219 + struct drm_device *dev = pci_get_drvdata(pdev); 1220 + bool can_switch; 1221 + 1222 + spin_lock(&dev->count_lock); 1223 + can_switch = (dev->open_count == 0); 1224 + spin_unlock(&dev->count_lock); 1225 + return can_switch; 1226 + } 1227 + 1203 1228 static int i915_load_modeset_init(struct drm_device *dev, 1204 1229 unsigned long prealloc_start, 1205 1230 unsigned long prealloc_size, ··· 1284 1257 1285 1258 /* if we have > 1 VGA cards, then disable the radeon VGA resources */ 1286 1259 ret = vga_client_register(dev->pdev, dev, NULL, i915_vga_set_decode); 1260 + if (ret) 1261 + goto destroy_ringbuffer; 1262 + 1263 + ret = vga_switcheroo_register_client(dev->pdev, 1264 + i915_switcheroo_set_state, 1265 + i915_switcheroo_can_switch); 1287 1266 if (ret) 1288 1267 goto destroy_ringbuffer; 1289 1268 ··· 1577 1544 dev_priv->child_dev_num = 0; 1578 1545 } 1579 1546 drm_irq_uninstall(dev); 1547 + vga_switcheroo_unregister_client(dev->pdev); 1580 1548 vga_client_register(dev->pdev, NULL, NULL, NULL); 1581 1549 } 1582 1550 ··· 1645 1611 1646 1612 if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) { 1647 1613 drm_fb_helper_restore(); 1614 + vga_switcheroo_process_delayed_switch(); 1648 1615 return; 1649 1616 } 1650 1617
+2 -2
drivers/gpu/drm/i915/i915_drv.c
··· 201 201 return 0; 202 202 } 203 203 204 - static int i915_suspend(struct drm_device *dev, pm_message_t state) 204 + int i915_suspend(struct drm_device *dev, pm_message_t state) 205 205 { 206 206 int error; 207 207 ··· 255 255 return error; 256 256 } 257 257 258 - static int i915_resume(struct drm_device *dev) 258 + int i915_resume(struct drm_device *dev) 259 259 { 260 260 if (pci_enable_device(dev->pdev)) 261 261 return -EIO;
+2
drivers/gpu/drm/i915/i915_drv.h
··· 736 736 extern unsigned int i915_powersave; 737 737 extern unsigned int i915_lvds_downclock; 738 738 739 + extern int i915_suspend(struct drm_device *dev, pm_message_t state); 740 + extern int i915_resume(struct drm_device *dev); 739 741 extern void i915_save_display(struct drm_device *dev); 740 742 extern void i915_restore_display(struct drm_device *dev); 741 743 extern int i915_master_create(struct drm_device *dev, struct drm_master *master);
+2
drivers/gpu/drm/i915/intel_fb.c
··· 35 35 #include <linux/delay.h> 36 36 #include <linux/fb.h> 37 37 #include <linux/init.h> 38 + #include <linux/vga_switcheroo.h> 38 39 39 40 #include "drmP.h" 40 41 #include "drm.h" ··· 236 235 obj_priv->gtt_offset, fbo); 237 236 238 237 mutex_unlock(&dev->struct_mutex); 238 + vga_switcheroo_client_fb_set(dev->pdev, info); 239 239 return 0; 240 240 241 241 out_unpin:
+122 -42
drivers/gpu/drm/nouveau/nouveau_acpi.c
··· 11 11 #include "nouveau_drm.h" 12 12 #include "nv50_display.h" 13 13 14 + #include <linux/vga_switcheroo.h> 15 + 14 16 #define NOUVEAU_DSM_SUPPORTED 0x00 15 17 #define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00 16 18 ··· 30 28 #define NOUVEAU_DSM_POWER_SPEED 0x01 31 29 #define NOUVEAU_DSM_POWER_STAMINA 0x02 32 30 33 - static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result) 34 - { 35 - static char muid[] = { 36 - 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 37 - 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 38 - }; 31 + static struct nouveau_dsm_priv { 32 + bool dsm_detected; 33 + acpi_handle dhandle; 34 + acpi_handle dsm_handle; 35 + } nouveau_dsm_priv; 39 36 40 - struct pci_dev *pdev = dev->pdev; 41 - struct acpi_handle *handle; 37 + static const char nouveau_dsm_muid[] = { 38 + 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, 39 + 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, 40 + }; 41 + 42 + static int nouveau_dsm(acpi_handle handle, int func, int arg, int *result) 43 + { 42 44 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; 43 45 struct acpi_object_list input; 44 46 union acpi_object params[4]; 45 47 union acpi_object *obj; 46 48 int err; 47 49 48 - handle = DEVICE_ACPI_HANDLE(&pdev->dev); 49 - 50 - if (!handle) 51 - return -ENODEV; 52 - 53 50 input.count = 4; 54 51 input.pointer = params; 55 52 params[0].type = ACPI_TYPE_BUFFER; 56 - params[0].buffer.length = sizeof(muid); 57 - params[0].buffer.pointer = (char *)muid; 53 + params[0].buffer.length = sizeof(nouveau_dsm_muid); 54 + params[0].buffer.pointer = (char *)nouveau_dsm_muid; 58 55 params[1].type = ACPI_TYPE_INTEGER; 59 56 params[1].integer.value = 0x00000102; 60 57 params[2].type = ACPI_TYPE_INTEGER; ··· 63 62 64 63 err = acpi_evaluate_object(handle, "_DSM", &input, &output); 65 64 if (err) { 66 - NV_INFO(dev, "failed to evaluate _DSM: %d\n", err); 65 + printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); 67 66 return err; 68 67 } 69 68 ··· 87 86 return 0; 88 87 } 89 88 90 - int nouveau_hybrid_setup(struct drm_device *dev) 89 + static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) 91 90 { 92 - int result; 91 + return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); 92 + } 93 93 94 - if (nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STATE, 95 - &result)) 96 - return -ENODEV; 97 - 98 - NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result); 99 - 100 - if (result) { /* Ensure that the external GPU is enabled */ 101 - nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL); 102 - nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED, 103 - NULL); 104 - } else { /* Stamina mode - disable the external GPU */ 105 - nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA, 106 - NULL); 107 - nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA, 108 - NULL); 109 - } 110 - 94 + static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) 95 + { 96 + int arg; 97 + if (state == VGA_SWITCHEROO_ON) 98 + arg = NOUVEAU_DSM_POWER_SPEED; 99 + else 100 + arg = NOUVEAU_DSM_POWER_STAMINA; 101 + nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); 111 102 return 0; 112 103 } 113 104 114 - bool nouveau_dsm_probe(struct drm_device *dev) 105 + static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) 115 106 { 116 - int support = 0; 107 + if (id == VGA_SWITCHEROO_IGD) 108 + return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_STAMINA); 109 + else 110 + return nouveau_dsm_switch_mux(nouveau_dsm_priv.dsm_handle, NOUVEAU_DSM_LED_SPEED); 111 + } 117 112 118 - if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED, 119 - NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support)) 113 + static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, 114 + enum vga_switcheroo_state state) 115 + { 116 + if (id == VGA_SWITCHEROO_IGD) 117 + return 0; 118 + 119 + return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dsm_handle, state); 120 + } 121 + 122 + static int nouveau_dsm_init(void) 123 + { 124 + return 0; 125 + } 126 + 127 + static int nouveau_dsm_get_client_id(struct pci_dev *pdev) 128 + { 129 + if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) 130 + return VGA_SWITCHEROO_IGD; 131 + else 132 + return VGA_SWITCHEROO_DIS; 133 + } 134 + 135 + static struct vga_switcheroo_handler nouveau_dsm_handler = { 136 + .switchto = nouveau_dsm_switchto, 137 + .power_state = nouveau_dsm_power_state, 138 + .init = nouveau_dsm_init, 139 + .get_client_id = nouveau_dsm_get_client_id, 140 + }; 141 + 142 + static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) 143 + { 144 + acpi_handle dhandle, nvidia_handle; 145 + acpi_status status; 146 + int ret; 147 + uint32_t result; 148 + 149 + dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); 150 + if (!dhandle) 151 + return false; 152 + status = acpi_get_handle(dhandle, "_DSM", &nvidia_handle); 153 + if (ACPI_FAILURE(status)) { 154 + return false; 155 + } 156 + 157 + ret= nouveau_dsm(nvidia_handle, NOUVEAU_DSM_SUPPORTED, 158 + NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); 159 + if (ret < 0) 120 160 return false; 121 161 122 - if (!support) 123 - return false; 124 - 162 + nouveau_dsm_priv.dhandle = dhandle; 163 + nouveau_dsm_priv.dsm_handle = nvidia_handle; 125 164 return true; 165 + } 166 + 167 + static bool nouveau_dsm_detect(void) 168 + { 169 + char acpi_method_name[255] = { 0 }; 170 + struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; 171 + struct pci_dev *pdev = NULL; 172 + int has_dsm = 0; 173 + int vga_count = 0; 174 + while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { 175 + vga_count++; 176 + 177 + has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); 178 + } 179 + 180 + if (vga_count == 2 && has_dsm) { 181 + acpi_get_name(nouveau_dsm_priv.dsm_handle, ACPI_FULL_PATHNAME, &buffer); 182 + printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", 183 + acpi_method_name); 184 + nouveau_dsm_priv.dsm_detected = true; 185 + return true; 186 + } 187 + return false; 188 + } 189 + 190 + void nouveau_register_dsm_handler(void) 191 + { 192 + bool r; 193 + 194 + r = nouveau_dsm_detect(); 195 + if (!r) 196 + return; 197 + 198 + vga_switcheroo_register_handler(&nouveau_dsm_handler); 199 + } 200 + 201 + void nouveau_unregister_dsm_handler(void) 202 + { 203 + vga_switcheroo_unregister_handler(); 126 204 }
+6 -3
drivers/gpu/drm/nouveau/nouveau_drv.c
··· 135 135 drm_put_dev(dev); 136 136 } 137 137 138 - static int 138 + int 139 139 nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state) 140 140 { 141 141 struct drm_device *dev = pci_get_drvdata(pdev); ··· 233 233 return ret; 234 234 } 235 235 236 - static int 236 + int 237 237 nouveau_pci_resume(struct pci_dev *pdev) 238 238 { 239 239 struct drm_device *dev = pci_get_drvdata(pdev); ··· 402 402 nouveau_modeset = 1; 403 403 } 404 404 405 - if (nouveau_modeset == 1) 405 + if (nouveau_modeset == 1) { 406 406 driver.driver_features |= DRIVER_MODESET; 407 + nouveau_register_dsm_handler(); 408 + } 407 409 408 410 return drm_init(&driver); 409 411 } ··· 413 411 static void __exit nouveau_exit(void) 414 412 { 415 413 drm_exit(&driver); 414 + nouveau_unregister_dsm_handler(); 416 415 } 417 416 418 417 module_init(nouveau_init);
+5 -14
drivers/gpu/drm/nouveau/nouveau_drv.h
··· 614 614 } susres; 615 615 616 616 struct backlight_device *backlight; 617 - bool acpi_dsm; 618 617 619 618 struct nouveau_channel *evo; 620 619 ··· 680 681 extern int nouveau_ignorelid; 681 682 extern int nouveau_nofbaccel; 682 683 extern int nouveau_noaccel; 684 + 685 + extern int nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state); 686 + extern int nouveau_pci_resume(struct pci_dev *pdev); 683 687 684 688 /* nouveau_state.c */ 685 689 extern void nouveau_preclose(struct drm_device *dev, struct drm_file *); ··· 850 848 extern int nouveau_dma_wait(struct nouveau_channel *, int size); 851 849 852 850 /* nouveau_acpi.c */ 853 - #ifdef CONFIG_ACPI 854 - extern int nouveau_hybrid_setup(struct drm_device *dev); 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 851 + void nouveau_register_dsm_handler(void); 852 + void nouveau_unregister_dsm_handler(void); 866 853 867 854 /* nouveau_backlight.c */ 868 855 #ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
+2
drivers/gpu/drm/nouveau/nouveau_fbcon.c
··· 36 36 #include <linux/fb.h> 37 37 #include <linux/init.h> 38 38 #include <linux/screen_info.h> 39 + #include <linux/vga_switcheroo.h> 39 40 40 41 #include "drmP.h" 41 42 #include "drm.h" ··· 371 370 nvbo->bo.offset, nvbo); 372 371 373 372 mutex_unlock(&dev->struct_mutex); 373 + vga_switcheroo_client_fb_set(dev->pdev, info); 374 374 return 0; 375 375 376 376 out_unref:
+27 -5
drivers/gpu/drm/nouveau/nouveau_state.c
··· 29 29 #include "drm_sarea.h" 30 30 #include "drm_crtc_helper.h" 31 31 #include <linux/vgaarb.h> 32 + #include <linux/vga_switcheroo.h> 32 33 33 34 #include "nouveau_drv.h" 34 35 #include "nouveau_drm.h" ··· 372 371 return ret; 373 372 } 374 373 374 + static void nouveau_switcheroo_set_state(struct pci_dev *pdev, 375 + enum vga_switcheroo_state state) 376 + { 377 + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; 378 + if (state == VGA_SWITCHEROO_ON) { 379 + printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); 380 + nouveau_pci_resume(pdev); 381 + } else { 382 + printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); 383 + nouveau_pci_suspend(pdev, pmm); 384 + } 385 + } 386 + 387 + static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev) 388 + { 389 + struct drm_device *dev = pci_get_drvdata(pdev); 390 + bool can_switch; 391 + 392 + spin_lock(&dev->count_lock); 393 + can_switch = (dev->open_count == 0); 394 + spin_unlock(&dev->count_lock); 395 + return can_switch; 396 + } 397 + 375 398 int 376 399 nouveau_card_init(struct drm_device *dev) 377 400 { ··· 409 384 return 0; 410 385 411 386 vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); 387 + vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state, 388 + nouveau_switcheroo_can_switch); 412 389 413 390 /* Initialise internal driver API hooks */ 414 391 ret = nouveau_init_engine_ptrs(dev); ··· 643 616 644 617 NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n", 645 618 dev->pci_vendor, dev->pci_device, dev->pdev->class); 646 - 647 - dev_priv->acpi_dsm = nouveau_dsm_probe(dev); 648 - 649 - if (dev_priv->acpi_dsm) 650 - nouveau_hybrid_setup(dev); 651 619 652 620 dev_priv->wq = create_workqueue("nouveau"); 653 621 if (!dev_priv->wq)
+2 -1
drivers/gpu/drm/radeon/Makefile
··· 54 54 radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \ 55 55 rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \ 56 56 r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \ 57 - r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o 57 + r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o \ 58 + radeon_atpx_handler.o 58 59 59 60 radeon-$(CONFIG_COMPAT) += radeon_ioc32.o 60 61
+8
drivers/gpu/drm/radeon/radeon.h
··· 118 118 /* 119 119 * BIOS. 120 120 */ 121 + #define ATRM_BIOS_PAGE 4096 122 + 123 + bool radeon_atrm_supported(struct pci_dev *pdev); 124 + int radeon_atrm_get_bios_chunk(uint8_t *bios, int offset, int len); 121 125 bool radeon_get_bios(struct radeon_device *rdev); 122 126 123 127 ··· 842 838 int audio_bits_per_sample; 843 839 uint8_t audio_status_bits; 844 840 uint8_t audio_category_code; 841 + 842 + bool powered_down; 845 843 }; 846 844 847 845 int radeon_device_init(struct radeon_device *rdev, ··· 1048 1042 extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable); 1049 1043 extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain); 1050 1044 extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo); 1045 + extern int radeon_resume_kms(struct drm_device *dev); 1046 + extern int radeon_suspend_kms(struct drm_device *dev, pm_message_t state); 1051 1047 1052 1048 /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */ 1053 1049 struct r100_mc_save {
+258
drivers/gpu/drm/radeon/radeon_atpx_handler.c
··· 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 + }
+39 -5
drivers/gpu/drm/radeon/radeon_bios.c
··· 30 30 #include "radeon.h" 31 31 #include "atom.h" 32 32 33 + #include <linux/vga_switcheroo.h> 33 34 /* 34 35 * BIOS. 35 36 */ ··· 63 62 iounmap(bios); 64 63 return false; 65 64 } 66 - memcpy(rdev->bios, bios, size); 65 + memcpy_fromio(rdev->bios, bios, size); 67 66 iounmap(bios); 68 67 return true; 69 68 } ··· 94 93 return true; 95 94 } 96 95 96 + /* ATRM is used to get the BIOS on the discrete cards in 97 + * dual-gpu systems. 98 + */ 99 + static bool radeon_atrm_get_bios(struct radeon_device *rdev) 100 + { 101 + int ret; 102 + int size = 64 * 1024; 103 + int i; 104 + 105 + if (!radeon_atrm_supported(rdev->pdev)) 106 + return false; 107 + 108 + rdev->bios = kmalloc(size, GFP_KERNEL); 109 + if (!rdev->bios) { 110 + DRM_ERROR("Unable to allocate bios\n"); 111 + return false; 112 + } 113 + 114 + for (i = 0; i < size / ATRM_BIOS_PAGE; i++) { 115 + ret = radeon_atrm_get_bios_chunk(rdev->bios, 116 + (i * ATRM_BIOS_PAGE), 117 + ATRM_BIOS_PAGE); 118 + if (ret <= 0) 119 + break; 120 + } 121 + 122 + if (i == 0 || rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { 123 + kfree(rdev->bios); 124 + return false; 125 + } 126 + return true; 127 + } 97 128 static bool r700_read_disabled_bios(struct radeon_device *rdev) 98 129 { 99 130 uint32_t viph_control; ··· 421 388 return legacy_read_disabled_bios(rdev); 422 389 } 423 390 391 + 424 392 bool radeon_get_bios(struct radeon_device *rdev) 425 393 { 426 394 bool r; 427 395 uint16_t tmp; 428 396 429 - if (rdev->flags & RADEON_IS_IGP) { 397 + r = radeon_atrm_get_bios(rdev); 398 + if (r == false) 430 399 r = igp_read_bios_from_vram(rdev); 431 - if (r == false) 432 - r = radeon_read_bios(rdev); 433 - } else 400 + if (r == false) 434 401 r = radeon_read_bios(rdev); 435 402 if (r == false) { 436 403 r = radeon_read_disabled_bios(rdev); ··· 441 408 return false; 442 409 } 443 410 if (rdev->bios[0] != 0x55 || rdev->bios[1] != 0xaa) { 411 + printk("BIOS signature incorrect %x %x\n", rdev->bios[0], rdev->bios[1]); 444 412 goto free_bios; 445 413 } 446 414
+40
drivers/gpu/drm/radeon/radeon_device.c
··· 30 30 #include <drm/drm_crtc_helper.h> 31 31 #include <drm/radeon_drm.h> 32 32 #include <linux/vgaarb.h> 33 + #include <linux/vga_switcheroo.h> 33 34 #include "radeon_reg.h" 34 35 #include "radeon.h" 35 36 #include "radeon_asic.h" ··· 614 613 } 615 614 } 616 615 616 + static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) 617 + { 618 + struct drm_device *dev = pci_get_drvdata(pdev); 619 + struct radeon_device *rdev = dev->dev_private; 620 + pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; 621 + if (state == VGA_SWITCHEROO_ON) { 622 + printk(KERN_INFO "radeon: switched on\n"); 623 + /* don't suspend or resume card normally */ 624 + rdev->powered_down = false; 625 + radeon_resume_kms(dev); 626 + } else { 627 + printk(KERN_INFO "radeon: switched off\n"); 628 + radeon_suspend_kms(dev, pmm); 629 + /* don't suspend or resume card normally */ 630 + rdev->powered_down = true; 631 + } 632 + } 633 + 634 + static bool radeon_switcheroo_can_switch(struct pci_dev *pdev) 635 + { 636 + struct drm_device *dev = pci_get_drvdata(pdev); 637 + bool can_switch; 638 + 639 + spin_lock(&dev->count_lock); 640 + can_switch = (dev->open_count == 0); 641 + spin_unlock(&dev->count_lock); 642 + return can_switch; 643 + } 644 + 645 + 617 646 int radeon_device_init(struct radeon_device *rdev, 618 647 struct drm_device *ddev, 619 648 struct pci_dev *pdev, ··· 723 692 /* this will fail for cards that aren't VGA class devices, just 724 693 * ignore it */ 725 694 vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); 695 + vga_switcheroo_register_client(rdev->pdev, 696 + radeon_switcheroo_set_state, 697 + radeon_switcheroo_can_switch); 726 698 727 699 r = radeon_init(rdev); 728 700 if (r) ··· 757 723 rdev->shutdown = true; 758 724 radeon_fini(rdev); 759 725 destroy_workqueue(rdev->wq); 726 + vga_switcheroo_unregister_client(rdev->pdev); 760 727 vga_client_register(rdev->pdev, NULL, NULL, NULL); 761 728 iounmap(rdev->rmmio); 762 729 rdev->rmmio = NULL; ··· 781 746 } 782 747 rdev = dev->dev_private; 783 748 749 + if (rdev->powered_down) 750 + return 0; 784 751 /* unpin the front buffers */ 785 752 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 786 753 struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb); ··· 827 790 int radeon_resume_kms(struct drm_device *dev) 828 791 { 829 792 struct radeon_device *rdev = dev->dev_private; 793 + 794 + if (rdev->powered_down) 795 + return 0; 830 796 831 797 acquire_console_sem(); 832 798 pci_set_power_state(dev->pdev, PCI_D0);
+2
drivers/gpu/drm/radeon/radeon_drv.c
··· 339 339 driver = &kms_driver; 340 340 driver->driver_features |= DRIVER_MODESET; 341 341 driver->num_ioctls = radeon_max_kms_ioctl; 342 + radeon_register_atpx_handler(); 342 343 } 343 344 /* if the vga console setting is enabled still 344 345 * let modprobe override it */ ··· 349 348 static void __exit radeon_exit(void) 350 349 { 351 350 drm_exit(driver); 351 + radeon_unregister_atpx_handler(); 352 352 } 353 353 354 354 module_init(radeon_init);
+3
drivers/gpu/drm/radeon/radeon_drv.h
··· 455 455 int sx, int sy, int dx, int dy, 456 456 int w, int h, int src_pitch, int dst_pitch, int cpp); 457 457 458 + /* atpx handler */ 459 + void radeon_register_atpx_handler(void); 460 + void radeon_unregister_atpx_handler(void); 458 461 /* Flags for stats.boxes 459 462 */ 460 463 #define RADEON_BOX_DMA_IDLE 0x1
+3
drivers/gpu/drm/radeon/radeon_fb.c
··· 39 39 40 40 #include "drm_fb_helper.h" 41 41 42 + #include <linux/vga_switcheroo.h> 43 + 42 44 struct radeon_fb_device { 43 45 struct drm_fb_helper helper; 44 46 struct radeon_framebuffer *rfb; ··· 293 291 rfbdev->rdev = rdev; 294 292 295 293 mutex_unlock(&rdev->ddev->struct_mutex); 294 + vga_switcheroo_client_fb_set(rdev->ddev->pdev, info); 296 295 return 0; 297 296 298 297 out_unref:
+3
drivers/gpu/drm/radeon/radeon_kms.c
··· 30 30 #include "radeon.h" 31 31 #include "radeon_drm.h" 32 32 33 + #include <linux/vga_switcheroo.h> 34 + 33 35 int radeon_driver_unload_kms(struct drm_device *dev) 34 36 { 35 37 struct radeon_device *rdev = dev->dev_private; ··· 138 136 139 137 void radeon_driver_lastclose_kms(struct drm_device *dev) 140 138 { 139 + vga_switcheroo_process_delayed_switch(); 141 140 } 142 141 143 142 int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
+13
drivers/gpu/vga/Kconfig
··· 8 8 are accessed at same time they need some kind of coordination. Please 9 9 see Documentation/vgaarbiter.txt for more details. Select this to 10 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 +
+1
drivers/gpu/vga/Makefile
··· 1 1 obj-$(CONFIG_VGA_ARB) += vgaarb.o 2 + obj-$(CONFIG_VGA_SWITCHEROO) += vga_switcheroo.o
+453
drivers/gpu/vga/vga_switcheroo.c
··· 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 +
+18
drivers/video/console/fbcon.c
··· 3025 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 3042 #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY 3029 3043 static void fbcon_select_primary(struct fb_info *info) 3030 3044 { ··· 3238 3224 case FB_EVENT_GET_REQ: 3239 3225 caps = event->data; 3240 3226 fbcon_get_requirement(info, caps); 3227 + break; 3228 + case FB_EVENT_REMAP_ALL_CONSOLE: 3229 + idx = info->node; 3230 + fbcon_remap_all(idx); 3241 3231 break; 3242 3232 } 3243 3233 done:
+2
include/linux/fb.h
··· 543 543 #define FB_EVENT_GET_REQ 0x0D 544 544 /* Unbind from the console if possible */ 545 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 549 struct fb_event { 548 550 struct fb_info *info;
+58
include/linux/vga_switcheroo.h
··· 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