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 <acpi/acpi_drivers.h>
#include <acpi/acpi_bus.h>
#include <acpi/video.h>
+#include <acpi/acpi.h>
+#include <linux/mxm-wmi.h>
#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 <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+
+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 <linux/pci-dma.h>
@@ -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,