From patchwork Thu Feb 25 04:44:27 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dave Airlie X-Patchwork-Id: 81893 Received: from lists.sourceforge.net (lists.sourceforge.net [216.34.181.88]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o1P4jW7q011595 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO) for ; Thu, 25 Feb 2010 04:46:10 GMT Received: from localhost ([127.0.0.1] helo=sfs-ml-4.v29.ch3.sourceforge.com) by sfs-ml-4.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1NkVaU-0003Xh-MG; Thu, 25 Feb 2010 04:44:34 +0000 Received: from sfi-mx-3.v28.ch3.sourceforge.com ([172.29.28.123] helo=mx.sourceforge.net) by sfs-ml-4.v29.ch3.sourceforge.com with esmtp (Exim 4.69) (envelope-from ) id 1NkVaT-0003Xb-4E for dri-devel@lists.sourceforge.net; Thu, 25 Feb 2010 04:44:33 +0000 Received-SPF: neutral (sfi-mx-3.v28.ch3.sourceforge.com: 209.132.183.28 is neither permitted nor denied by domain of gmail.com) client-ip=209.132.183.28; envelope-from=airlied@gmail.com; helo=mx1.redhat.com; Received: from mx1.redhat.com ([209.132.183.28]) by sfi-mx-3.v28.ch3.sourceforge.com with esmtp (Exim 4.69) id 1NkVaR-000514-KZ for dri-devel@lists.sourceforge.net; Thu, 25 Feb 2010 04:44:33 +0000 Received: from int-mx04.intmail.prod.int.phx2.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.17]) by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o1P4iEMa013363 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Wed, 24 Feb 2010 23:44:14 -0500 Received: from localhost.localdomain (dhcp-0-222.bne.redhat.com [10.64.0.222]) by int-mx04.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o1P4i84o016051; Wed, 24 Feb 2010 23:44:13 -0500 From: Dave Airlie To: linux-fbdev@vger.kernel.org Subject: [PATCH 2/2] vga switch: hi my name is race condition Date: Thu, 25 Feb 2010 14:44:27 +1000 Message-Id: <1267073067-23117-3-git-send-email-airlied@gmail.com> In-Reply-To: <1267073067-23117-2-git-send-email-airlied@gmail.com> References: <1267073067-23117-1-git-send-email-airlied@gmail.com> <1267073067-23117-2-git-send-email-airlied@gmail.com> X-Scanned-By: MIMEDefang 2.67 on 10.5.11.17 X-Spam-Score: -6.2 (------) X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. -8.0 RCVD_IN_DNSWL_HI RBL: Sender listed at http://www.dnswl.org/, high trust [209.132.183.28 listed in list.dnswl.org] -0.0 SPF_HELO_PASS SPF: HELO matches SPF record 1.2 SPF_NEUTRAL SPF: sender does not match SPF record (neutral) 0.5 AWL AWL: From: address is in the auto white-list X-Headers-End: 1NkVaR-000514-KZ Cc: Dave Airlie , linux-kernel@vger.kernel.org, dri-devel@lists.sf.net X-BeenThere: dri-devel@lists.sourceforge.net X-Mailman-Version: 2.1.9 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: dri-devel-bounces@lists.sourceforge.net X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Thu, 25 Feb 2010 04:46:10 +0000 (UTC) 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 * * Licensed under GPLv2 + * + * ATPX support for both Intel/ATI */ #include 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 + 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);