From: Dave Airlie <[email protected]>

This adds a delayed switch mode, and also changes the debugfs file
to accept different parameters.
It also adds switch blocking by the drm if any devices are in use

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

no more PCI IDs.
---
 drivers/gpu/drm/i915/i915_dma.c              |   16 ++++-
 drivers/gpu/drm/nouveau/nouveau_state.c      |   14 +++-
 drivers/gpu/drm/radeon/r600_audio.c          |    3 +
 drivers/gpu/drm/radeon/radeon.h              |    2 +
 drivers/gpu/drm/radeon/radeon_atpx_handler.c |    2 +
 drivers/gpu/drm/radeon/radeon_device.c       |   26 ++++++-
 drivers/gpu/drm/radeon/radeon_kms.c          |    3 +
 drivers/gpu/vga/vga_switcheroo.c             |  108 ++++++++++++++++++++++---
 include/linux/vga_switcheroo.h               |    5 +-
 9 files changed, 162 insertions(+), 17 deletions(-)

diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 2e70292..3fb32fd 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1202,6 +1202,17 @@ static void i915_switcheroo_set_state(struct pci_dev 
*pdev, enum vga_switcheroo_
        }
 }
 
+static bool i915_switcheroo_can_switch(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       bool can_switch;
+
+       spin_lock(&dev->count_lock);
+       can_switch = (dev->open_count == 0);
+       spin_unlock(&dev->count_lock);
+       return can_switch;
+}
+
 static int i915_load_modeset_init(struct drm_device *dev,
                                  unsigned long prealloc_start,
                                  unsigned long prealloc_size,
@@ -1271,7 +1282,9 @@ static int i915_load_modeset_init(struct drm_device *dev,
        if (ret)
                goto destroy_ringbuffer;
 
-       ret = vga_switcheroo_register_client(dev->pdev, 
i915_switcheroo_set_state);
+       ret = vga_switcheroo_register_client(dev->pdev,
+                                            i915_switcheroo_set_state,
+                                            i915_switcheroo_can_switch);
        if (ret)
                goto destroy_ringbuffer;
 
@@ -1624,6 +1637,7 @@ void i915_driver_lastclose(struct drm_device * dev)
 
        if (!dev_priv || drm_core_check_feature(dev, DRIVER_MODESET)) {
                drm_fb_helper_restore();
+               vga_switcheroo_process_delayed_switch();
                return;
        }
 
diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c 
b/drivers/gpu/drm/nouveau/nouveau_state.c
index 940fdd3..afddcca 100644
--- a/drivers/gpu/drm/nouveau/nouveau_state.c
+++ b/drivers/gpu/drm/nouveau/nouveau_state.c
@@ -377,6 +377,17 @@ static void nouveau_switcheroo_set_state (struct pci_dev 
*pdev,
        }
 }
 
+static bool nouveau_switcheroo_can_switch(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       bool can_switch;
+
+       spin_lock(&dev->count_lock);
+       can_switch = (dev->open_count == 0);
+       spin_unlock(&dev->count_lock);
+       return can_switch;
+}
+
 int
 nouveau_card_init(struct drm_device *dev)
 {
@@ -390,7 +401,8 @@ nouveau_card_init(struct drm_device *dev)
                return 0;
 
        vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
-       vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state);
+       vga_switcheroo_register_client(dev->pdev, nouveau_switcheroo_set_state,
+                                      nouveau_switcheroo_can_switch);
 
        /* Initialise internal driver API hooks */
        ret = nouveau_init_engine_ptrs(dev);
diff --git a/drivers/gpu/drm/radeon/r600_audio.c 
b/drivers/gpu/drm/radeon/r600_audio.c
index 99e2c38..e880cd8 100644
--- a/drivers/gpu/drm/radeon/r600_audio.c
+++ b/drivers/gpu/drm/radeon/r600_audio.c
@@ -163,6 +163,9 @@ int r600_audio_init(struct radeon_device *rdev)
        rdev->audio_status_bits = 0;
        rdev->audio_category_code = 0;
 
+       if (!radeon_audio)
+               return 0;
+
        setup_timer(
                &rdev->audio_timer,
                r600_audio_update_hdmi,
diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
index f7df1a7..694c453 100644
--- a/drivers/gpu/drm/radeon/radeon.h
+++ b/drivers/gpu/drm/radeon/radeon.h
@@ -829,6 +829,8 @@ struct radeon_device {
        int                     audio_bits_per_sample;
        uint8_t                 audio_status_bits;
        uint8_t                 audio_category_code;
+
+       bool powered_down;
 };
 
 int radeon_device_init(struct radeon_device *rdev,
diff --git a/drivers/gpu/drm/radeon/radeon_atpx_handler.c 
b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
index 808d980..c614794 100644
--- a/drivers/gpu/drm/radeon/radeon_atpx_handler.c
+++ b/drivers/gpu/drm/radeon/radeon_atpx_handler.c
@@ -3,6 +3,8 @@
  * Author : Dave Airlie <[email protected]>
  * 
  * Licensed under GPLv2
+ *
+ * ATPX support for both Intel/ATI
  */
 
 #include <linux/vga_switcheroo.h>
diff --git a/drivers/gpu/drm/radeon/radeon_device.c 
b/drivers/gpu/drm/radeon/radeon_device.c
index 3ad4aba..a9b87b4 100644
--- a/drivers/gpu/drm/radeon/radeon_device.c
+++ b/drivers/gpu/drm/radeon/radeon_device.c
@@ -620,16 +620,33 @@ void radeon_check_arguments(struct radeon_device *rdev)
 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_ERR "VGA switched radeon on\n");
                radeon_resume_kms(dev);
+               r600_audio_init(rdev);
        } else {
                printk(KERN_ERR "VGA switched radeon off\n");
+               r600_audio_fini(rdev);
                radeon_suspend_kms(dev, pmm);
        }
+       /* don't suspend or resume card normally */
+       rdev->powered_down = true;
+}
+
+static bool radeon_switcheroo_can_switch(struct pci_dev *pdev)
+{
+       struct drm_device *dev = pci_get_drvdata(pdev);
+       bool can_switch;
+
+       spin_lock(&dev->count_lock);
+       can_switch = (dev->open_count == 0);
+       spin_unlock(&dev->count_lock);
+       return can_switch;
 }
 
+
 int radeon_device_init(struct radeon_device *rdev,
                       struct drm_device *ddev,
                       struct pci_dev *pdev,
@@ -709,7 +726,9 @@ 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_set_state);
+       vga_switcheroo_register_client(rdev->pdev,
+                                      radeon_switcheroo_set_state,
+                                      radeon_switcheroo_can_switch);
 
        r = radeon_init(rdev);
        if (r)
@@ -764,6 +783,8 @@ int radeon_suspend_kms(struct drm_device *dev, pm_message_t 
state)
        }
        rdev = dev->dev_private;
 
+       if (rdev->powered_down)
+               return 0;
        /* unpin the front buffers */
        list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
                struct radeon_framebuffer *rfb = 
to_radeon_framebuffer(crtc->fb);
@@ -809,6 +830,9 @@ int radeon_resume_kms(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
 
+       if (rdev->powered_down)
+               return 0;
+
        acquire_console_sem();
        pci_set_power_state(dev->pdev, PCI_D0);
        pci_restore_state(dev->pdev);
diff --git a/drivers/gpu/drm/radeon/radeon_kms.c 
b/drivers/gpu/drm/radeon/radeon_kms.c
index f23b056..5db7af6 100644
--- a/drivers/gpu/drm/radeon/radeon_kms.c
+++ b/drivers/gpu/drm/radeon/radeon_kms.c
@@ -30,6 +30,8 @@
 #include "radeon.h"
 #include "radeon_drm.h"
 
+#include <linux/vga_switcheroo.h>
+
 int radeon_driver_unload_kms(struct drm_device *dev)
 {
        struct radeon_device *rdev = dev->dev_private;
@@ -136,6 +138,7 @@ int radeon_driver_firstopen_kms(struct drm_device *dev)
 
 void radeon_driver_lastclose_kms(struct drm_device *dev)
 {
+       vga_switcheroo_process_delayed_switch();
 }
 
 int radeon_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv)
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index c4779ab..6b4781a 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -36,21 +36,21 @@ struct vga_switcheroo_client {
        struct pci_dev *pdev;
        struct fb_info *fb_info;
        int pwr_state;
-       /* TODO callbacks */
        void (*set_gpu_state)(struct pci_dev *pdev, enum vga_switcheroo_state);
-       /* TODO callbacks */
-       void (*switch_check)(struct pci_dev *pdev);
+       bool (*can_switch)(struct pci_dev *pdev);
 };
        
 struct vgasr_priv {
 
        bool active;
+       bool delayed_switch_active;
+       enum vga_switcheroo_client_id delayed_client_id;
 
        struct dentry *debugfs_root;
        struct dentry *switch_file;
 
        enum vga_switcheroo_method method;
-       int active_client;
+       enum vga_switcheroo_client_id active_client;
 
        int registered_clients;
        struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS];
@@ -164,7 +164,8 @@ void vga_switcheroo_unregister_handler(void)
 EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
 
 int vga_switcheroo_register_client(struct pci_dev *pdev,
-                                  void (*set_gpu_state)(struct pci_dev *pdev, 
enum vga_switcheroo_state))
+                                  void (*set_gpu_state)(struct pci_dev *pdev, 
enum vga_switcheroo_state),
+                                  bool (*can_switch)(struct pci_dev *pdev))
 {
        enum vga_switcheroo_client_id client_id;
 
@@ -178,6 +179,7 @@ int vga_switcheroo_register_client(struct pci_dev *pdev,
        vgasr_priv.clients[client_id].pwr_state = VGA_SWITCHEROO_ON;
        vgasr_priv.clients[client_id].pdev = pdev;
        vgasr_priv.clients[client_id].set_gpu_state = set_gpu_state;
+       vgasr_priv.clients[client_id].can_switch = can_switch;
        if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)
                vgasr_priv.active_client = client_id;
 
@@ -305,8 +307,10 @@ vga_switcheroo_debugfs_write(struct file *filp, const char 
__user *ubuf,
                             size_t cnt, loff_t *ppos)
 {
        char pci_id[64];
-       char *pdev_name;
+       const char *pdev_name;
        int i, ret;
+       enum vga_switcheroo_client_id client_id = -1;
+       bool delay = false, can_switch;
 
        if (cnt > 63)
                cnt = 63;
@@ -314,6 +318,7 @@ vga_switcheroo_debugfs_write(struct file *filp, const char 
__user *ubuf,
        if (copy_from_user(pci_id, ubuf, cnt))
                return -EFAULT;
 
+
        /* pwr off the device not in use */
        if (strncmp(pci_id, "OFF", 3) == 0) {
                for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
@@ -336,17 +341,60 @@ vga_switcheroo_debugfs_write(struct file *filp, const 
char __user *ubuf,
                }
                goto out;
        }
-       /* switch devices */
+
+       /* request a delayed switch - test can we switch now */
+       if (strncmp(pci_id, "DIGD", 4) == 0) {
+               client_id = VGA_SWITCHEROO_IGD;
+               delay = true;
+       }
+
+       if (strncmp(pci_id, "DDIS", 4) == 0) {
+               client_id = VGA_SWITCHEROO_DIS;
+               delay = true;
+       }
+
+       if (strncmp(pci_id, "IGD", 3) == 0) {
+               client_id = VGA_SWITCHEROO_IGD;
+       }
+
+       if (strncmp(pci_id, "DIS", 3) == 0) {
+               client_id = VGA_SWITCHEROO_DIS;
+       }
+
+       if (client_id == -1)
+               goto out;
+
+       vgasr_priv.delayed_switch_active = false;
+       /* okay we want a switch - test if devices are willing to switch */
+       can_switch = true;
        for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
-               pdev_name = pci_name(vgasr_priv.clients[i].pdev);
-               if (strncmp(pci_id, pdev_name, strlen(pdev_name)) == 0) {
-                       printk("switching to %d %s\n", i, pdev_name);
-                       ret = vga_switchto(i);
-                       if (ret)
-                               printk("switching failed %d\n", ret);
+               can_switch = 
vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);
+               if (can_switch == false) {
+                       printk(KERN_ERR "Client %d refused switch\n", i);
                        break;
                }
        }
+
+       if (can_switch == false && delay == false)
+               goto out;
+
+       if (can_switch == true) {
+               pdev_name = pci_name(vgasr_priv.clients[client_id].pdev);
+               printk("switching to %s\n", pdev_name);
+               ret = vga_switchto(client_id);
+               if (ret)
+                       printk("switching failed %d\n", ret);
+       } else {
+               printk(KERN_ERR "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 (vgasr_priv.clients[client_id].pwr_state == 
VGA_SWITCHEROO_OFF)
+                       vga_switchon(client_id);
+       }
+
 out:
        return cnt;
 }
@@ -395,3 +443,37 @@ fail:
        vga_switcheroo_debugfs_fini(priv);
        return -1;
 }
+
+int vga_switcheroo_process_delayed_switch(void)
+{
+       const char *pdev_name;
+       bool can_switch = true;
+       int i;
+       int ret;
+
+       if (!vgasr_priv.delayed_switch_active)
+               return -EINVAL;
+
+       printk(KERN_ERR "processing delayed switch to %d\n", 
vgasr_priv.delayed_client_id);
+
+       for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) {
+               can_switch = 
vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev);
+               if (can_switch == false) {
+                       printk(KERN_ERR "Client %d refused switch\n", i);
+                       break;
+               }
+       }
+
+       if (can_switch == false)
+               return -EINVAL;
+
+       pdev_name = 
pci_name(vgasr_priv.clients[vgasr_priv.delayed_client_id].pdev);
+       printk("delayed switching to %s\n", pdev_name);
+       ret = vga_switchto(vgasr_priv.delayed_client_id);
+       if (ret)
+               printk("switching failed %d\n", ret);
+
+       vgasr_priv.delayed_switch_active = false;
+       return 0;
+}
+EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index 694cfc5..fc30f82 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -29,7 +29,8 @@ enum vga_switcheroo_client_id {
 
 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 (*set_gpu_state)(struct pci_dev *dev, 
enum vga_switcheroo_state),
+                                  bool (*can_switch)(struct pci_dev *dev));
 
 void vga_switcheroo_client_fb_set(struct pci_dev *dev,
                                  struct fb_info *info);
@@ -45,3 +46,5 @@ struct vga_switcheroo_handler {
 
 int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler);
 void vga_switcheroo_unregister_handler(void);
+
+int vga_switcheroo_process_delayed_switch(void);
-- 
1.6.5.2


------------------------------------------------------------------------------
Download Intel&#174; Parallel Studio Eval
Try the new software tools for yourself. Speed compiling, find bugs
proactively, and fine-tune applications for parallel performance.
See why Intel Parallel Studio got high marks during beta.
http://p.sf.net/sfu/intel-sw-dev
--
_______________________________________________
Dri-devel mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/dri-devel

Reply via email to