Skip to content

Commit 07f4f97

Browse files
committed
vga_switcheroo: Use device link for HDA controller
Back in 2013, runtime PM for GPUs with integrated HDA controller was introduced with commits 0d69704 ("gpu/vga_switcheroo: add driver control power feature. (v3)") and 246efa4 ("snd/hda: add runtime suspend/resume on optimus support (v4)"). Briefly, the idea was that the HDA controller is forced on and off in unison with the GPU. The original code is mostly still in place even though it was never a 100% perfect solution: E.g. on access to the HDA controller, the GPU is powered up via vga_switcheroo_runtime_resume_hdmi_audio() but there are no provisions to keep it resumed until access to the HDA controller has ceased: The GPU autosuspends after 5 seconds, rendering the HDA controller inaccessible. Additionally, a kludge is required when hda_intel.c probes: It has to check whether the GPU is powered down (check_hdmi_disabled()) and defer probing if so. However in the meantime (in v4.10) the driver core has gained a feature called device links which promises to solve such issues in a clean way: It allows us to declare a dependency from the HDA controller (consumer) to the GPU (supplier). The PM core then automagically ensures that the GPU is runtime resumed as long as the HDA controller's ->probe hook is executed and whenever the HDA controller is accessed. By default, the HDA controller has a dependency on its parent, a PCIe Root Port. Adding a device link creates another dependency on its sibling: PCIe Root Port ^ ^ | | | | HDA ===> GPU The device link is not only used for runtime PM, it also guarantees that on system sleep, the HDA controller suspends before the GPU and resumes after the GPU, and on system shutdown the HDA controller's ->shutdown hook is executed before the one of the GPU. It is a complete solution. Using this functionality is as simple as calling device_link_add(), which results in a dmesg entry like this: pci 0000:01:00.1: Linked as a consumer to 0000:01:00.0 The code for the GPU-governed audio power management can thus be removed (except where it's still needed for legacy manual power control). The device link is added in a PCI quirk rather than in hda_intel.c. It is therefore legal for the GPU to runtime suspend to D3cold even if the HDA controller is not bound to a driver or if CONFIG_SND_HDA_INTEL is not enabled, for accesses to the HDA controller will cause the GPU to wake up regardless if they're occurring outside of hda_intel.c (think config space readout via sysfs). Contrary to the previous implementation, the HDA controller's power state is now self-governed, rather than GPU-governed, whereas the GPU's power state is no longer fully self-governed. (The HDA controller needs to runtime suspend before the GPU can.) It is thus crucial that runtime PM is always activated on the HDA controller even if CONFIG_SND_HDA_POWER_SAVE_DEFAULT is set to 0 (which is the default), lest the GPU stays awake. This is achieved by setting the auto_runtime_pm flag on every codec and the AZX_DCAPS_PM_RUNTIME flag on the HDA controller. A side effect is that power consumption might be reduced if the GPU is in use but the HDA controller is not, because the HDA controller is now allowed to go to D3hot. Before, it was forced to stay in D0 as long as the GPU was in use. (There is no reduction in power consumption on my Nvidia GK107, but there might be on other chips.) The code paths for legacy manual power control are adjusted such that runtime PM is disabled during power off, thereby preventing the PM core from resuming the HDA controller. Note that the device link is not only added on vga_switcheroo capable systems, but for *any* GPU with integrated HDA controller. The idea is that the HDA controller streams audio via connectors located on the GPU, so the GPU needs to be on for the HDA controller to do anything useful. This commit implicitly fixes an unbalanced runtime PM ref upon unbind of hda_intel.c: On ->probe, a runtime PM ref was previously released under the condition "azx_has_pm_runtime(chip) || hda->use_vga_switcheroo", but on ->remove a runtime PM ref was only acquired under the first of those conditions. Thus, binding and unbinding the driver twice on a vga_switcheroo capable system caused the runtime PM refcount to drop below zero. The issue is resolved because the AZX_DCAPS_PM_RUNTIME flag is now always set if use_vga_switcheroo is true. For more information on device links please refer to: https://www.kernel.org/doc/html/latest/driver-api/device_link.html Documentation/driver-api/device_link.rst Cc: Dave Airlie <[email protected]> Cc: Ben Skeggs <[email protected]> Cc: Alex Deucher <[email protected]> Cc: Rafael J. Wysocki <[email protected]> Acked-by: Bjorn Helgaas <[email protected]> Reviewed-by: Takashi Iwai <[email protected]> Reviewed-by: Peter Wu <[email protected]> Tested-by: Kai Heng Feng <[email protected]> # AMD PowerXpress Tested-by: Mike Lothian <[email protected]> # AMD PowerXpress Tested-by: Denis Lisov <[email protected]> # Nvidia Optimus Tested-by: Peter Wu <[email protected]> # Nvidia Optimus Tested-by: Lukas Wunner <[email protected]> # MacBook Pro Signed-off-by: Lukas Wunner <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/51bd38360ff502a8c42b1ebf4405ee1d3f27118d.1520068884.git.lukas@wunner.de
1 parent 8948ca1 commit 07f4f97

File tree

10 files changed

+73
-136
lines changed

10 files changed

+73
-136
lines changed

drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,6 @@ static int amdgpu_pmops_runtime_suspend(struct device *dev)
720720

721721
drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
722722
drm_kms_helper_poll_disable(drm_dev);
723-
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
724723

725724
ret = amdgpu_device_suspend(drm_dev, false, false);
726725
pci_save_state(pdev);
@@ -757,7 +756,6 @@ static int amdgpu_pmops_runtime_resume(struct device *dev)
757756

758757
ret = amdgpu_device_resume(drm_dev, false, false);
759758
drm_kms_helper_poll_enable(drm_dev);
760-
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
761759
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
762760
return 0;
763761
}

drivers/gpu/drm/nouveau/nouveau_drm.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,6 @@ nouveau_pmops_runtime_suspend(struct device *dev)
856856
}
857857

858858
drm_kms_helper_poll_disable(drm_dev);
859-
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
860859
nouveau_switcheroo_optimus_dsm();
861860
ret = nouveau_do_suspend(drm_dev, true);
862861
pci_save_state(pdev);
@@ -891,7 +890,6 @@ nouveau_pmops_runtime_resume(struct device *dev)
891890

892891
/* do magic */
893892
nvif_mask(&device->object, 0x088488, (1 << 25), (1 << 25));
894-
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
895893
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
896894

897895
/* Monitors may have been connected / disconnected during suspend */

drivers/gpu/drm/radeon/radeon_drv.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,6 @@ static int radeon_pmops_runtime_suspend(struct device *dev)
415415

416416
drm_dev->switch_power_state = DRM_SWITCH_POWER_CHANGING;
417417
drm_kms_helper_poll_disable(drm_dev);
418-
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_OFF);
419418

420419
ret = radeon_suspend_kms(drm_dev, false, false, false);
421420
pci_save_state(pdev);
@@ -452,7 +451,6 @@ static int radeon_pmops_runtime_resume(struct device *dev)
452451

453452
ret = radeon_resume_kms(drm_dev, false, false);
454453
drm_kms_helper_poll_enable(drm_dev);
455-
vga_switcheroo_set_dynamic_switch(pdev, VGA_SWITCHEROO_ON);
456454
drm_dev->switch_power_state = DRM_SWITCH_POWER_ON;
457455
return 0;
458456
}

drivers/gpu/vga/vga_switcheroo.c

Lines changed: 8 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,7 @@
105105
* @list: client list
106106
*
107107
* Registered client. A client can be either a GPU or an audio device on a GPU.
108-
* For audio clients, the @fb_info, @active and @driver_power_control members
109-
* are bogus.
108+
* For audio clients, the @fb_info and @active members are bogus.
110109
*/
111110
struct vga_switcheroo_client {
112111
struct pci_dev *pdev;
@@ -332,8 +331,8 @@ EXPORT_SYMBOL(vga_switcheroo_register_client);
332331
* @ops: client callbacks
333332
* @id: client identifier
334333
*
335-
* Register audio client (audio device on a GPU). The power state of the
336-
* client is assumed to be ON. Beforehand, vga_switcheroo_client_probe_defer()
334+
* Register audio client (audio device on a GPU). The client is assumed
335+
* to use runtime PM. Beforehand, vga_switcheroo_client_probe_defer()
337336
* shall be called to ensure that all prerequisites are met.
338337
*
339338
* Return: 0 on success, -ENOMEM on memory allocation error.
@@ -342,7 +341,7 @@ int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
342341
const struct vga_switcheroo_client_ops *ops,
343342
enum vga_switcheroo_client_id id)
344343
{
345-
return register_client(pdev, ops, id | ID_BIT_AUDIO, false, false);
344+
return register_client(pdev, ops, id | ID_BIT_AUDIO, false, true);
346345
}
347346
EXPORT_SYMBOL(vga_switcheroo_register_audio_client);
348347

@@ -655,10 +654,8 @@ static void set_audio_state(enum vga_switcheroo_client_id id,
655654
struct vga_switcheroo_client *client;
656655

657656
client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO);
658-
if (client) {
657+
if (client)
659658
client->ops->set_gpu_state(client->pdev, state);
660-
client->pwr_state = state;
661-
}
662659
}
663660

664661
/* stage one happens before delay */
@@ -953,19 +950,16 @@ EXPORT_SYMBOL(vga_switcheroo_process_delayed_switch);
953950
* Specifying nouveau.runpm=0, radeon.runpm=0 or amdgpu.runpm=0 on the kernel
954951
* command line disables it.
955952
*
956-
* When the driver decides to power up or down, it notifies vga_switcheroo
957-
* thereof so that it can power the audio device on the GPU up or down.
958-
* This is achieved by vga_switcheroo_set_dynamic_switch().
959-
*
960953
* After the GPU has been suspended, the handler needs to be called to cut
961954
* power to the GPU. Likewise it needs to reinstate power before the GPU
962955
* can resume. This is achieved by vga_switcheroo_init_domain_pm_ops(),
963956
* which augments the GPU's suspend/resume functions by the requisite
964957
* calls to the handler.
965958
*
966959
* When the audio device resumes, the GPU needs to be woken. This is achieved
967-
* by vga_switcheroo_init_domain_pm_optimus_hdmi_audio(), which augments the
968-
* audio device's resume function.
960+
* by a PCI quirk which calls device_link_add() to declare a dependency on the
961+
* GPU. That way, the GPU is kept awake whenever and as long as the audio
962+
* device is in use.
969963
*
970964
* On muxed machines, if the mux is initially switched to the discrete GPU,
971965
* 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,
991985
vgasr_priv.handler->power_state(client->id, state);
992986
}
993987

994-
/**
995-
* vga_switcheroo_set_dynamic_switch() - helper for driver power control
996-
* @pdev: client pci device
997-
* @dynamic: new power state
998-
*
999-
* Helper for GPUs whose power state is controlled by the driver's runtime pm.
1000-
* When the driver decides to power up or down, it notifies vga_switcheroo
1001-
* thereof using this helper so that it can power the audio device on the GPU
1002-
* up or down.
1003-
*/
1004-
void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev,
1005-
enum vga_switcheroo_state dynamic)
1006-
{
1007-
struct vga_switcheroo_client *client;
1008-
1009-
mutex_lock(&vgasr_mutex);
1010-
client = find_client_from_pci(&vgasr_priv.clients, pdev);
1011-
if (!client || !client->driver_power_control) {
1012-
mutex_unlock(&vgasr_mutex);
1013-
return;
1014-
}
1015-
1016-
set_audio_state(client->id, dynamic);
1017-
mutex_unlock(&vgasr_mutex);
1018-
}
1019-
EXPORT_SYMBOL(vga_switcheroo_set_dynamic_switch);
1020-
1021988
/* switcheroo power domain */
1022989
static int vga_switcheroo_runtime_suspend(struct device *dev)
1023990
{
@@ -1089,69 +1056,3 @@ void vga_switcheroo_fini_domain_pm_ops(struct device *dev)
10891056
dev_pm_domain_set(dev, NULL);
10901057
}
10911058
EXPORT_SYMBOL(vga_switcheroo_fini_domain_pm_ops);
1092-
1093-
static int vga_switcheroo_runtime_resume_hdmi_audio(struct device *dev)
1094-
{
1095-
struct pci_dev *pdev = to_pci_dev(dev);
1096-
struct vga_switcheroo_client *client;
1097-
struct device *video_dev = NULL;
1098-
int ret;
1099-
1100-
/* we need to check if we have to switch back on the video
1101-
* device so the audio device can come back
1102-
*/
1103-
mutex_lock(&vgasr_mutex);
1104-
list_for_each_entry(client, &vgasr_priv.clients, list) {
1105-
if (PCI_SLOT(client->pdev->devfn) == PCI_SLOT(pdev->devfn) &&
1106-
client_is_vga(client)) {
1107-
video_dev = &client->pdev->dev;
1108-
break;
1109-
}
1110-
}
1111-
mutex_unlock(&vgasr_mutex);
1112-
1113-
if (video_dev) {
1114-
ret = pm_runtime_get_sync(video_dev);
1115-
if (ret && ret != 1)
1116-
return ret;
1117-
}
1118-
ret = dev->bus->pm->runtime_resume(dev);
1119-
1120-
/* put the reference for the gpu */
1121-
if (video_dev) {
1122-
pm_runtime_mark_last_busy(video_dev);
1123-
pm_runtime_put_autosuspend(video_dev);
1124-
}
1125-
return ret;
1126-
}
1127-
1128-
/**
1129-
* vga_switcheroo_init_domain_pm_optimus_hdmi_audio() - helper for driver
1130-
* power control
1131-
* @dev: audio client device
1132-
* @domain: power domain
1133-
*
1134-
* Helper for GPUs whose power state is controlled by the driver's runtime pm.
1135-
* When the audio device resumes, the GPU needs to be woken. This helper
1136-
* augments the audio device's resume function to do that.
1137-
*
1138-
* Return: 0 on success, -EINVAL if no power management operations are
1139-
* defined for this device.
1140-
*/
1141-
int
1142-
vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev,
1143-
struct dev_pm_domain *domain)
1144-
{
1145-
/* copy over all the bus versions */
1146-
if (dev->bus && dev->bus->pm) {
1147-
domain->ops = *dev->bus->pm;
1148-
domain->ops.runtime_resume =
1149-
vga_switcheroo_runtime_resume_hdmi_audio;
1150-
1151-
dev_pm_domain_set(dev, domain);
1152-
return 0;
1153-
}
1154-
dev_pm_domain_set(dev, NULL);
1155-
return -EINVAL;
1156-
}
1157-
EXPORT_SYMBOL(vga_switcheroo_init_domain_pm_optimus_hdmi_audio);

drivers/pci/quirks.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include <linux/ktime.h>
2727
#include <linux/mm.h>
2828
#include <linux/platform_data/x86/apple.h>
29+
#include <linux/pm_runtime.h>
2930
#include <asm/dma.h> /* isa_dma_bridge_buggy */
3031
#include "pci.h"
3132

@@ -4832,3 +4833,41 @@ static void quirk_fsl_no_msi(struct pci_dev *pdev)
48324833
pdev->no_msi = 1;
48334834
}
48344835
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_FREESCALE, PCI_ANY_ID, quirk_fsl_no_msi);
4836+
4837+
/*
4838+
* GPUs with integrated HDA controller for streaming audio to attached displays
4839+
* need a device link from the HDA controller (consumer) to the GPU (supplier)
4840+
* so that the GPU is powered up whenever the HDA controller is accessed.
4841+
* The GPU and HDA controller are functions 0 and 1 of the same PCI device.
4842+
* The device link stays in place until shutdown (or removal of the PCI device
4843+
* if it's hotplugged). Runtime PM is allowed by default on the HDA controller
4844+
* to prevent it from permanently keeping the GPU awake.
4845+
*/
4846+
static void quirk_gpu_hda(struct pci_dev *hda)
4847+
{
4848+
struct pci_dev *gpu;
4849+
4850+
if (PCI_FUNC(hda->devfn) != 1)
4851+
return;
4852+
4853+
gpu = pci_get_domain_bus_and_slot(pci_domain_nr(hda->bus),
4854+
hda->bus->number,
4855+
PCI_DEVFN(PCI_SLOT(hda->devfn), 0));
4856+
if (!gpu || (gpu->class >> 16) != PCI_BASE_CLASS_DISPLAY) {
4857+
pci_dev_put(gpu);
4858+
return;
4859+
}
4860+
4861+
if (!device_link_add(&hda->dev, &gpu->dev,
4862+
DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME))
4863+
pci_err(hda, "cannot link HDA to GPU %s\n", pci_name(gpu));
4864+
4865+
pm_runtime_allow(&hda->dev);
4866+
pci_dev_put(gpu);
4867+
}
4868+
DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_ATI, PCI_ANY_ID,
4869+
PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
4870+
DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_AMD, PCI_ANY_ID,
4871+
PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);
4872+
DECLARE_PCI_FIXUP_CLASS_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID,
4873+
PCI_CLASS_MULTIMEDIA_HD_AUDIO, 8, quirk_gpu_hda);

include/linux/pci_ids.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400
4646
#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401
4747
#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402
48+
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
4849
#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480
4950

5051
#define PCI_BASE_CLASS_MEMORY 0x05

include/linux/vga_switcheroo.h

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -168,11 +168,8 @@ int vga_switcheroo_process_delayed_switch(void);
168168
bool vga_switcheroo_client_probe_defer(struct pci_dev *pdev);
169169
enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *dev);
170170

171-
void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic);
172-
173171
int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain);
174172
void vga_switcheroo_fini_domain_pm_ops(struct device *dev);
175-
int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain);
176173
#else
177174

178175
static inline void vga_switcheroo_unregister_client(struct pci_dev *dev) {}
@@ -192,11 +189,8 @@ static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
192189
static inline bool vga_switcheroo_client_probe_defer(struct pci_dev *pdev) { return false; }
193190
static inline enum vga_switcheroo_state vga_switcheroo_get_client_state(struct pci_dev *dev) { return VGA_SWITCHEROO_ON; }
194191

195-
static inline void vga_switcheroo_set_dynamic_switch(struct pci_dev *pdev, enum vga_switcheroo_state dynamic) {}
196-
197192
static inline int vga_switcheroo_init_domain_pm_ops(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
198193
static inline void vga_switcheroo_fini_domain_pm_ops(struct device *dev) {}
199-
static inline int vga_switcheroo_init_domain_pm_optimus_hdmi_audio(struct device *dev, struct dev_pm_domain *domain) { return -EINVAL; }
200194

201195
#endif
202196
#endif /* _LINUX_VGA_SWITCHEROO_H_ */

include/sound/hdaudio.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -227,9 +227,6 @@ struct hdac_io_ops {
227227
#define HDA_UNSOL_QUEUE_SIZE 64
228228
#define HDA_MAX_CODECS 8 /* limit by controller side */
229229

230-
/* HD Audio class code */
231-
#define PCI_CLASS_MULTIMEDIA_HD_AUDIO 0x0403
232-
233230
/*
234231
* CORB/RIRB
235232
*

0 commit comments

Comments
 (0)