diff options
| author | Dave Airlie <airlied@redhat.com> | 2011-05-08 23:23:18 -0400 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2011-05-08 23:23:18 -0400 |
| commit | 269dc2d9194f74b920647e0f2ed0d822dc431770 (patch) | |
| tree | 6b6be1a7b9c152e27f12fba9d9d75cced9551b21 | |
| parent | 776f2b7cb473baa52cdeee9804073342dee7d7cb (diff) | |
| parent | 000703f44c77b152cd966eaf06f4ab043274ff46 (diff) | |
Merge remote branch 'korg/drm-nvidia-switch-fixes' into drm-core-next
* korg/drm-nvidia-switch-fixes:
mxm/wmi: add MXMX interface entry point.
nouveau: add optimus detection to DSM code.
vgaarb: use bridges to control VGA routing where possible.
nouveau/acpi: hook up to the MXM method for mux switching.
platform/x86: add MXM WMI driver.
| -rw-r--r-- | drivers/gpu/drm/nouveau/Kconfig | 1 | ||||
| -rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_acpi.c | 108 | ||||
| -rw-r--r-- | drivers/gpu/vga/vgaarb.c | 113 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 25 | ||||
| -rw-r--r-- | drivers/platform/x86/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/platform/x86/Makefile | 1 | ||||
| -rw-r--r-- | drivers/platform/x86/mxm-wmi.c | 111 | ||||
| -rw-r--r-- | include/linux/mxm-wmi.h | 33 | ||||
| -rw-r--r-- | include/linux/pci.h | 7 |
9 files changed, 368 insertions, 38 deletions
diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig index de70959b9ed5..e2f8a38d5f24 100644 --- a/drivers/gpu/drm/nouveau/Kconfig +++ b/drivers/gpu/drm/nouveau/Kconfig | |||
| @@ -11,6 +11,7 @@ config DRM_NOUVEAU | |||
| 11 | select FRAMEBUFFER_CONSOLE if !EXPERT | 11 | select FRAMEBUFFER_CONSOLE if !EXPERT |
| 12 | select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT | 12 | select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT |
| 13 | select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT | 13 | select ACPI_VIDEO if ACPI && X86 && BACKLIGHT_CLASS_DEVICE && VIDEO_OUTPUT_CONTROL && INPUT |
| 14 | select MXM_WMI if ACPI | ||
| 14 | help | 15 | help |
| 15 | Choose this option for open-source nVidia support. | 16 | Choose this option for open-source nVidia support. |
| 16 | 17 | ||
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c index a54238058dc5..f0d459bb46e4 100644 --- a/drivers/gpu/drm/nouveau/nouveau_acpi.c +++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | #include <acpi/acpi_drivers.h> | 4 | #include <acpi/acpi_drivers.h> |
| 5 | #include <acpi/acpi_bus.h> | 5 | #include <acpi/acpi_bus.h> |
| 6 | #include <acpi/video.h> | 6 | #include <acpi/video.h> |
| 7 | #include <acpi/acpi.h> | ||
| 8 | #include <linux/mxm-wmi.h> | ||
| 7 | 9 | ||
| 8 | #include "drmP.h" | 10 | #include "drmP.h" |
| 9 | #include "drm.h" | 11 | #include "drm.h" |
| @@ -35,15 +37,71 @@ | |||
| 35 | 37 | ||
| 36 | static struct nouveau_dsm_priv { | 38 | static struct nouveau_dsm_priv { |
| 37 | bool dsm_detected; | 39 | bool dsm_detected; |
| 40 | bool optimus_detected; | ||
| 38 | acpi_handle dhandle; | 41 | acpi_handle dhandle; |
| 39 | acpi_handle rom_handle; | 42 | acpi_handle rom_handle; |
| 40 | } nouveau_dsm_priv; | 43 | } nouveau_dsm_priv; |
| 41 | 44 | ||
| 45 | #define NOUVEAU_DSM_HAS_MUX 0x1 | ||
| 46 | #define NOUVEAU_DSM_HAS_OPT 0x2 | ||
| 47 | |||
| 42 | static const char nouveau_dsm_muid[] = { | 48 | static const char nouveau_dsm_muid[] = { |
| 43 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, | 49 | 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D, |
| 44 | 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, | 50 | 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4, |
| 45 | }; | 51 | }; |
| 46 | 52 | ||
| 53 | static const char nouveau_op_dsm_muid[] = { | ||
| 54 | 0xF8, 0xD8, 0x86, 0xA4, 0xDA, 0x0B, 0x1B, 0x47, | ||
| 55 | 0xA7, 0x2B, 0x60, 0x42, 0xA6, 0xB5, 0xBE, 0xE0, | ||
| 56 | }; | ||
| 57 | |||
| 58 | static int nouveau_optimus_dsm(acpi_handle handle, int func, int arg, uint32_t *result) | ||
| 59 | { | ||
| 60 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 61 | struct acpi_object_list input; | ||
| 62 | union acpi_object params[4]; | ||
| 63 | union acpi_object *obj; | ||
| 64 | int err; | ||
| 65 | |||
| 66 | input.count = 4; | ||
| 67 | input.pointer = params; | ||
| 68 | params[0].type = ACPI_TYPE_BUFFER; | ||
| 69 | params[0].buffer.length = sizeof(nouveau_op_dsm_muid); | ||
| 70 | params[0].buffer.pointer = (char *)nouveau_op_dsm_muid; | ||
| 71 | params[1].type = ACPI_TYPE_INTEGER; | ||
| 72 | params[1].integer.value = 0x00000100; | ||
| 73 | params[2].type = ACPI_TYPE_INTEGER; | ||
| 74 | params[2].integer.value = func; | ||
| 75 | params[3].type = ACPI_TYPE_BUFFER; | ||
| 76 | params[3].buffer.length = 0; | ||
| 77 | |||
| 78 | err = acpi_evaluate_object(handle, "_DSM", &input, &output); | ||
| 79 | if (err) { | ||
| 80 | printk(KERN_INFO "failed to evaluate _DSM: %d\n", err); | ||
| 81 | return err; | ||
| 82 | } | ||
| 83 | |||
| 84 | obj = (union acpi_object *)output.pointer; | ||
| 85 | |||
| 86 | if (obj->type == ACPI_TYPE_INTEGER) | ||
| 87 | if (obj->integer.value == 0x80000002) { | ||
| 88 | return -ENODEV; | ||
| 89 | } | ||
| 90 | |||
| 91 | if (obj->type == ACPI_TYPE_BUFFER) { | ||
| 92 | if (obj->buffer.length == 4 && result) { | ||
| 93 | *result = 0; | ||
| 94 | *result |= obj->buffer.pointer[0]; | ||
| 95 | *result |= (obj->buffer.pointer[1] << 8); | ||
| 96 | *result |= (obj->buffer.pointer[2] << 16); | ||
| 97 | *result |= (obj->buffer.pointer[3] << 24); | ||
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | kfree(output.pointer); | ||
| 102 | return 0; | ||
| 103 | } | ||
| 104 | |||
| 47 | static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) | 105 | static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) |
| 48 | { | 106 | { |
| 49 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | 107 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; |
| @@ -92,6 +150,8 @@ static int nouveau_dsm(acpi_handle handle, int func, int arg, uint32_t *result) | |||
| 92 | 150 | ||
| 93 | static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) | 151 | static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) |
| 94 | { | 152 | { |
| 153 | mxm_wmi_call_mxmx(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); | ||
| 154 | mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0); | ||
| 95 | return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); | 155 | return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); |
| 96 | } | 156 | } |
| 97 | 157 | ||
| @@ -148,11 +208,11 @@ static struct vga_switcheroo_handler nouveau_dsm_handler = { | |||
| 148 | .get_client_id = nouveau_dsm_get_client_id, | 208 | .get_client_id = nouveau_dsm_get_client_id, |
| 149 | }; | 209 | }; |
| 150 | 210 | ||
| 151 | static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) | 211 | static int nouveau_dsm_pci_probe(struct pci_dev *pdev) |
| 152 | { | 212 | { |
| 153 | acpi_handle dhandle, nvidia_handle; | 213 | acpi_handle dhandle, nvidia_handle; |
| 154 | acpi_status status; | 214 | acpi_status status; |
| 155 | int ret; | 215 | int ret, retval = 0; |
| 156 | uint32_t result; | 216 | uint32_t result; |
| 157 | 217 | ||
| 158 | dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); | 218 | dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); |
| @@ -166,11 +226,17 @@ static bool nouveau_dsm_pci_probe(struct pci_dev *pdev) | |||
| 166 | 226 | ||
| 167 | ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, | 227 | ret = nouveau_dsm(dhandle, NOUVEAU_DSM_SUPPORTED, |
| 168 | NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); | 228 | NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &result); |
| 169 | if (ret < 0) | 229 | if (ret == 0) |
| 170 | return false; | 230 | retval |= NOUVEAU_DSM_HAS_MUX; |
| 171 | 231 | ||
| 172 | nouveau_dsm_priv.dhandle = dhandle; | 232 | ret = nouveau_optimus_dsm(dhandle, 0, 0, &result); |
| 173 | return true; | 233 | if (ret == 0) |
| 234 | retval |= NOUVEAU_DSM_HAS_OPT; | ||
| 235 | |||
| 236 | if (retval) | ||
| 237 | nouveau_dsm_priv.dhandle = dhandle; | ||
| 238 | |||
| 239 | return retval; | ||
| 174 | } | 240 | } |
| 175 | 241 | ||
| 176 | static bool nouveau_dsm_detect(void) | 242 | static bool nouveau_dsm_detect(void) |
| @@ -179,22 +245,42 @@ static bool nouveau_dsm_detect(void) | |||
| 179 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; | 245 | struct acpi_buffer buffer = {sizeof(acpi_method_name), acpi_method_name}; |
| 180 | struct pci_dev *pdev = NULL; | 246 | struct pci_dev *pdev = NULL; |
| 181 | int has_dsm = 0; | 247 | int has_dsm = 0; |
| 248 | int has_optimus; | ||
| 182 | int vga_count = 0; | 249 | int vga_count = 0; |
| 250 | bool guid_valid; | ||
| 251 | int retval; | ||
| 252 | bool ret = false; | ||
| 253 | |||
| 254 | /* lookup the MXM GUID */ | ||
| 255 | guid_valid = mxm_wmi_supported(); | ||
| 183 | 256 | ||
| 257 | if (guid_valid) | ||
| 258 | printk("MXM: GUID detected in BIOS\n"); | ||
| 259 | |||
| 260 | /* now do DSM detection */ | ||
| 184 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { | 261 | while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { |
| 185 | vga_count++; | 262 | vga_count++; |
| 186 | 263 | ||
| 187 | has_dsm |= (nouveau_dsm_pci_probe(pdev) == true); | 264 | retval = nouveau_dsm_pci_probe(pdev); |
| 265 | printk("ret val is %d\n", retval); | ||
| 266 | if (retval & NOUVEAU_DSM_HAS_MUX) | ||
| 267 | has_dsm |= 1; | ||
| 268 | if (retval & NOUVEAU_DSM_HAS_OPT) | ||
| 269 | has_optimus = 1; | ||
| 188 | } | 270 | } |
| 189 | 271 | ||
| 190 | if (vga_count == 2 && has_dsm) { | 272 | if (vga_count == 2 && has_dsm && guid_valid) { |
| 191 | acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); | 273 | acpi_get_name(nouveau_dsm_priv.dhandle, ACPI_FULL_PATHNAME, &buffer); |
| 192 | printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", | 274 | printk(KERN_INFO "VGA switcheroo: detected DSM switching method %s handle\n", |
| 193 | acpi_method_name); | 275 | acpi_method_name); |
| 194 | nouveau_dsm_priv.dsm_detected = true; | 276 | nouveau_dsm_priv.dsm_detected = true; |
| 195 | return true; | 277 | ret = true; |
| 196 | } | 278 | } |
| 197 | return false; | 279 | |
| 280 | if (has_optimus == 1) | ||
| 281 | nouveau_dsm_priv.optimus_detected = true; | ||
| 282 | |||
| 283 | return ret; | ||
| 198 | } | 284 | } |
| 199 | 285 | ||
| 200 | void nouveau_register_dsm_handler(void) | 286 | void nouveau_register_dsm_handler(void) |
| @@ -247,7 +333,7 @@ bool nouveau_acpi_rom_supported(struct pci_dev *pdev) | |||
| 247 | acpi_status status; | 333 | acpi_status status; |
| 248 | acpi_handle dhandle, rom_handle; | 334 | acpi_handle dhandle, rom_handle; |
| 249 | 335 | ||
| 250 | if (!nouveau_dsm_priv.dsm_detected) | 336 | if (!nouveau_dsm_priv.dsm_detected && !nouveau_dsm_priv.optimus_detected) |
| 251 | return false; | 337 | return false; |
| 252 | 338 | ||
| 253 | dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); | 339 | dhandle = DEVICE_ACPI_HANDLE(&pdev->dev); |
diff --git a/drivers/gpu/vga/vgaarb.c b/drivers/gpu/vga/vgaarb.c index be8d4cb5861c..8a1021f2e319 100644 --- a/drivers/gpu/vga/vgaarb.c +++ b/drivers/gpu/vga/vgaarb.c | |||
| @@ -61,7 +61,7 @@ struct vga_device { | |||
| 61 | unsigned int mem_lock_cnt; /* legacy MEM lock count */ | 61 | unsigned int mem_lock_cnt; /* legacy MEM lock count */ |
| 62 | unsigned int io_norm_cnt; /* normal IO count */ | 62 | unsigned int io_norm_cnt; /* normal IO count */ |
| 63 | unsigned int mem_norm_cnt; /* normal MEM count */ | 63 | unsigned int mem_norm_cnt; /* normal MEM count */ |
| 64 | 64 | bool bridge_has_one_vga; | |
| 65 | /* allow IRQ enable/disable hook */ | 65 | /* allow IRQ enable/disable hook */ |
| 66 | void *cookie; | 66 | void *cookie; |
| 67 | void (*irq_set_state)(void *cookie, bool enable); | 67 | void (*irq_set_state)(void *cookie, bool enable); |
| @@ -165,6 +165,8 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, | |||
| 165 | unsigned int wants, legacy_wants, match; | 165 | unsigned int wants, legacy_wants, match; |
| 166 | struct vga_device *conflict; | 166 | struct vga_device *conflict; |
| 167 | unsigned int pci_bits; | 167 | unsigned int pci_bits; |
| 168 | u32 flags = 0; | ||
| 169 | |||
| 168 | /* Account for "normal" resources to lock. If we decode the legacy, | 170 | /* Account for "normal" resources to lock. If we decode the legacy, |
| 169 | * counterpart, we need to request it as well | 171 | * counterpart, we need to request it as well |
| 170 | */ | 172 | */ |
| @@ -237,16 +239,23 @@ static struct vga_device *__vga_tryget(struct vga_device *vgadev, | |||
| 237 | /* looks like he doesn't have a lock, we can steal | 239 | /* looks like he doesn't have a lock, we can steal |
| 238 | * them from him | 240 | * them from him |
| 239 | */ | 241 | */ |
| 240 | vga_irq_set_state(conflict, false); | ||
| 241 | 242 | ||
| 243 | flags = 0; | ||
| 242 | pci_bits = 0; | 244 | pci_bits = 0; |
| 243 | if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) | ||
| 244 | pci_bits |= PCI_COMMAND_MEMORY; | ||
| 245 | if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) | ||
| 246 | pci_bits |= PCI_COMMAND_IO; | ||
| 247 | 245 | ||
| 248 | pci_set_vga_state(conflict->pdev, false, pci_bits, | 246 | if (!conflict->bridge_has_one_vga) { |
| 249 | change_bridge); | 247 | vga_irq_set_state(conflict, false); |
| 248 | flags |= PCI_VGA_STATE_CHANGE_DECODES; | ||
| 249 | if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) | ||
| 250 | pci_bits |= PCI_COMMAND_MEMORY; | ||
| 251 | if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) | ||
| 252 | pci_bits |= PCI_COMMAND_IO; | ||
| 253 | } | ||
| 254 | |||
| 255 | if (change_bridge) | ||
| 256 | flags |= PCI_VGA_STATE_CHANGE_BRIDGE; | ||
| 257 | |||
| 258 | pci_set_vga_state(conflict->pdev, false, pci_bits, flags); | ||
| 250 | conflict->owns &= ~lwants; | 259 | conflict->owns &= ~lwants; |
| 251 | /* If he also owned non-legacy, that is no longer the case */ | 260 | /* If he also owned non-legacy, that is no longer the case */ |
| 252 | if (lwants & VGA_RSRC_LEGACY_MEM) | 261 | if (lwants & VGA_RSRC_LEGACY_MEM) |
| @@ -261,14 +270,24 @@ enable_them: | |||
| 261 | * also have in "decodes". We can lock resources we don't decode but | 270 | * also have in "decodes". We can lock resources we don't decode but |
| 262 | * not own them. | 271 | * not own them. |
| 263 | */ | 272 | */ |
| 273 | flags = 0; | ||
| 264 | pci_bits = 0; | 274 | pci_bits = 0; |
| 265 | if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) | ||
| 266 | pci_bits |= PCI_COMMAND_MEMORY; | ||
| 267 | if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) | ||
| 268 | pci_bits |= PCI_COMMAND_IO; | ||
| 269 | pci_set_vga_state(vgadev->pdev, true, pci_bits, !!(wants & VGA_RSRC_LEGACY_MASK)); | ||
| 270 | 275 | ||
| 271 | vga_irq_set_state(vgadev, true); | 276 | if (!vgadev->bridge_has_one_vga) { |
| 277 | flags |= PCI_VGA_STATE_CHANGE_DECODES; | ||
| 278 | if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) | ||
| 279 | pci_bits |= PCI_COMMAND_MEMORY; | ||
| 280 | if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) | ||
| 281 | pci_bits |= PCI_COMMAND_IO; | ||
| 282 | } | ||
| 283 | if (!!(wants & VGA_RSRC_LEGACY_MASK)) | ||
| 284 | flags |= PCI_VGA_STATE_CHANGE_BRIDGE; | ||
| 285 | |||
| 286 | pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); | ||
| 287 | |||
| 288 | if (!vgadev->bridge_has_one_vga) { | ||
| 289 | vga_irq_set_state(vgadev, true); | ||
| 290 | } | ||
| 272 | vgadev->owns |= (wants & vgadev->decodes); | 291 | vgadev->owns |= (wants & vgadev->decodes); |
| 273 | lock_them: | 292 | lock_them: |
| 274 | vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK); | 293 | vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK); |
| @@ -421,6 +440,62 @@ bail: | |||
| 421 | } | 440 | } |
| 422 | EXPORT_SYMBOL(vga_put); | 441 | EXPORT_SYMBOL(vga_put); |
| 423 | 442 | ||
| 443 | /* Rules for using a bridge to control a VGA descendant decoding: | ||
| 444 | if a bridge has only one VGA descendant then it can be used | ||
| 445 | to control the VGA routing for that device. | ||
| 446 | It should always use the bridge closest to the device to control it. | ||
| 447 | If a bridge has a direct VGA descendant, but also have a sub-bridge | ||
| 448 | VGA descendant then we cannot use that bridge to control the direct VGA descendant. | ||
| 449 | So for every device we register, we need to iterate all its parent bridges | ||
| 450 | so we can invalidate any devices using them properly. | ||
| 451 | */ | ||
| 452 | static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev) | ||
| 453 | { | ||
| 454 | struct vga_device *same_bridge_vgadev; | ||
| 455 | struct pci_bus *new_bus, *bus; | ||
| 456 | struct pci_dev *new_bridge, *bridge; | ||
| 457 | |||
| 458 | vgadev->bridge_has_one_vga = true; | ||
| 459 | |||
| 460 | if (list_empty(&vga_list)) | ||
| 461 | return; | ||
| 462 | |||
| 463 | /* okay iterate the new devices bridge hierarachy */ | ||
| 464 | new_bus = vgadev->pdev->bus; | ||
| 465 | while (new_bus) { | ||
| 466 | new_bridge = new_bus->self; | ||
| 467 | |||
| 468 | if (new_bridge) { | ||
| 469 | /* go through list of devices already registered */ | ||
| 470 | list_for_each_entry(same_bridge_vgadev, &vga_list, list) { | ||
| 471 | bus = same_bridge_vgadev->pdev->bus; | ||
| 472 | bridge = bus->self; | ||
| 473 | |||
| 474 | /* see if the share a bridge with this device */ | ||
| 475 | if (new_bridge == bridge) { | ||
| 476 | /* if their direct parent bridge is the same | ||
| 477 | as any bridge of this device then it can't be used | ||
| 478 | for that device */ | ||
| 479 | same_bridge_vgadev->bridge_has_one_vga = false; | ||
| 480 | } | ||
| 481 | |||
| 482 | /* now iterate the previous devices bridge hierarchy */ | ||
| 483 | /* if the new devices parent bridge is in the other devices | ||
| 484 | hierarchy then we can't use it to control this device */ | ||
| 485 | while (bus) { | ||
| 486 | bridge = bus->self; | ||
| 487 | if (bridge) { | ||
| 488 | if (bridge == vgadev->pdev->bus->self) | ||
| 489 | vgadev->bridge_has_one_vga = false; | ||
| 490 | } | ||
| 491 | bus = bus->parent; | ||
| 492 | } | ||
| 493 | } | ||
| 494 | } | ||
| 495 | new_bus = new_bus->parent; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | |||
| 424 | /* | 499 | /* |
| 425 | * Currently, we assume that the "initial" setup of the system is | 500 | * Currently, we assume that the "initial" setup of the system is |
| 426 | * not sane, that is we come up with conflicting devices and let | 501 | * not sane, that is we come up with conflicting devices and let |
| @@ -500,6 +575,8 @@ static bool vga_arbiter_add_pci_device(struct pci_dev *pdev) | |||
| 500 | vga_default = pci_dev_get(pdev); | 575 | vga_default = pci_dev_get(pdev); |
| 501 | #endif | 576 | #endif |
| 502 | 577 | ||
| 578 | vga_arbiter_check_bridge_sharing(vgadev); | ||
| 579 | |||
| 503 | /* Add to the list */ | 580 | /* Add to the list */ |
| 504 | list_add(&vgadev->list, &vga_list); | 581 | list_add(&vgadev->list, &vga_list); |
| 505 | vga_count++; | 582 | vga_count++; |
| @@ -1222,6 +1299,7 @@ static int __init vga_arb_device_init(void) | |||
| 1222 | { | 1299 | { |
| 1223 | int rc; | 1300 | int rc; |
| 1224 | struct pci_dev *pdev; | 1301 | struct pci_dev *pdev; |
| 1302 | struct vga_device *vgadev; | ||
| 1225 | 1303 | ||
| 1226 | rc = misc_register(&vga_arb_device); | 1304 | rc = misc_register(&vga_arb_device); |
| 1227 | if (rc < 0) | 1305 | if (rc < 0) |
| @@ -1238,6 +1316,13 @@ static int __init vga_arb_device_init(void) | |||
| 1238 | vga_arbiter_add_pci_device(pdev); | 1316 | vga_arbiter_add_pci_device(pdev); |
| 1239 | 1317 | ||
| 1240 | pr_info("vgaarb: loaded\n"); | 1318 | pr_info("vgaarb: loaded\n"); |
| 1319 | |||
| 1320 | list_for_each_entry(vgadev, &vga_list, list) { | ||
| 1321 | if (vgadev->bridge_has_one_vga) | ||
| 1322 | pr_info("vgaarb: bridge control possible %s\n", pci_name(vgadev->pdev)); | ||
| 1323 | else | ||
| 1324 | pr_info("vgaarb: no bridge control possible %s\n", pci_name(vgadev->pdev)); | ||
| 1325 | } | ||
| 1241 | return rc; | 1326 | return rc; |
| 1242 | } | 1327 | } |
| 1243 | subsys_initcall(vga_arb_device_init); | 1328 | subsys_initcall(vga_arb_device_init); |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 2472e7177b4b..a339237f4f96 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -2875,31 +2875,34 @@ static int pci_set_vga_state_arch(struct pci_dev *dev, bool decode, | |||
| 2875 | * @dev: the PCI device | 2875 | * @dev: the PCI device |
| 2876 | * @decode: true = enable decoding, false = disable decoding | 2876 | * @decode: true = enable decoding, false = disable decoding |
| 2877 | * @command_bits: PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY | 2877 | * @command_bits: PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY |
| 2878 | * @change_bridge: traverse ancestors and change bridges | 2878 | * @change_bridge_flags: traverse ancestors and change bridges |
| 2879 | * CHANGE_BRIDGE_ONLY / CHANGE_BRIDGE | ||
| 2879 | */ | 2880 | */ |
| 2880 | int pci_set_vga_state(struct pci_dev *dev, bool decode, | 2881 | int pci_set_vga_state(struct pci_dev *dev, bool decode, |
| 2881 | unsigned int command_bits, bool change_bridge) | 2882 | unsigned int command_bits, u32 flags) |
| 2882 | { | 2883 | { |
| 2883 | struct pci_bus *bus; | 2884 | struct pci_bus *bus; |
| 2884 | struct pci_dev *bridge; | 2885 | struct pci_dev *bridge; |
| 2885 | u16 cmd; | 2886 | u16 cmd; |
| 2886 | int rc; | 2887 | int rc; |
| 2887 | 2888 | ||
| 2888 | WARN_ON(command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)); | 2889 | WARN_ON((flags & PCI_VGA_STATE_CHANGE_DECODES) & (command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY))); |
| 2889 | 2890 | ||
| 2890 | /* ARCH specific VGA enables */ | 2891 | /* ARCH specific VGA enables */ |
| 2891 | rc = pci_set_vga_state_arch(dev, decode, command_bits, change_bridge); | 2892 | rc = pci_set_vga_state_arch(dev, decode, command_bits, flags); |
| 2892 | if (rc) | 2893 | if (rc) |
| 2893 | return rc; | 2894 | return rc; |
| 2894 | 2895 | ||
| 2895 | pci_read_config_word(dev, PCI_COMMAND, &cmd); | 2896 | if (flags & PCI_VGA_STATE_CHANGE_DECODES) { |
| 2896 | if (decode == true) | 2897 | pci_read_config_word(dev, PCI_COMMAND, &cmd); |
| 2897 | cmd |= command_bits; | 2898 | if (decode == true) |
| 2898 | else | 2899 | cmd |= command_bits; |
| 2899 | cmd &= ~command_bits; | 2900 | else |
| 2900 | pci_write_config_word(dev, PCI_COMMAND, cmd); | 2901 | cmd &= ~command_bits; |
| 2902 | pci_write_config_word(dev, PCI_COMMAND, cmd); | ||
| 2903 | } | ||
| 2901 | 2904 | ||
| 2902 | if (change_bridge == false) | 2905 | if (!(flags & PCI_VGA_STATE_CHANGE_BRIDGE)) |
| 2903 | return 0; | 2906 | return 0; |
| 2904 | 2907 | ||
| 2905 | bus = dev->bus; | 2908 | bus = dev->bus; |
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index 0485e394712a..94914572dd7f 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig | |||
| @@ -753,4 +753,11 @@ config SAMSUNG_LAPTOP | |||
| 753 | To compile this driver as a module, choose M here: the module | 753 | To compile this driver as a module, choose M here: the module |
| 754 | will be called samsung-laptop. | 754 | will be called samsung-laptop. |
| 755 | 755 | ||
| 756 | config MXM_WMI | ||
| 757 | tristate "WMI support for MXM Laptop Graphics" | ||
| 758 | depends on WMI | ||
| 759 | ---help--- | ||
| 760 | MXM is a standard for laptop graphics cards, the WMI interface | ||
| 761 | is required for switchable nvidia graphics machines | ||
| 762 | |||
| 756 | endif # X86_PLATFORM_DEVICES | 763 | endif # X86_PLATFORM_DEVICES |
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 029e8861d086..a7ab3bc7b3a1 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile | |||
| @@ -42,3 +42,4 @@ obj-$(CONFIG_XO15_EBOOK) += xo15-ebook.o | |||
| 42 | obj-$(CONFIG_IBM_RTL) += ibm_rtl.o | 42 | obj-$(CONFIG_IBM_RTL) += ibm_rtl.o |
| 43 | obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o | 43 | obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o |
| 44 | obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o | 44 | obj-$(CONFIG_INTEL_MFLD_THERMAL) += intel_mid_thermal.o |
| 45 | obj-$(CONFIG_MXM_WMI) += mxm-wmi.o | ||
diff --git a/drivers/platform/x86/mxm-wmi.c b/drivers/platform/x86/mxm-wmi.c new file mode 100644 index 000000000000..0aea63b3729a --- /dev/null +++ b/drivers/platform/x86/mxm-wmi.c | |||
| @@ -0,0 +1,111 @@ | |||
| 1 | /* | ||
| 2 | * MXM WMI driver | ||
| 3 | * | ||
| 4 | * Copyright(C) 2010 Red Hat. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | */ | ||
| 20 | #include <linux/kernel.h> | ||
| 21 | #include <linux/module.h> | ||
| 22 | #include <linux/init.h> | ||
| 23 | #include <acpi/acpi_bus.h> | ||
| 24 | #include <acpi/acpi_drivers.h> | ||
| 25 | |||
| 26 | MODULE_AUTHOR("Dave Airlie"); | ||
| 27 | MODULE_DESCRIPTION("MXM WMI Driver"); | ||
| 28 | MODULE_LICENSE("GPL"); | ||
| 29 | |||
| 30 | #define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" | ||
| 31 | |||
| 32 | MODULE_ALIAS("wmi:"MXM_WMMX_GUID); | ||
| 33 | |||
| 34 | #define MXM_WMMX_FUNC_MXDS 0x5344584D /* "MXDS" */ | ||
| 35 | #define MXM_WMMX_FUNC_MXMX 0x53445344 /* "MXMX" */ | ||
| 36 | |||
| 37 | struct mxds_args { | ||
| 38 | u32 func; | ||
| 39 | u32 args; | ||
| 40 | u32 xarg; | ||
| 41 | }; | ||
| 42 | |||
| 43 | int mxm_wmi_call_mxds(int adapter) | ||
| 44 | { | ||
| 45 | struct mxds_args args = { | ||
| 46 | .func = MXM_WMMX_FUNC_MXDS, | ||
| 47 | .args = 0, | ||
| 48 | .xarg = 1, | ||
| 49 | }; | ||
| 50 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | ||
| 51 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 52 | acpi_status status; | ||
| 53 | |||
| 54 | printk("calling mux switch %d\n", adapter); | ||
| 55 | |||
| 56 | status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input, | ||
| 57 | &output); | ||
| 58 | |||
| 59 | if (ACPI_FAILURE(status)) | ||
| 60 | return status; | ||
| 61 | |||
| 62 | printk("mux switched %d\n", status); | ||
| 63 | return 0; | ||
| 64 | |||
| 65 | } | ||
| 66 | EXPORT_SYMBOL_GPL(mxm_wmi_call_mxds); | ||
| 67 | |||
| 68 | int mxm_wmi_call_mxmx(int adapter) | ||
| 69 | { | ||
| 70 | struct mxds_args args = { | ||
| 71 | .func = MXM_WMMX_FUNC_MXMX, | ||
| 72 | .args = 0, | ||
| 73 | .xarg = 1, | ||
| 74 | }; | ||
| 75 | struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; | ||
| 76 | struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; | ||
| 77 | acpi_status status; | ||
| 78 | |||
| 79 | printk("calling mux switch %d\n", adapter); | ||
| 80 | |||
| 81 | status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input, | ||
| 82 | &output); | ||
| 83 | |||
| 84 | if (ACPI_FAILURE(status)) | ||
| 85 | return status; | ||
| 86 | |||
| 87 | printk("mux mutex set switched %d\n", status); | ||
| 88 | return 0; | ||
| 89 | |||
| 90 | } | ||
| 91 | EXPORT_SYMBOL_GPL(mxm_wmi_call_mxmx); | ||
| 92 | |||
| 93 | bool mxm_wmi_supported(void) | ||
| 94 | { | ||
| 95 | bool guid_valid; | ||
| 96 | guid_valid = wmi_has_guid(MXM_WMMX_GUID); | ||
| 97 | return guid_valid; | ||
| 98 | } | ||
| 99 | EXPORT_SYMBOL_GPL(mxm_wmi_supported); | ||
| 100 | |||
| 101 | static int __init mxm_wmi_init(void) | ||
| 102 | { | ||
| 103 | return 0; | ||
| 104 | } | ||
| 105 | |||
| 106 | static void __exit mxm_wmi_exit(void) | ||
| 107 | { | ||
| 108 | } | ||
| 109 | |||
| 110 | module_init(mxm_wmi_init); | ||
| 111 | module_exit(mxm_wmi_exit); | ||
diff --git a/include/linux/mxm-wmi.h b/include/linux/mxm-wmi.h new file mode 100644 index 000000000000..617a2950523c --- /dev/null +++ b/include/linux/mxm-wmi.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | /* | ||
| 2 | * MXM WMI driver | ||
| 3 | * | ||
| 4 | * Copyright(C) 2010 Red Hat. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify | ||
| 7 | * it under the terms of the GNU General Public License as published by | ||
| 8 | * the Free Software Foundation; either version 2 of the License, or | ||
| 9 | * (at your option) any later version. | ||
| 10 | * | ||
| 11 | * This program is distributed in the hope that it will be useful, | ||
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 14 | * GNU General Public License for more details. | ||
| 15 | * | ||
| 16 | * You should have received a copy of the GNU General Public License | ||
| 17 | * along with this program; if not, write to the Free Software | ||
| 18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
| 19 | */ | ||
| 20 | |||
| 21 | #ifndef MXM_WMI_H | ||
| 22 | #define MXM_WMI_H | ||
| 23 | |||
| 24 | /* discrete adapters */ | ||
| 25 | #define MXM_MXDS_ADAPTER_0 0x0 | ||
| 26 | #define MXM_MXDS_ADAPTER_1 0x0 | ||
| 27 | /* integrated adapter */ | ||
| 28 | #define MXM_MXDS_ADAPTER_IGD 0x10 | ||
| 29 | int mxm_wmi_call_mxds(int adapter); | ||
| 30 | int mxm_wmi_call_mxmx(int adapter); | ||
| 31 | bool mxm_wmi_supported(void); | ||
| 32 | |||
| 33 | #endif | ||
diff --git a/include/linux/pci.h b/include/linux/pci.h index 96f70d7e058d..f2e57b2e6a81 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h | |||
| @@ -915,8 +915,11 @@ int pci_cfg_space_size_ext(struct pci_dev *dev); | |||
| 915 | int pci_cfg_space_size(struct pci_dev *dev); | 915 | int pci_cfg_space_size(struct pci_dev *dev); |
| 916 | unsigned char pci_bus_max_busnr(struct pci_bus *bus); | 916 | unsigned char pci_bus_max_busnr(struct pci_bus *bus); |
| 917 | 917 | ||
| 918 | #define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) | ||
| 919 | #define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) | ||
| 920 | |||
| 918 | int pci_set_vga_state(struct pci_dev *pdev, bool decode, | 921 | int pci_set_vga_state(struct pci_dev *pdev, bool decode, |
| 919 | unsigned int command_bits, bool change_bridge); | 922 | unsigned int command_bits, u32 flags); |
| 920 | /* kmem_cache style wrapper around pci_alloc_consistent() */ | 923 | /* kmem_cache style wrapper around pci_alloc_consistent() */ |
| 921 | 924 | ||
| 922 | #include <linux/pci-dma.h> | 925 | #include <linux/pci-dma.h> |
| @@ -1061,7 +1064,7 @@ static inline int pci_proc_domain(struct pci_bus *bus) | |||
| 1061 | 1064 | ||
| 1062 | /* some architectures require additional setup to direct VGA traffic */ | 1065 | /* some architectures require additional setup to direct VGA traffic */ |
| 1063 | typedef int (*arch_set_vga_state_t)(struct pci_dev *pdev, bool decode, | 1066 | typedef int (*arch_set_vga_state_t)(struct pci_dev *pdev, bool decode, |
| 1064 | unsigned int command_bits, bool change_bridge); | 1067 | unsigned int command_bits, u32 flags); |
| 1065 | extern void pci_register_set_vga_state(arch_set_vga_state_t func); | 1068 | extern void pci_register_set_vga_state(arch_set_vga_state_t func); |
| 1066 | 1069 | ||
| 1067 | #else /* CONFIG_PCI is not enabled */ | 1070 | #else /* CONFIG_PCI is not enabled */ |
