diff -urNp linux-2.6.37//drivers/gpu/drm/drm_fops.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/drm_fops.c --- linux-2.6.37//drivers/gpu/drm/drm_fops.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/drm_fops.c 2011-01-15 15:12:12.000000000 -0200 @@ -236,6 +236,8 @@ static int drm_open_helper(struct inode return -EBUSY; /* No exclusive opens */ if (!drm_cpu_valid()) return -EINVAL; + if (dev->switch_power_state != DRM_SWITCH_POWER_ON) + return -EINVAL; DRM_DEBUG("pid = %d, minor = %d\n", task_pid_nr(current), minor_id); diff -urNp linux-2.6.37//drivers/gpu/drm/i915/i915_dma.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/i915/i915_dma.c --- linux-2.6.37//drivers/gpu/drm/i915/i915_dma.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/i915/i915_dma.c 2011-01-15 15:13:34.000000000 -0200 @@ -1176,12 +1176,16 @@ static void i915_switcheroo_set_state(st pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; if (state == VGA_SWITCHEROO_ON) { printk(KERN_INFO "i915: switched on\n"); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; /* i915 resume handler doesn't set to D0 */ pci_set_power_state(dev->pdev, PCI_D0); i915_resume(dev); + dev->switch_power_state = DRM_SWITCH_POWER_ON; } else { printk(KERN_ERR "i915: switched off\n"); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; i915_suspend(dev, pmm); + dev->switch_power_state = DRM_SWITCH_POWER_OFF; } } @@ -1252,6 +1256,7 @@ static int i915_load_modeset_init(struct ret = vga_switcheroo_register_client(dev->pdev, i915_switcheroo_set_state, + NULL, i915_switcheroo_can_switch); if (ret) goto cleanup_vga_client; diff -urNp linux-2.6.37//drivers/gpu/drm/i915/i915_drv.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/i915/i915_drv.c --- linux-2.6.37//drivers/gpu/drm/i915/i915_drv.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/i915/i915_drv.c 2011-01-15 15:16:23.000000000 -0200 @@ -248,6 +248,8 @@ static int i915_drm_freeze(struct drm_de { struct drm_i915_private *dev_priv = dev->dev_private; + drm_kms_helper_poll_disable(dev); + pci_save_state(dev->pdev); /* If KMS is active, we do the leavevt stuff here */ @@ -284,7 +286,8 @@ int i915_suspend(struct drm_device *dev, if (state.event == PM_EVENT_PRETHAW) return 0; - drm_kms_helper_poll_disable(dev); + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; error = i915_drm_freeze(dev); if (error) @@ -332,6 +335,9 @@ int i915_resume(struct drm_device *dev) { int ret; + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + if (pci_enable_device(dev->pdev)) return -EIO; @@ -523,6 +529,9 @@ static int i915_pm_suspend(struct device return -ENODEV; } + if (drm_dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + error = i915_drm_freeze(drm_dev); if (error) return error; diff -urNp linux-2.6.37//drivers/gpu/drm/i915/intel_acpi.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/i915/intel_acpi.c --- linux-2.6.37//drivers/gpu/drm/i915/intel_acpi.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/i915/intel_acpi.c 2011-01-15 14:53:53.000000000 -0200 @@ -182,8 +182,12 @@ static void intel_dsm_platform_mux_info( DRM_DEBUG_DRIVER(" hpd mux info: %s\n", intel_dsm_mux_type(info->buffer.pointer[3])); } + } else if (pkg->type == ACPI_TYPE_BUFFER) { + DRM_ERROR("MUX INFO returned buffer %d %p\n", pkg->buffer.length, pkg->buffer.pointer); + if (pkg->buffer.length == 4) + DRM_ERROR("returned %08x\n", *(uint32_t *)pkg->buffer.pointer); } else { - DRM_ERROR("MUX INFO call failed\n"); + DRM_ERROR("MUX INFO call failed %d\n", pkg->type); } out: diff -urNp linux-2.6.37//drivers/gpu/drm/nouveau/Kconfig linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/Kconfig --- linux-2.6.37//drivers/gpu/drm/nouveau/Kconfig 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/Kconfig 2011-01-15 15:03:23.000000000 -0200 @@ -11,6 +11,7 @@ config DRM_NOUVEAU select FRAMEBUFFER_CONSOLE if !EMBEDDED select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT select ACPI_VIDEO if ACPI + select MXM_WMI if ACPI help Choose this option for open-source nVidia support. diff -urNp linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_acpi.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_acpi.c --- linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_acpi.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_acpi.c 2011-01-15 15:44:50.000000000 -0200 @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include "drmP.h" #include "drm.h" @@ -92,20 +94,13 @@ static int nouveau_dsm(acpi_handle handl static int nouveau_dsm_switch_mux(acpi_handle handle, int mux_id) { + int output_id = 0; + //hack output id using acpi lcd id + //sony vaio 0x80010100 + mxm_wmi_call_mxds(mux_id == NOUVEAU_DSM_LED_STAMINA ? MXM_MXDS_ADAPTER_IGD : MXM_MXDS_ADAPTER_0, output_id); return nouveau_dsm(handle, NOUVEAU_DSM_LED, mux_id, NULL); } -static int nouveau_dsm_set_discrete_state(acpi_handle handle, enum vga_switcheroo_state state) -{ - int arg; - if (state == VGA_SWITCHEROO_ON) - arg = NOUVEAU_DSM_POWER_SPEED; - else - arg = NOUVEAU_DSM_POWER_STAMINA; - nouveau_dsm(handle, NOUVEAU_DSM_POWER, arg, NULL); - return 0; -} - static int nouveau_dsm_switchto(enum vga_switcheroo_client_id id) { if (id == VGA_SWITCHEROO_IGD) @@ -114,15 +109,6 @@ static int nouveau_dsm_switchto(enum vga return nouveau_dsm_switch_mux(nouveau_dsm_priv.dhandle, NOUVEAU_DSM_LED_SPEED); } -static int nouveau_dsm_power_state(enum vga_switcheroo_client_id id, - enum vga_switcheroo_state state) -{ - if (id == VGA_SWITCHEROO_IGD) - return 0; - - return nouveau_dsm_set_discrete_state(nouveau_dsm_priv.dhandle, state); -} - static int nouveau_dsm_init(void) { return 0; @@ -130,15 +116,19 @@ static int nouveau_dsm_init(void) static int nouveau_dsm_get_client_id(struct pci_dev *pdev) { - if (nouveau_dsm_priv.dhandle == DEVICE_ACPI_HANDLE(&pdev->dev)) + /* easy option one - intel vendor ID means Integrated */ + if (pdev->vendor == PCI_VENDOR_ID_INTEL) return VGA_SWITCHEROO_IGD; - else - return VGA_SWITCHEROO_DIS; + + /* is this device on Bus 0? - this may need improving */ + if (pdev->bus->number == 0) + return VGA_SWITCHEROO_IGD; + + return VGA_SWITCHEROO_DIS; } static struct vga_switcheroo_handler nouveau_dsm_handler = { .switchto = nouveau_dsm_switchto, - .power_state = nouveau_dsm_power_state, .init = nouveau_dsm_init, .get_client_id = nouveau_dsm_get_client_id, }; @@ -175,6 +165,14 @@ static bool nouveau_dsm_detect(void) struct pci_dev *pdev = NULL; int has_dsm = 0; int vga_count = 0; + bool guid_valid; + + /* lookup the GUID */ + guid_valid = mxm_wmi_supported(); + if (!guid_valid) + return false; + + printk("MXM GUID detected in BIOS\n"); while ((pdev = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, pdev)) != NULL) { vga_count++; diff -urNp linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_drv.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_drv.c --- linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_drv.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_drv.c 2011-01-15 15:17:35.000000000 -0200 @@ -167,6 +167,9 @@ nouveau_pci_suspend(struct pci_dev *pdev if (pm_state.event == PM_EVENT_PRETHAW) return 0; + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + NV_INFO(dev, "Disabling fbcon acceleration...\n"); nouveau_fbcon_save_disable_accel(dev); @@ -263,6 +266,9 @@ nouveau_pci_resume(struct pci_dev *pdev) struct drm_crtc *crtc; int ret, i; + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) + return 0; + nouveau_fbcon_save_disable_accel(dev); NV_INFO(dev, "We're back, enabling device...\n"); diff -urNp linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_drv.h linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_drv.h --- linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_drv.h 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_drv.h 2011-01-15 15:18:35.000000000 -0200 @@ -686,6 +686,8 @@ struct drm_nouveau_private { struct nouveau_fbdev *nfbdev; struct apertures_struct *apertures; + + bool powered_down; }; static inline struct drm_nouveau_private * diff -urNp linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_state.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_state.c --- linux-2.6.37//drivers/gpu/drm/nouveau/nouveau_state.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/nouveau/nouveau_state.c 2011-01-15 15:38:42.000000000 -0200 @@ -531,15 +531,25 @@ static void nouveau_switcheroo_set_state pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; if (state == VGA_SWITCHEROO_ON) { printk(KERN_ERR "VGA switcheroo: switched nouveau on\n"); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; nouveau_pci_resume(pdev); drm_kms_helper_poll_enable(dev); + dev->switch_power_state = DRM_SWITCH_POWER_ON; } else { printk(KERN_ERR "VGA switcheroo: switched nouveau off\n"); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; drm_kms_helper_poll_disable(dev); nouveau_pci_suspend(pdev, pmm); + dev->switch_power_state = DRM_SWITCH_POWER_OFF; } } +static void nouveau_switcheroo_reprobe(struct pci_dev *pdev) +{ + struct drm_device *dev = pci_get_drvdata(pdev); + nouveau_fbcon_output_poll_changed(dev); +} + static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev) { struct drm_device *dev = pci_get_drvdata(pdev); @@ -560,6 +570,7 @@ nouveau_card_init(struct drm_device *dev vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode); vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state, + nouveau_switcheroo_reprobe, nouveau_switcheroo_can_switch); /* Initialise internal driver API hooks */ @@ -980,6 +991,7 @@ err_out: void nouveau_lastclose(struct drm_device *dev) { + vga_switcheroo_process_delayed_switch(); } int nouveau_unload(struct drm_device *dev) diff -urNp linux-2.6.37//drivers/gpu/drm/radeon/radeon_device.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/radeon/radeon_device.c --- linux-2.6.37//drivers/gpu/drm/radeon/radeon_device.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/radeon/radeon_device.c 2011-01-15 15:23:55.000000000 -0200 @@ -636,20 +636,20 @@ void radeon_check_arguments(struct radeo static void radeon_switcheroo_set_state(struct pci_dev *pdev, enum vga_switcheroo_state state) { struct drm_device *dev = pci_get_drvdata(pdev); - struct radeon_device *rdev = dev->dev_private; pm_message_t pmm = { .event = PM_EVENT_SUSPEND }; if (state == VGA_SWITCHEROO_ON) { printk(KERN_INFO "radeon: switched on\n"); /* don't suspend or resume card normally */ - rdev->powered_down = false; + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; radeon_resume_kms(dev); + dev->switch_power_state = DRM_SWITCH_POWER_ON; drm_kms_helper_poll_enable(dev); } else { printk(KERN_INFO "radeon: switched off\n"); drm_kms_helper_poll_disable(dev); + dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; radeon_suspend_kms(dev, pmm); - /* don't suspend or resume card normally */ - rdev->powered_down = true; + dev->switch_power_state = DRM_SWITCH_POWER_OFF; } } @@ -773,6 +773,7 @@ int radeon_device_init(struct radeon_dev vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode); vga_switcheroo_register_client(rdev->pdev, radeon_switcheroo_set_state, + NULL, radeon_switcheroo_can_switch); r = radeon_init(rdev); @@ -835,7 +836,7 @@ int radeon_suspend_kms(struct drm_device } rdev = dev->dev_private; - if (rdev->powered_down) + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; /* turn off display hw */ @@ -893,7 +894,7 @@ int radeon_resume_kms(struct drm_device struct drm_connector *connector; struct radeon_device *rdev = dev->dev_private; - if (rdev->powered_down) + if (dev->switch_power_state == DRM_SWITCH_POWER_OFF) return 0; acquire_console_sem(); diff -urNp linux-2.6.37//drivers/gpu/drm/radeon/radeon.h linux-2.6.37-OPTIMUS//drivers/gpu/drm/radeon/radeon.h --- linux-2.6.37//drivers/gpu/drm/radeon/radeon.h 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/radeon/radeon.h 2011-01-15 15:21:15.000000000 -0200 @@ -1110,7 +1110,6 @@ struct radeon_device { uint8_t audio_status_bits; uint8_t audio_category_code; - bool powered_down; struct notifier_block acpi_nb; /* only one userspace can use Hyperz features at a time */ struct drm_file *hyperz_filp; diff -urNp linux-2.6.37//drivers/gpu/drm/radeon/radeon_kms.c linux-2.6.37-OPTIMUS//drivers/gpu/drm/radeon/radeon_kms.c --- linux-2.6.37//drivers/gpu/drm/radeon/radeon_kms.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/drm/radeon/radeon_kms.c 2011-01-15 15:24:36.000000000 -0200 @@ -203,10 +203,6 @@ int radeon_info_ioctl(struct drm_device */ int radeon_driver_firstopen_kms(struct drm_device *dev) { - struct radeon_device *rdev = dev->dev_private; - - if (rdev->powered_down) - return -EINVAL; return 0; } diff -urNp linux-2.6.37//drivers/gpu/vga/vgaarb.c linux-2.6.37-OPTIMUS//drivers/gpu/vga/vgaarb.c --- linux-2.6.37//drivers/gpu/vga/vgaarb.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/vga/vgaarb.c 2011-01-15 15:34:05.000000000 -0200 @@ -62,6 +62,7 @@ struct vga_device { unsigned int io_norm_cnt; /* normal IO count */ unsigned int mem_norm_cnt; /* normal MEM count */ + bool bridge_has_one_vga; /* allow IRQ enable/disable hook */ void *cookie; void (*irq_set_state)(void *cookie, bool enable); @@ -165,6 +166,8 @@ static struct vga_device *__vga_tryget(s unsigned int wants, legacy_wants, match; struct vga_device *conflict; unsigned int pci_bits; + u32 flags = 0; + /* Account for "normal" resources to lock. If we decode the legacy, * counterpart, we need to request it as well */ @@ -237,16 +240,22 @@ static struct vga_device *__vga_tryget(s /* looks like he doesn't have a lock, we can steal * them from him */ - vga_irq_set_state(conflict, false); + flags = 0; pci_bits = 0; - if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) - pci_bits |= PCI_COMMAND_MEMORY; - if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) - pci_bits |= PCI_COMMAND_IO; - - pci_set_vga_state(conflict->pdev, false, pci_bits, - change_bridge); + if (!conflict->bridge_has_one_vga) { + vga_irq_set_state(conflict, false); + flags |= PCI_VGA_STATE_CHANGE_DECODES; + if (lwants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) + pci_bits |= PCI_COMMAND_MEMORY; + if (lwants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) + pci_bits |= PCI_COMMAND_IO; + } + + if (change_bridge) + flags |= PCI_VGA_STATE_CHANGE_BRIDGE; + + pci_set_vga_state(conflict->pdev, false, pci_bits, flags); conflict->owns &= ~lwants; /* If he also owned non-legacy, that is no longer the case */ if (lwants & VGA_RSRC_LEGACY_MEM) @@ -261,14 +270,23 @@ enable_them: * also have in "decodes". We can lock resources we don't decode but * not own them. */ + flags = 0; pci_bits = 0; - if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) - pci_bits |= PCI_COMMAND_MEMORY; - if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) - pci_bits |= PCI_COMMAND_IO; - pci_set_vga_state(vgadev->pdev, true, pci_bits, !!(wants & VGA_RSRC_LEGACY_MASK)); - - vga_irq_set_state(vgadev, true); + if (!vgadev->bridge_has_one_vga) { + flags |= PCI_VGA_STATE_CHANGE_DECODES; + if (wants & (VGA_RSRC_LEGACY_MEM|VGA_RSRC_NORMAL_MEM)) + pci_bits |= PCI_COMMAND_MEMORY; + if (wants & (VGA_RSRC_LEGACY_IO|VGA_RSRC_NORMAL_IO)) + pci_bits |= PCI_COMMAND_IO; + } + if (!!(wants & VGA_RSRC_LEGACY_MASK)) + flags |= PCI_VGA_STATE_CHANGE_BRIDGE; + + pci_set_vga_state(vgadev->pdev, true, pci_bits, flags); + + if (!vgadev->bridge_has_one_vga) { + vga_irq_set_state(vgadev, true); + } vgadev->owns |= (wants & vgadev->decodes); lock_them: vgadev->locks |= (rsrc & VGA_RSRC_LEGACY_MASK); @@ -421,6 +439,62 @@ bail: } EXPORT_SYMBOL(vga_put); +/* Rules for using a bridge to control a VGA descendant decoding: + if a bridge has only one VGA descendant then it can be used + to control the VGA routing for that device. + It should always use the bridge closest to the device to control it. + If a bridge has a direct VGA descendant, but also have a sub-bridge + VGA descendant then we cannot use that bridge to control the direct VGA descendant. + So for every device we register, we need to iterate all its parent bridges + so we can invalidate any devices using them properly. +*/ +static void vga_arbiter_check_bridge_sharing(struct vga_device *vgadev) +{ + struct vga_device *same_bridge_vgadev; + struct pci_bus *new_bus, *bus; + struct pci_dev *new_bridge, *bridge; + + vgadev->bridge_has_one_vga = true; + + if (list_empty(&vga_list)) + return; + + /* okay iterate the new devices bridge hierarachy */ + new_bus = vgadev->pdev->bus; + while (new_bus) { + new_bridge = new_bus->self; + + if (new_bridge) { + /* go through list of devices already registered */ + list_for_each_entry(same_bridge_vgadev, &vga_list, list) { + bus = same_bridge_vgadev->pdev->bus; + bridge = bus->self; + + /* see if the share a bridge with this device */ + if (new_bridge == bridge) { + /* if their direct parent bridge is the same + as any bridge of this device then it can't be used + for that device */ + same_bridge_vgadev->bridge_has_one_vga = false; + } + + /* now iterate the previous devices bridge hierarchy */ + /* if the new devices parent bridge is in the other devices + hierarchy then we can't use it to control this device */ + while (bus) { + bridge = bus->self; + if (bridge) { + if (bridge == vgadev->pdev->bus->self) + vgadev->bridge_has_one_vga = false; + } + bus = bus->parent; + } + } + } + new_bus = new_bus->parent; + } +} + /* * Currently, we assume that the "initial" setup of the system is * not sane, that is we come up with conflicting devices and let @@ -500,6 +574,8 @@ static bool vga_arbiter_add_pci_device(s vga_default = pci_dev_get(pdev); #endif + vga_arbiter_check_bridge_sharing(vgadev); + /* Add to the list */ list_add(&vgadev->list, &vga_list); vga_count++; @@ -1222,6 +1298,7 @@ static int __init vga_arb_device_init(vo { int rc; struct pci_dev *pdev; + struct vga_device *vgadev; rc = misc_register(&vga_arb_device); if (rc < 0) @@ -1238,6 +1315,13 @@ static int __init vga_arb_device_init(vo vga_arbiter_add_pci_device(pdev); pr_info("vgaarb: loaded\n"); + + list_for_each_entry(vgadev, &vga_list, list) { + if (vgadev->bridge_has_one_vga) + pr_info("vgaarb: bridge control possible %s\n", pci_name(vgadev->pdev)); + else + pr_info("vgaarb: no bridge control possible %s\n", pci_name(vgadev->pdev)); + } return rc; } subsys_initcall(vga_arb_device_init); diff -urNp linux-2.6.37//drivers/gpu/vga/vga_switcheroo.c linux-2.6.37-OPTIMUS//drivers/gpu/vga/vga_switcheroo.c --- linux-2.6.37//drivers/gpu/vga/vga_switcheroo.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/gpu/vga/vga_switcheroo.c 2011-01-15 15:43:37.000000000 -0200 @@ -33,6 +33,7 @@ struct vga_switcheroo_client { struct fb_info *fb_info; int pwr_state; void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state); + void (*reprobe)(struct pci_dev *pdev); bool (*can_switch)(struct pci_dev *pdev); int id; bool active; @@ -103,6 +104,7 @@ static void vga_switcheroo_enable(void) int vga_switcheroo_register_client(struct pci_dev *pdev, void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state), + void (*reprobe)(struct pci_dev *pdev), bool (*can_switch)(struct pci_dev *pdev)) { int index; @@ -117,6 +119,7 @@ int vga_switcheroo_register_client(struc vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON; vgasr_priv.clients[index].pdev = pdev; vgasr_priv.clients[index].set_gpu_state = set_gpu_state; + vgasr_priv.clients[index].reprobe = reprobe; vgasr_priv.clients[index].can_switch = can_switch; vgasr_priv.clients[index].id = -1; if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) @@ -174,7 +177,8 @@ static int vga_switcheroo_show(struct se int i; mutex_lock(&vgasr_mutex); for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { - seq_printf(m, "%d:%c:%s:%s\n", i, + seq_printf(m, "%d:%s:%c:%s:%s\n", i, + vgasr_priv.clients[i].id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", vgasr_priv.clients[i].active ? '+' : ' ', vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off", pci_name(vgasr_priv.clients[i].pdev)); @@ -190,9 +194,8 @@ static int vga_switcheroo_debugfs_open(s static int vga_switchon(struct vga_switcheroo_client *client) { - int ret; - - ret = vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); + if (vgasr_priv.handler->power_state) + vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON); /* call the driver callback to turn on device */ client->set_gpu_state(client->pdev, VGA_SWITCHEROO_ON); client->pwr_state = VGA_SWITCHEROO_ON; @@ -203,12 +206,14 @@ static int vga_switchoff(struct vga_swit { /* call the driver callback to turn off device */ client->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF); - vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); + if (vgasr_priv.handler->power_state) + vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_OFF); client->pwr_state = VGA_SWITCHEROO_OFF; return 0; } -static int vga_switchto(struct vga_switcheroo_client *new_client) +/* stage one happens before delay */ +static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) { int ret; int i; @@ -235,10 +240,28 @@ static int vga_switchto(struct vga_switc vga_switchon(new_client); /* swap shadow resource to denote boot VGA device has changed so X starts on new device */ - active->active = false; - active->pdev->resource[PCI_ROM_RESOURCE].flags &= ~IORESOURCE_ROM_SHADOW; new_client->pdev->resource[PCI_ROM_RESOURCE].flags |= IORESOURCE_ROM_SHADOW; + return 0; +} + +/* post delay */ +static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) +{ + int ret; + int i; + struct vga_switcheroo_client *active = NULL; + + for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { + if (vgasr_priv.clients[i].active == true) { + active = &vgasr_priv.clients[i]; + break; + } + } + if (!active) + return 0; + + active->active = false; if (new_client->fb_info) { struct fb_event event; @@ -250,6 +273,9 @@ static int vga_switchto(struct vga_switc if (ret) return ret; + if (new_client->reprobe) + new_client->reprobe(new_client->pdev); + if (active->pwr_state == VGA_SWITCHEROO_ON) vga_switchoff(active); @@ -265,6 +291,7 @@ vga_switcheroo_debugfs_write(struct file const char *pdev_name; int i, ret; bool delay = false, can_switch; + bool just_mux = false; int client_id = -1; struct vga_switcheroo_client *client = NULL; @@ -319,6 +346,15 @@ vga_switcheroo_debugfs_write(struct file if (strncmp(usercmd, "DIS", 3) == 0) client_id = VGA_SWITCHEROO_DIS; + if (strncmp(usercmd, "MIGD", 3) == 0) { + just_mux = true; + client_id = VGA_SWITCHEROO_IGD; + } + if (strncmp(usercmd, "MDIS", 3) == 0) { + just_mux = true; + client_id = VGA_SWITCHEROO_DIS; + } + if (client_id == -1) goto out; @@ -330,6 +366,12 @@ vga_switcheroo_debugfs_write(struct file } vgasr_priv.delayed_switch_active = false; + + if (just_mux) { + ret = vgasr_priv.handler->switchto(client_id); + goto out; + } + /* okay we want a switch - test if devices are willing to switch */ can_switch = true; for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { @@ -345,18 +387,21 @@ vga_switcheroo_debugfs_write(struct file if (can_switch == true) { pdev_name = pci_name(client->pdev); - ret = vga_switchto(client); + ret = vga_switchto_stage1(client); + if (ret) + printk(KERN_ERR "vga_switcheroo: switching failed stage 1 %d\n", ret); + + ret = vga_switchto_stage2(client); if (ret) - printk(KERN_ERR "vga_switcheroo: switching failed %d\n", ret); ++ printk(KERN_ERR "vga_switcheroo: switching failed stage 2 %d\n", ret); } else { printk(KERN_INFO "vga_switcheroo: setting delayed switch to client %d\n", client->id); vgasr_priv.delayed_switch_active = true; vgasr_priv.delayed_client_id = client_id; - /* we should at least power up the card to - make the switch faster */ - if (client->pwr_state == VGA_SWITCHEROO_OFF) - vga_switchon(client); + ret = vga_switchto_stage1(client); + if (ret) + printk(KERN_ERR "vga_switcheroo: delayed switching stage 1 failed %d\n", ret); } out: @@ -438,9 +483,9 @@ int vga_switcheroo_process_delayed_switc goto err; pdev_name = pci_name(client->pdev); - ret = vga_switchto(client); + ret = vga_switchto_stage2(client); if (ret) - printk(KERN_ERR "vga_switcheroo: delayed switching failed %d\n", ret); + printk(KERN_ERR "vga_switcheroo: delayed switching failed stage 2 %d\n", ret); vgasr_priv.delayed_switch_active = false; err = 0; diff -urNp linux-2.6.37//drivers/pci/pci.c linux-2.6.37-OPTIMUS//drivers/pci/pci.c --- linux-2.6.37//drivers/pci/pci.c 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/pci/pci.c 2011-01-15 15:36:29.000000000 -0200 @@ -2888,31 +2888,34 @@ static int pci_set_vga_state_arch(struct * @dev: the PCI device * @decode: true = enable decoding, false = disable decoding * @command_bits: PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY - * @change_bridge: traverse ancestors and change bridges + * @change_bridge_flags: traverse ancestors and change bridges + * CHANGE_BRIDGE_ONLY / CHANGE_BRIDGE */ int pci_set_vga_state(struct pci_dev *dev, bool decode, - unsigned int command_bits, bool change_bridge) + unsigned int command_bits, u32 flags) { struct pci_bus *bus; struct pci_dev *bridge; u16 cmd; int rc; - WARN_ON(command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)); + WARN_ON((flags & PCI_VGA_STATE_CHANGE_DECODES) & (command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY))); /* ARCH specific VGA enables */ - rc = pci_set_vga_state_arch(dev, decode, command_bits, change_bridge); + rc = pci_set_vga_state_arch(dev, decode, command_bits, flags); if (rc) return rc; - pci_read_config_word(dev, PCI_COMMAND, &cmd); - if (decode == true) - cmd |= command_bits; - else - cmd &= ~command_bits; - pci_write_config_word(dev, PCI_COMMAND, cmd); + if (flags & PCI_VGA_STATE_CHANGE_DECODES) { + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (decode == true) + cmd |= command_bits; + else + cmd &= ~command_bits; + pci_write_config_word(dev, PCI_COMMAND, cmd); + } - if (change_bridge == false) + if (!(flags & PCI_VGA_STATE_CHANGE_BRIDGE)) return 0; bus = dev->bus; diff -urNp linux-2.6.37//drivers/platform/x86/Kconfig linux-2.6.37-OPTIMUS//drivers/platform/x86/Kconfig --- linux-2.6.37//drivers/platform/x86/Kconfig 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/platform/x86/Kconfig 2011-01-15 14:54:56.000000000 -0200 @@ -639,4 +639,11 @@ config XO1_RFKILL Support for enabling/disabling the WLAN interface on the OLPC XO-1 laptop. +config MXM_WMI + tristate "WMI support for MXM Laptop Graphics" + depends on WMI + ---help--- + MXM is a standard for laptop graphics cards, the WMI interface + is required for switchable nvidia graphics machines + endif # X86_PLATFORM_DEVICES diff -urNp linux-2.6.37//drivers/platform/x86/Makefile linux-2.6.37-OPTIMUS//drivers/platform/x86/Makefile --- linux-2.6.37//drivers/platform/x86/Makefile 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//drivers/platform/x86/Makefile 2011-01-15 14:55:33.000000000 -0200 @@ -33,3 +33,4 @@ obj-$(CONFIG_INTEL_IPS) += intel_ips.o obj-$(CONFIG_GPIO_INTEL_PMIC) += intel_pmic_gpio.o obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o obj-$(CONFIG_IBM_RTL) += ibm_rtl.o +obj-$(CONFIG_MXM_WMI) += mxm-wmi.o diff -urNp linux-2.6.37//drivers/platform/x86/mxm-wmi.c linux-2.6.37-OPTIMUS//drivers/platform/x86/mxm-wmi.c --- linux-2.6.37//drivers/platform/x86/mxm-wmi.c 1969-12-31 21:00:00.000000000 -0300 +++ linux-2.6.37-OPTIMUS//drivers/platform/x86/mxm-wmi.c 2011-01-15 15:45:34.000000000 -0200 @@ -0,0 +1,85 @@ +/* + * MXM WMI driver + * + * Copyright(C) 2010 Red Hat. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Dave Airlie"); +MODULE_DESCRIPTION("MXM WMI Driver"); +MODULE_LICENSE("GPL"); + +#define MXM_WMMX_GUID "F6CB5C3C-9CAE-4EBD-B577-931EA32A2CC0" + +MODULE_ALIAS("wmi:"MXM_WMMX_GUID); + +#define MXM_WMMX_FUNC_MXDS 0x5344584D /* "MXDS" */ + +struct mxds_args { + u32 func; + u32 args; + u32 xarg; +}; + +int mxm_wmi_call_mxds(int adapter, uint32_t output_id) +{ + struct mxds_args args = { + .func = MXM_WMMX_FUNC_MXDS, + .args = output_id, + .xarg = 1, + }; + struct acpi_buffer input = { (acpi_size)sizeof(args), &args }; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + acpi_status status; + + printk("calling mux switch %d\n", adapter); + + status = wmi_evaluate_method(MXM_WMMX_GUID, 0x1, adapter, &input, + &output); + + if (ACPI_FAILURE(status)) + return status; + + printk("mux switched %d\n", status); + return 0; + +} +EXPORT_SYMBOL_GPL(mxm_wmi_call_mxds); + +bool mxm_wmi_supported(void) +{ + bool guid_valid; + guid_valid = wmi_has_guid(MXM_WMMX_GUID); + return guid_valid; +} +EXPORT_SYMBOL_GPL(mxm_wmi_supported); + +static int __init mxm_wmi_init(void) +{ + return 0; +} + +static void __exit mxm_wmi_exit(void) +{ +} + +module_init(mxm_wmi_init); +module_exit(mxm_wmi_exit); Os arquivos binários linux-2.6.37//drivers/staging/ft1000/ft1000-pcmcia/ft1000.img e linux-2.6.37-OPTIMUS//drivers/staging/ft1000/ft1000-pcmcia/ft1000.img são diferentes Os arquivos binários linux-2.6.37//drivers/staging/ft1000/ft1000-usb/ft3000.img e linux-2.6.37-OPTIMUS//drivers/staging/ft1000/ft1000-usb/ft3000.img são diferentes diff -urNp linux-2.6.37//include/drm/drmP.h linux-2.6.37-OPTIMUS//include/drm/drmP.h --- linux-2.6.37//include/drm/drmP.h 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//include/drm/drmP.h 2011-01-15 15:25:49.000000000 -0200 @@ -1044,9 +1044,13 @@ struct drm_device { uint32_t invalidate_domains; /* domains pending invalidation */ uint32_t flush_domains; /* domains pending flush */ /*@} */ - + int switch_power_state; }; +#define DRM_SWITCH_POWER_ON 0 +#define DRM_SWITCH_POWER_OFF 1 +#define DRM_SWITCH_POWER_CHANGING 2 + static __inline__ int drm_core_check_feature(struct drm_device *dev, int feature) { diff -urNp linux-2.6.37//include/linux/mxm-wmi.h linux-2.6.37-OPTIMUS//include/linux/mxm-wmi.h --- linux-2.6.37//include/linux/mxm-wmi.h 1969-12-31 21:00:00.000000000 -0300 +++ linux-2.6.37-OPTIMUS//include/linux/mxm-wmi.h 2011-01-15 15:46:03.000000000 -0200 @@ -0,0 +1,32 @@ +/* + * MXM WMI driver + * + * Copyright(C) 2010 Red Hat. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#ifndef MXM_WMI_H +#define MXM_WMI_H + +/* discrete adapters */ +#define MXM_MXDS_ADAPTER_0 0x0 +#define MXM_MXDS_ADAPTER_1 0x0 +/* integrated adapter */ +#define MXM_MXDS_ADAPTER_IGD 0x10 +int mxm_wmi_call_mxds(int adapter, u32 output_id); +bool mxm_wmi_supported(void); + +#endif diff -urNp linux-2.6.37//include/linux/pci.h linux-2.6.37-OPTIMUS//include/linux/pci.h --- linux-2.6.37//include/linux/pci.h 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//include/linux/pci.h 2011-01-15 15:37:50.000000000 -0200 @@ -916,8 +916,11 @@ int pci_cfg_space_size_ext(struct pci_de int pci_cfg_space_size(struct pci_dev *dev); unsigned char pci_bus_max_busnr(struct pci_bus *bus); +#define PCI_VGA_STATE_CHANGE_BRIDGE (1 << 0) +#define PCI_VGA_STATE_CHANGE_DECODES (1 << 1) + int pci_set_vga_state(struct pci_dev *pdev, bool decode, - unsigned int command_bits, bool change_bridge); + unsigned int command_bits, u32 flags); /* kmem_cache style wrapper around pci_alloc_consistent() */ #include @@ -1047,7 +1050,7 @@ static inline int pci_proc_domain(struct /* some architectures require additional setup to direct VGA traffic */ typedef int (*arch_set_vga_state_t)(struct pci_dev *pdev, bool decode, - unsigned int command_bits, bool change_bridge); + unsigned int command_bits, u32 flags); extern void pci_register_set_vga_state(arch_set_vga_state_t func); #else /* CONFIG_PCI is not enabled */ diff -urNp linux-2.6.37//include/linux/vga_switcheroo.h linux-2.6.37-OPTIMUS//include/linux/vga_switcheroo.h --- linux-2.6.37//include/linux/vga_switcheroo.h 2011-01-04 22:50:19.000000000 -0200 +++ linux-2.6.37-OPTIMUS//include/linux/vga_switcheroo.h 2011-01-15 15:11:26.000000000 -0200 @@ -33,6 +33,7 @@ struct vga_switcheroo_handler { void vga_switcheroo_unregister_client(struct pci_dev *dev); int vga_switcheroo_register_client(struct pci_dev *dev, void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state), + void (*reprobe)(struct pci_dev *dev), bool (*can_switch)(struct pci_dev *dev)); void vga_switcheroo_client_fb_set(struct pci_dev *dev,