Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vga_switcheroo: set audio client id according to bound GPU id

On modern laptop, there are more and more platforms
have two GPUs, and each of them maybe have audio codec
for HDMP/DP output. For some dGPU which is no output,
audio codec usually is disabled.

In currect HDA audio driver, it will set all codec as
VGA_SWITCHEROO_DIS, the audio which is binded to UMA
will be suspended if user use debugfs to contorl power

In HDA driver side, it is difficult to know which GPU
the audio has binded to. So set the bound gpu pci dev
to vga_switcheroo.

if the audio client is not the third registration, audio
id will set in vga_switcheroo enable function. if the
audio client is the last registration when vga_switcheroo
_ready() get true, we should get audio client id from bound
GPU directly.

Signed-off-by: Jim Qu <Jim.Qu@amd.com>
Reviewed-by: Lukas Wunner <lukas@wunner.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>

authored by

Jim Qu and committed by
Takashi Iwai
4aaf448f 9d3cce1e

+62 -20
+52 -11
drivers/gpu/vga/vga_switcheroo.c
··· 103 103 * runtime pm. If true, writing ON and OFF to the vga_switcheroo debugfs 104 104 * interface is a no-op so as not to interfere with runtime pm 105 105 * @list: client list 106 + * @vga_dev: pci device, indicate which GPU is bound to current audio client 106 107 * 107 108 * Registered client. A client can be either a GPU or an audio device on a GPU. 108 - * For audio clients, the @fb_info and @active members are bogus. 109 + * For audio clients, the @fb_info and @active members are bogus. For GPU 110 + * clients, the @vga_dev is bogus. 109 111 */ 110 112 struct vga_switcheroo_client { 111 113 struct pci_dev *pdev; ··· 118 116 bool active; 119 117 bool driver_power_control; 120 118 struct list_head list; 119 + struct pci_dev *vga_dev; 121 120 }; 122 121 123 122 /* ··· 164 161 }; 165 162 166 163 #define ID_BIT_AUDIO 0x100 167 - #define client_is_audio(c) ((c)->id & ID_BIT_AUDIO) 168 - #define client_is_vga(c) ((c)->id == VGA_SWITCHEROO_UNKNOWN_ID || \ 169 - !client_is_audio(c)) 164 + #define client_is_audio(c) ((c)->id & ID_BIT_AUDIO) 165 + #define client_is_vga(c) (!client_is_audio(c)) 170 166 #define client_id(c) ((c)->id & ~ID_BIT_AUDIO) 171 167 172 168 static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); ··· 194 192 vgasr_priv.handler->init(); 195 193 196 194 list_for_each_entry(client, &vgasr_priv.clients, list) { 197 - if (client->id != VGA_SWITCHEROO_UNKNOWN_ID) 195 + if (!client_is_vga(client) || 196 + client_id(client) != VGA_SWITCHEROO_UNKNOWN_ID) 198 197 continue; 198 + 199 199 ret = vgasr_priv.handler->get_client_id(client->pdev); 200 200 if (ret < 0) 201 201 return; 202 202 203 203 client->id = ret; 204 204 } 205 + 206 + list_for_each_entry(client, &vgasr_priv.clients, list) { 207 + if (!client_is_audio(client) || 208 + client_id(client) != VGA_SWITCHEROO_UNKNOWN_ID) 209 + continue; 210 + 211 + ret = vgasr_priv.handler->get_client_id(client->vga_dev); 212 + if (ret < 0) 213 + return; 214 + 215 + client->id = ret | ID_BIT_AUDIO; 216 + } 217 + 205 218 vga_switcheroo_debugfs_init(&vgasr_priv); 206 219 vgasr_priv.active = true; 207 220 } ··· 289 272 290 273 static int register_client(struct pci_dev *pdev, 291 274 const struct vga_switcheroo_client_ops *ops, 292 - enum vga_switcheroo_client_id id, bool active, 275 + enum vga_switcheroo_client_id id, 276 + struct pci_dev *vga_dev, 277 + bool active, 293 278 bool driver_power_control) 294 279 { 295 280 struct vga_switcheroo_client *client; ··· 306 287 client->id = id; 307 288 client->active = active; 308 289 client->driver_power_control = driver_power_control; 290 + client->vga_dev = vga_dev; 309 291 310 292 mutex_lock(&vgasr_mutex); 311 293 list_add_tail(&client->list, &vgasr_priv.clients); ··· 339 319 const struct vga_switcheroo_client_ops *ops, 340 320 bool driver_power_control) 341 321 { 342 - return register_client(pdev, ops, VGA_SWITCHEROO_UNKNOWN_ID, 322 + return register_client(pdev, ops, VGA_SWITCHEROO_UNKNOWN_ID, NULL, 343 323 pdev == vga_default_device(), 344 324 driver_power_control); 345 325 } ··· 349 329 * vga_switcheroo_register_audio_client - register audio client 350 330 * @pdev: client pci device 351 331 * @ops: client callbacks 352 - * @id: client identifier 332 + * @vga_dev: pci device which is bound to current audio client 353 333 * 354 334 * Register audio client (audio device on a GPU). The client is assumed 355 335 * to use runtime PM. Beforehand, vga_switcheroo_client_probe_defer() 356 336 * shall be called to ensure that all prerequisites are met. 357 337 * 358 - * Return: 0 on success, -ENOMEM on memory allocation error. 338 + * Return: 0 on success, -ENOMEM on memory allocation error, -EINVAL on getting 339 + * client id error. 359 340 */ 360 341 int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 361 342 const struct vga_switcheroo_client_ops *ops, 362 - enum vga_switcheroo_client_id id) 343 + struct pci_dev *vga_dev) 363 344 { 364 - return register_client(pdev, ops, id | ID_BIT_AUDIO, false, true); 345 + enum vga_switcheroo_client_id id = VGA_SWITCHEROO_UNKNOWN_ID; 346 + 347 + /* 348 + * if vga_switcheroo has enabled, that mean two GPU clients and also 349 + * handler are registered. Get audio client id from bound GPU client 350 + * id directly, otherwise, set it as VGA_SWITCHEROO_UNKNOWN_ID, 351 + * it will set to correct id in later when vga_switcheroo_enable() 352 + * is called. 353 + */ 354 + mutex_lock(&vgasr_mutex); 355 + if (vgasr_priv.active) { 356 + id = vgasr_priv.handler->get_client_id(vga_dev); 357 + if (id < 0) { 358 + mutex_unlock(&vgasr_mutex); 359 + return -EINVAL; 360 + } 361 + } 362 + mutex_unlock(&vgasr_mutex); 363 + 364 + return register_client(pdev, ops, id | ID_BIT_AUDIO, vga_dev, 365 + false, true); 365 366 } 366 367 EXPORT_SYMBOL(vga_switcheroo_register_audio_client); 367 368
+4 -4
include/linux/vga_switcheroo.h
··· 84 84 * Client identifier. Audio clients use the same identifier & 0x100. 85 85 */ 86 86 enum vga_switcheroo_client_id { 87 - VGA_SWITCHEROO_UNKNOWN_ID = -1, 88 - VGA_SWITCHEROO_IGD, 87 + VGA_SWITCHEROO_UNKNOWN_ID = 0x1000, 88 + VGA_SWITCHEROO_IGD = 0, 89 89 VGA_SWITCHEROO_DIS, 90 90 VGA_SWITCHEROO_MAX_CLIENTS, 91 91 }; ··· 151 151 bool driver_power_control); 152 152 int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 153 153 const struct vga_switcheroo_client_ops *ops, 154 - enum vga_switcheroo_client_id id); 154 + struct pci_dev *vga_dev); 155 155 156 156 void vga_switcheroo_client_fb_set(struct pci_dev *dev, 157 157 struct fb_info *info); ··· 180 180 enum vga_switcheroo_handler_flags_t handler_flags) { return 0; } 181 181 static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev, 182 182 const struct vga_switcheroo_client_ops *ops, 183 - enum vga_switcheroo_client_id id) { return 0; } 183 + struct pci_dev *vga_dev) { return 0; } 184 184 static inline void vga_switcheroo_unregister_handler(void) {} 185 185 static inline enum vga_switcheroo_handler_flags_t vga_switcheroo_handler_flags(void) { return 0; } 186 186 static inline int vga_switcheroo_lock_ddc(struct pci_dev *pdev) { return -ENODEV; }
+6 -5
sound/pci/hda/hda_intel.c
··· 1319 1319 static int register_vga_switcheroo(struct azx *chip) 1320 1320 { 1321 1321 struct hda_intel *hda = container_of(chip, struct hda_intel, chip); 1322 + struct pci_dev *p; 1322 1323 int err; 1323 1324 1324 1325 if (!hda->use_vga_switcheroo) 1325 1326 return 0; 1326 - /* FIXME: currently only handling DIS controller 1327 - * is there any machine with two switchable HDMI audio controllers? 1328 - */ 1329 - err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, 1330 - VGA_SWITCHEROO_DIS); 1327 + 1328 + p = get_bound_vga(chip->pci); 1329 + err = vga_switcheroo_register_audio_client(chip->pci, &azx_vs_ops, p); 1330 + pci_dev_put(p); 1331 + 1331 1332 if (err < 0) 1332 1333 return err; 1333 1334 hda->vga_switcheroo_registered = 1;