From: Dave Airlie <airl...@dhcp-40-90.bne.redhat.com>

For optimus and powerxpress muxless we really want the GPU
driver deciding when to power up/down the GPU, not userspace.

This adds the ability for a driver to dynamically power up/down
the GPU and remove the switcheroo from controlling it, the
switcheroo reports the dynamic state to userspace also.

Signed-off-by: Dave Airlie <airlied at redhat.com>
---
 drivers/gpu/drm/i915/i915_dma.c        |  2 +-
 drivers/gpu/drm/nouveau/nouveau_vga.c  |  2 +-
 drivers/gpu/drm/radeon/radeon_device.c |  2 +-
 drivers/gpu/vga/vga_switcheroo.c       | 44 ++++++++++++++++++++++++++++++----
 include/linux/vga_switcheroo.h         |  6 ++++-
 5 files changed, 47 insertions(+), 9 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 9cf7dfe..a377204 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1302,7 +1302,7 @@ static int i915_load_modeset_init(struct drm_device *dev)

        intel_register_dsm_handler();

-       ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops);
+       ret = vga_switcheroo_register_client(dev->pdev, &i915_switcheroo_ops, 
false);
        if (ret)
                goto cleanup_vga_client;

diff --git a/drivers/gpu/drm/nouveau/nouveau_vga.c 
b/drivers/gpu/drm/nouveau/nouveau_vga.c
index 7bf7d13..37fcc9d 100644
--- a/drivers/gpu/drm/nouveau/nouveau_vga.c
+++ b/drivers/gpu/drm/nouveau/nouveau_vga.c
@@ -80,7 +80,7 @@ nouveau_vga_init(struct nouveau_drm *drm)
 {
        struct drm_device *dev = drm->dev;
        vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
-       vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops);
+       vga_switcheroo_register_client(dev->pdev, &nouveau_switcheroo_ops, 
false);
 }

 void
diff --git a/drivers/gpu/drm/radeon/radeon_device.c 
b/drivers/gpu/drm/radeon/radeon_device.c
index 7a3daeb..857cf5b 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -1093,7 +1093,7 @@ int radeon_device_init(struct radeon_device *rdev,
        /* this will fail for cards that aren't VGA class devices, just
         * ignore it */
        vga_client_register(rdev->pdev, rdev, NULL, radeon_vga_set_decode);
-       vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops);
+       vga_switcheroo_register_client(rdev->pdev, &radeon_switcheroo_ops, 
false);

        r = radeon_init(rdev);
        if (r)
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index e25cf31..efd147a 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -37,6 +37,7 @@ struct vga_switcheroo_client {
        const struct vga_switcheroo_client_ops *ops;
        int id;
        bool active;
+       bool driver_power_control;
        struct list_head list;
 };

@@ -132,7 +133,7 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_handler);

 static int register_client(struct pci_dev *pdev,
                           const struct vga_switcheroo_client_ops *ops,
-                          int id, bool active)
+                          int id, bool active, bool driver_power_control)
 {
        struct vga_switcheroo_client *client;

@@ -145,6 +146,7 @@ static int register_client(struct pci_dev *pdev,
        client->ops = ops;
        client->id = id;
        client->active = active;
+       client->driver_power_control = driver_power_control;

        mutex_lock(&vgasr_mutex);
        list_add_tail(&client->list, &vgasr_priv.clients);
@@ -160,10 +162,11 @@ static int register_client(struct pci_dev *pdev,
 }

 int vga_switcheroo_register_client(struct pci_dev *pdev,
-                                  const struct vga_switcheroo_client_ops *ops)
+                                  const struct vga_switcheroo_client_ops *ops,
+                                  bool driver_power_control)
 {
        return register_client(pdev, ops, -1,
-                              pdev == vga_default_device());
+                              pdev == vga_default_device(), 
driver_power_control);
 }
 EXPORT_SYMBOL(vga_switcheroo_register_client);

@@ -171,7 +174,7 @@ int vga_switcheroo_register_audio_client(struct pci_dev 
*pdev,
                                         const struct vga_switcheroo_client_ops 
*ops,
                                         int id, bool active)
 {
-       return register_client(pdev, ops, id | ID_BIT_AUDIO, active);
+       return register_client(pdev, ops, id | ID_BIT_AUDIO, active, false);
 }
 EXPORT_SYMBOL(vga_switcheroo_register_audio_client);

@@ -258,10 +261,11 @@ static int vga_switcheroo_show(struct seq_file *m, void 
*v)
        int i = 0;
        mutex_lock(&vgasr_mutex);
        list_for_each_entry(client, &vgasr_priv.clients, list) {
-               seq_printf(m, "%d:%s%s:%c:%s:%s\n", i,
+               seq_printf(m, "%d:%s%s:%c:%s%s:%s\n", i,
                           client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : 
"IGD",
                           client_is_vga(client) ? "" : "-Audio",
                           client->active ? '+' : ' ',
+                          client->driver_power_control ? "Dyn" : "",
                           client->pwr_state ? "Pwr" : "Off",
                           pci_name(client->pdev));
                i++;
@@ -277,6 +281,8 @@ static int vga_switcheroo_debugfs_open(struct inode *inode, 
struct file *file)

 static int vga_switchon(struct vga_switcheroo_client *client)
 {
+       if (client->driver_power_control)
+               return 0;
        if (vgasr_priv.handler->power_state)
                vgasr_priv.handler->power_state(client->id, VGA_SWITCHEROO_ON);
        /* call the driver callback to turn on device */
@@ -287,6 +293,8 @@ static int vga_switchon(struct vga_switcheroo_client 
*client)

 static int vga_switchoff(struct vga_switcheroo_client *client)
 {
+       if (client->driver_power_control)
+               return 0;
        /* call the driver callback to turn off device */
        client->ops->set_gpu_state(client->pdev, VGA_SWITCHEROO_OFF);
        if (vgasr_priv.handler->power_state)
@@ -401,6 +409,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char 
__user *ubuf,
                list_for_each_entry(client, &vgasr_priv.clients, list) {
                        if (client->active || client_is_audio(client))
                                continue;
+                       if (client->driver_power_control)
+                               continue;
                        set_audio_state(client->id, VGA_SWITCHEROO_OFF);
                        if (client->pwr_state == VGA_SWITCHEROO_ON)
                                vga_switchoff(client);
@@ -412,6 +422,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char 
__user *ubuf,
                list_for_each_entry(client, &vgasr_priv.clients, list) {
                        if (client->active || client_is_audio(client))
                                continue;
+                       if (client->driver_power_control)
+                               continue;
                        if (client->pwr_state == VGA_SWITCHEROO_OFF)
                                vga_switchon(client);
                        set_audio_state(client->id, VGA_SWITCHEROO_ON);
@@ -568,3 +580,25 @@ err:
 }
 EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);

+/* force a PCI device to a certain state - mainly to turn off audio clients */
+void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum 
vga_switcheroo_state dynamic, bool main_power)
+{
+       struct vga_switcheroo_client *client;
+
+       client = find_client_from_pci(&vgasr_priv.clients, pdev);
+       if (!client)
+               return;
+
+       if (!client->driver_power_control)
+               return;
+
+       if (main_power) {
+               if (vgasr_priv.handler->power_state)
+                       vgasr_priv.handler->power_state(client->id, dynamic);
+               return;
+       }
+
+       set_audio_state(client->id, dynamic);
+       client->pwr_state = dynamic;
+}
+EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch);
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index ddb419c..f327093 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -45,7 +45,8 @@ struct vga_switcheroo_client_ops {
 #if defined(CONFIG_VGA_SWITCHEROO)
 void vga_switcheroo_unregister_client(struct pci_dev *dev);
 int vga_switcheroo_register_client(struct pci_dev *dev,
-                                  const struct vga_switcheroo_client_ops *ops);
+                                  const struct vga_switcheroo_client_ops *ops,
+                                  bool driver_power_control);
 int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
                                         const struct vga_switcheroo_client_ops 
*ops,
                                         int id, bool active);
@@ -60,6 +61,8 @@ int vga_switcheroo_process_delayed_switch(void);

 int vga_switcheroo_get_client_state(struct pci_dev *dev);

+void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum 
vga_switcheroo_state dynamic, bool main_power);
+
 #else

 static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
@@ -74,6 +77,7 @@ static inline void vga_switcheroo_unregister_handler(void) {}
 static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
 static inline int vga_switcheroo_get_client_state(struct pci_dev *dev) { 
return VGA_SWITCHEROO_ON; }

+static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, 
enum vga_switcheroo_state dynamic, bool main_power) {}

 #endif
 #endif /* _LINUX_VGA_SWITCHEROO_H_ */
-- 
1.7.12

Reply via email to