Skip to content

Commit 37a3a98

Browse files
committed
ALSA: hda - Enable runtime PM only for discrete GPU
The recent change of vga_switcheroo allowed the runtime PM for HD-audio on AMD GPUs, but this also resulted in a regression. When the HD-audio controller driver gets runtime-suspended, HD-audio link is turned off, and the hotplug notification is ignored. This leads to the inconsistent audio state (the connection isn't notified and ELD is ignored). The best fix would be to implement the proper ELD notification via the audio component, but it's still not ready. As a quick workaround, this patch adds the check of runtime_idle and allows the runtime suspend only when the vga_switcheroo is bound with discrete GPU. That is, a system with a single GPU and APU would be again without runtime PM to keep the HD-audio link for the hotplug notification and ELD read out. Also, the codec->auto_runtime_pm flag is set only for the discrete GPU at the time GPU gets bound via vga_switcheroo (i.e. only dGPU is forcibly runtime-PM enabled), so that APU can still get the ELD notification. For identifying which GPU is bound, a new vga_switcheroo client callback, gpu_bound, is implemented. The vga_switcheroo simply calls this when GPU is bound, and tells whether it's dGPU or APU. Bugzilla: https://bugzilla.kernel.org/show_bug.cgi?id=200945 Fixes: 07f4f97 ("vga_switcheroo: Use device link for HDA controller") Reported-by: Jian-Hong Pan <[email protected]> Tested-by: Jian-Hong Pan <[email protected]> Acked-by: Lukas Wunner <[email protected]> Signed-off-by: Takashi Iwai <[email protected]>
1 parent 498fe23 commit 37a3a98

File tree

4 files changed

+69
-23
lines changed

4 files changed

+69
-23
lines changed

drivers/gpu/vga/vga_switcheroo.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ static void vga_switcheroo_enable(void)
215215
return;
216216

217217
client->id = ret | ID_BIT_AUDIO;
218+
if (client->ops->gpu_bound)
219+
client->ops->gpu_bound(client->pdev, ret);
218220
}
219221

220222
vga_switcheroo_debugfs_init(&vgasr_priv);

include/linux/vga_switcheroo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,18 @@ struct vga_switcheroo_handler {
133133
* @can_switch: check if the device is in a position to switch now.
134134
* Mandatory. The client should return false if a user space process
135135
* has one of its device files open
136+
* @gpu_bound: notify the client id to audio client when the GPU is bound.
136137
*
137138
* Client callbacks. A client can be either a GPU or an audio device on a GPU.
138139
* The @set_gpu_state and @can_switch methods are mandatory, @reprobe may be
139140
* set to NULL. For audio clients, the @reprobe member is bogus.
141+
* OTOH, @gpu_bound is only for audio clients, and not used for GPU clients.
140142
*/
141143
struct vga_switcheroo_client_ops {
142144
void (*set_gpu_state)(struct pci_dev *dev, enum vga_switcheroo_state);
143145
void (*reprobe)(struct pci_dev *dev);
144146
bool (*can_switch)(struct pci_dev *dev);
147+
void (*gpu_bound)(struct pci_dev *dev, enum vga_switcheroo_client_id);
145148
};
146149

147150
#if defined(CONFIG_VGA_SWITCHEROO)

sound/pci/hda/hda_intel.c

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,10 @@ enum {
365365
*/
366366
#ifdef SUPPORT_VGA_SWITCHEROO
367367
#define use_vga_switcheroo(chip) ((chip)->use_vga_switcheroo)
368+
#define needs_eld_notify_link(chip) ((chip)->need_eld_notify_link)
368369
#else
369370
#define use_vga_switcheroo(chip) 0
371+
#define needs_eld_notify_link(chip) false
370372
#endif
371373

372374
#define CONTROLLER_IN_GPU(pci) (((pci)->device == 0x0a0c) || \
@@ -453,6 +455,7 @@ static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev,
453455
#endif
454456

455457
static int azx_acquire_irq(struct azx *chip, int do_disconnect);
458+
static void set_default_power_save(struct azx *chip);
456459

457460
/*
458461
* initialize the PCI registers
@@ -1201,6 +1204,10 @@ static int azx_runtime_idle(struct device *dev)
12011204
azx_bus(chip)->codec_powered || !chip->running)
12021205
return -EBUSY;
12031206

1207+
/* ELD notification gets broken when HD-audio bus is off */
1208+
if (needs_eld_notify_link(hda))
1209+
return -EBUSY;
1210+
12041211
return 0;
12051212
}
12061213

@@ -1298,6 +1305,36 @@ static bool azx_vs_can_switch(struct pci_dev *pci)
12981305
return true;
12991306
}
13001307

1308+
/*
1309+
* The discrete GPU cannot power down unless the HDA controller runtime
1310+
* suspends, so activate runtime PM on codecs even if power_save == 0.
1311+
*/
1312+
static void setup_vga_switcheroo_runtime_pm(struct azx *chip)
1313+
{
1314+
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
1315+
struct hda_codec *codec;
1316+
1317+
if (hda->use_vga_switcheroo && !hda->need_eld_notify_link) {
1318+
list_for_each_codec(codec, &chip->bus)
1319+
codec->auto_runtime_pm = 1;
1320+
/* reset the power save setup */
1321+
if (chip->running)
1322+
set_default_power_save(chip);
1323+
}
1324+
}
1325+
1326+
static void azx_vs_gpu_bound(struct pci_dev *pci,
1327+
enum vga_switcheroo_client_id client_id)
1328+
{
1329+
struct snd_card *card = pci_get_drvdata(pci);
1330+
struct azx *chip = card->private_data;
1331+
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
1332+
1333+
if (client_id == VGA_SWITCHEROO_DIS)
1334+
hda->need_eld_notify_link = 0;
1335+
setup_vga_switcheroo_runtime_pm(chip);
1336+
}
1337+
13011338
static void init_vga_switcheroo(struct azx *chip)
13021339
{
13031340
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
@@ -1306,6 +1343,7 @@ static void init_vga_switcheroo(struct azx *chip)
13061343
dev_info(chip->card->dev,
13071344
"Handle vga_switcheroo audio client\n");
13081345
hda->use_vga_switcheroo = 1;
1346+
hda->need_eld_notify_link = 1; /* cleared in gpu_bound op */
13091347
chip->driver_caps |= AZX_DCAPS_PM_RUNTIME;
13101348
pci_dev_put(p);
13111349
}
@@ -1314,6 +1352,7 @@ static void init_vga_switcheroo(struct azx *chip)
13141352
static const struct vga_switcheroo_client_ops azx_vs_ops = {
13151353
.set_gpu_state = azx_vs_set_state,
13161354
.can_switch = azx_vs_can_switch,
1355+
.gpu_bound = azx_vs_gpu_bound,
13171356
};
13181357

13191358
static int register_vga_switcheroo(struct azx *chip)
@@ -1339,6 +1378,7 @@ static int register_vga_switcheroo(struct azx *chip)
13391378
#define init_vga_switcheroo(chip) /* NOP */
13401379
#define register_vga_switcheroo(chip) 0
13411380
#define check_hdmi_disabled(pci) false
1381+
#define setup_vga_switcheroo_runtime_pm(chip) /* NOP */
13421382
#endif /* SUPPORT_VGA_SWITCHER */
13431383

13441384
/*
@@ -1352,6 +1392,7 @@ static int azx_free(struct azx *chip)
13521392

13531393
if (azx_has_pm_runtime(chip) && chip->running)
13541394
pm_runtime_get_noresume(&pci->dev);
1395+
chip->running = 0;
13551396

13561397
azx_del_card_list(chip);
13571398

@@ -2230,6 +2271,25 @@ static struct snd_pci_quirk power_save_blacklist[] = {
22302271
};
22312272
#endif /* CONFIG_PM */
22322273

2274+
static void set_default_power_save(struct azx *chip)
2275+
{
2276+
int val = power_save;
2277+
2278+
#ifdef CONFIG_PM
2279+
if (pm_blacklist) {
2280+
const struct snd_pci_quirk *q;
2281+
2282+
q = snd_pci_quirk_lookup(chip->pci, power_save_blacklist);
2283+
if (q && val) {
2284+
dev_info(chip->card->dev, "device %04x:%04x is on the power_save blacklist, forcing power_save to 0\n",
2285+
q->subvendor, q->subdevice);
2286+
val = 0;
2287+
}
2288+
}
2289+
#endif /* CONFIG_PM */
2290+
snd_hda_set_power_save(&chip->bus, val * 1000);
2291+
}
2292+
22332293
/* number of codec slots for each chipset: 0 = default slots (i.e. 4) */
22342294
static unsigned int azx_max_codecs[AZX_NUM_DRIVERS] = {
22352295
[AZX_DRIVER_NVIDIA] = 8,
@@ -2241,9 +2301,7 @@ static int azx_probe_continue(struct azx *chip)
22412301
struct hda_intel *hda = container_of(chip, struct hda_intel, chip);
22422302
struct hdac_bus *bus = azx_bus(chip);
22432303
struct pci_dev *pci = chip->pci;
2244-
struct hda_codec *codec;
22452304
int dev = chip->dev_index;
2246-
int val;
22472305
int err;
22482306

22492307
hda->probe_continued = 1;
@@ -2322,31 +2380,13 @@ static int azx_probe_continue(struct azx *chip)
23222380
if (err < 0)
23232381
goto out_free;
23242382

2383+
setup_vga_switcheroo_runtime_pm(chip);
2384+
23252385
chip->running = 1;
23262386
azx_add_card_list(chip);
23272387

2328-
val = power_save;
2329-
#ifdef CONFIG_PM
2330-
if (pm_blacklist) {
2331-
const struct snd_pci_quirk *q;
2332-
2333-
q = snd_pci_quirk_lookup(chip->pci, power_save_blacklist);
2334-
if (q && val) {
2335-
dev_info(chip->card->dev, "device %04x:%04x is on the power_save blacklist, forcing power_save to 0\n",
2336-
q->subvendor, q->subdevice);
2337-
val = 0;
2338-
}
2339-
}
2340-
#endif /* CONFIG_PM */
2341-
/*
2342-
* The discrete GPU cannot power down unless the HDA controller runtime
2343-
* suspends, so activate runtime PM on codecs even if power_save == 0.
2344-
*/
2345-
if (use_vga_switcheroo(hda))
2346-
list_for_each_codec(codec, &chip->bus)
2347-
codec->auto_runtime_pm = 1;
2388+
set_default_power_save(chip);
23482389

2349-
snd_hda_set_power_save(&chip->bus, val * 1000);
23502390
if (azx_has_pm_runtime(chip))
23512391
pm_runtime_put_autosuspend(&pci->dev);
23522392

sound/pci/hda/hda_intel.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ struct hda_intel {
3737

3838
/* vga_switcheroo setup */
3939
unsigned int use_vga_switcheroo:1;
40+
unsigned int need_eld_notify_link:1;
4041
unsigned int vga_switcheroo_registered:1;
4142
unsigned int init_failed:1; /* delayed init failed */
4243

0 commit comments

Comments
 (0)