diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/nouveau/nouveau_drm.c | 2 | ||||
-rw-r--r-- | drivers/gpu/drm/radeon/radeon_drv.c | 2 | ||||
-rw-r--r-- | drivers/gpu/vga/vga_switcheroo.c | 115 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 39 |
5 files changed, 47 insertions, 113 deletions
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 50afcf65181a..ba4335fd4f65 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -720,7 +720,6 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev) drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; drm_kms_helper_poll_disable(drm_dev); - vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); ret = amdgpu_device_suspend(drm_dev, false, false); pci_save_state(pdev); @@ -757,7 +756,6 @@ static int amdgpu_pmops_runtime_resume(struct device *dev) ret = amdgpu_device_resume(drm_dev, false, false); drm_kms_helper_poll_enable(drm_dev); - vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; return 0; } diff --git a/drivers/gpu/drm/nouveau/nouveau_drm.c b/drivers/gpu/drm/nouveau/nouveau_drm.c index 3e293029e3a6..6959951d45d6 100644 --- a/drivers/gpu/drm/nouveau/nouveau_drm.c +++ b/drivers/gpu/drm/nouveau/nouveau_drm.c @@ -856,7 +856,6 @@ nouveau_pmops_runtime_suspend(struct device *dev) } drm_kms_helper_poll_disable(drm_dev); - vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); nouveau_switcheroo_optimus_dsm(); ret = nouveau_do_suspend(drm_dev, true); pci_save_state(pdev); @@ -891,7 +890,6 @@ nouveau_pmops_runtime_resume(struct device *dev) /* do magic */ nvif_mask(&device->object, 0x088488, (1 << 25), (1 << 25)); - vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; /* Monitors may have been connected / disconnected during suspend */ diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c index 31dd04f6baa1..b28288a781ef 100644 --- a/drivers/gpu/drm/radeon/radeon_drv.c +++ b/drivers/gpu/drm/radeon/radeon_drv.c @@ -415,7 +415,6 @@ static int radeon_pmops_runtime_suspend(struct device *dev) drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING; drm_kms_helper_poll_disable(drm_dev); - vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF); ret = radeon_suspend_kms(drm_dev, false, false, false); pci_save_state(pdev); @@ -452,7 +451,6 @@ static int radeon_pmops_runtime_resume(struct device *dev) ret = radeon_resume_kms(drm_dev, false, false); drm_kms_helper_poll_enable(drm_dev); - vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON); drm_dev->switch_power_state = DRM_SWITCH_POWER_ON; return 0; } diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 2488af797020..4ee0ed642386 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c @@ -105,8 +105,7 @@ * @list: client list * * Registered client. A client can be either a GPU or an audio device on a GPU. - * For audio clients, the @fb_info, @active and @driver_power_control members - * are bogus. + * For audio clients, the @fb_info and @active members are bogus. */ struct vga_switcheroo_client { struct pci_dev *pdev; @@ -332,8 +331,8 @@ EXPORT_SYMBOL(vga_switcheroo_register_client); * @ops: client callbacks * @id: client identifier * - * Register audio client (audio device on a GPU). The power state of the - * client is assumed to be ON. Beforehand, vga_switcheroo_client_probe_defer() + * Register audio client (audio device on a GPU). The client is assumed + * to use runtime PM. Beforehand, vga_switcheroo_client_probe_defer() * shall be called to ensure that all prerequisites are met. * * Return: 0 on success, -ENOMEM on memory allocation error. @@ -342,7 +341,7 @@ int vga_switcheroo_register_audio_client(struct pci_dev *pdev, const struct vga_switcheroo_client_ops *ops, enum vga_switcheroo_client_id id) { - return register_client(pdev, ops, id | ID_BIT_AUDIO, false, false); + return register_client(pdev, ops, id | ID_BIT_AUDIO, false, true); } EXPORT_SYMBOL(vga_switcheroo_register_audio_client); @@ -655,10 +654,8 @@ static void set_audio_state(enum vga_switcheroo_client_id id, struct vga_switcheroo_client *client; client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO); - if (client) { + if (client) client->ops->set_gpu_state(client->pdev, state); - client->pwr_state = state; - } } /* stage one happens before delay */ @@ -953,10 +950,6 @@ EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); * Specifying nouveau.runpm=0, radeon.runpm=0 or amdgpu.runpm=0 on the kernel * command line disables it. * - * When the driver decides to power up or down, it notifies vga_switcheroo - * thereof so that it can power the audio device on the GPU up or down. - * This is achieved by vga_switcheroo_set_dynamic_switch(). - * * After the GPU has been suspended, the handler needs to be called to cut * power to the GPU. Likewise it needs to reinstate power before the GPU * can resume. This is achieved by vga_switcheroo_init_domain_pm_ops(), @@ -964,8 +957,9 @@ EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch); * calls to the handler. * * When the audio device resumes, the GPU needs to be woken. This is achieved - * by vga_switcheroo_init_domain_pm_optimus_hdmi_audio(), which augments the - * audio device's resume function. + * by a PCI quirk which calls device_link_add() to declare a dependency on the + * GPU. That way, the GPU is kept awake whenever and as long as the audio + * device is in use. * * On muxed machines, if the mux is initially switched to the discrete GPU, * the user ends up with a black screen when the GPU powers down after boot. @@ -991,33 +985,6 @@ static void vga_switcheroo_power_switch(struct pci_dev *pdev, vgasr_priv.handler->power_state(client->id, state); } -/** - * vga_switcheroo_set_dynamic_switch() - helper for driver power control - * @pdev: client pci device - * @dynamic: new power state - * - * Helper for GPUs whose power state is controlled by the driver's runtime pm. - * When the driver decides to power up or down, it notifies vga_switcheroo - * thereof using this helper so that it can power the audio device on the GPU - * up or down. - */ -void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, - enum vga_switcheroo_state dynamic) -{ - struct vga_switcheroo_client *client; - - mutex_lock(&vgasr_mutex); - client = find_client_from_pci(&vgasr_priv.clients, pdev); - if (!client || !client->driver_power_control) { - mutex_unlock(&vgasr_mutex); - return; - } - - set_audio_state(client->id, dynamic); - mutex_unlock(&vgasr_mutex); -} -EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch); - /* switcheroo power domain */ static int vga_switcheroo_runtime_suspend(struct device *dev) { @@ -1089,69 +1056,3 @@ void vga_switcheroo_fini_domain_pm_ops(struct device *dev) dev_pm_domain_set(dev, NULL); } EXPORT_SYMBOL(vga_switcheroo_fini_domain_pm_ops); - -static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - struct vga_switcheroo_client *client; - struct device *video_dev = NULL; - int ret; - - /* we need to check if we have to switch back on the video - * device so the audio device can come back - */ - mutex_lock(&vgasr_mutex); - list_for_each_entry(client, &vgasr_priv.clients, list) { - if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) && - client_is_vga(client)) { - video_dev = &client->pdev->dev; - break; - } - } - mutex_unlock(&vgasr_mutex); - - if (video_dev) { - ret = pm_runtime_get_sync(video_dev); - if (ret && ret != 1) - return ret; - } - ret = dev->bus->pm->runtime_resume(dev); - - /* put the reference for the gpu */ - if (video_dev) { - pm_runtime_mark_last_busy(video_dev); - pm_runtime_put_autosuspend(video_dev); - } - return ret; -} - -/** - * vga_switcheroo_init_domain_pm_optimus_hdmi_audio() - helper for driver - * power control - * @dev: audio client device - * @domain: power domain - * - * Helper for GPUs whose power state is controlled by the driver's runtime pm. - * When the audio device resumes, the GPU needs to be woken. This helper - * augments the audio device's resume function to do that. - * - * Return: 0 on success, -EINVAL if no power management operations are - * defined for this device. - */ -int -vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, - struct dev_pm_domain *domain) -{ - /* copy over all the bus versions */ - if (dev->bus && dev->bus->pm) { - domain->ops = *dev->bus->pm; - domain->ops.runtime_resume = - vga_switcheroo_runtime_resume_hdmi_audio; - - dev_pm_domain_set(dev, domain); - return 0; - } - dev_pm_domain_set(dev, NULL); - return -EINVAL; -} -EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio); diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index fc734014206f..ec582d37c189 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -26,6 +26,7 @@ #include <linux/ktime.h> #include <linux/mm.h> #include <linux/platform_data/x86/apple.h> +#include <linux/pm_runtime.h> #include <asm/dma.h> /* isa_dma_bridge_buggy */ #include "pci.h" @@ -4832,3 +4833,41 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev) pdev->no_msi = 1; } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi); + +/* + * GPUs with integrated HDA controller for streaming audio to attached displays + * need a device link from the HDA controller (consumer) to the GPU (supplier) + * so that the GPU is powered up whenever the HDA controller is accessed. + * The GPU and HDA controller are functions 0 and 1 of the same PCI device. + * The device link stays in place until shutdown (or removal of the PCI device + * if it's hotplugged). Runtime PM is allowed by default on the HDA controller + * to prevent it from permanently keeping the GPU awake. + */ +static void quirk_gpu_hda(struct pci_dev *hda) +{ + struct pci_dev *gpu; + + if (PCI_FUNC(hda->devfn) != 1) + return; + + gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus), + hda->bus->number, + PCI_DEVFN(PCI_SLOT(hda->devfn), 0)); + if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) { + pci_dev_put(gpu); + return; + } + + if (!device_link_add(&hda->dev, &gpu->dev, + DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME)) + pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu)); + + pm_runtime_allow(&hda->dev); + pci_dev_put(gpu); +} +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); +DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, + PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda); |