27
27
#include <linux/pci.h>
28
28
#include <linux/console.h>
29
29
#include <linux/vga_switcheroo.h>
30
+ #include <linux/pm_runtime.h>
30
31
31
32
#include <linux/vgaarb.h>
32
33
@@ -37,6 +38,7 @@ struct vga_switcheroo_client {
37
38
const struct vga_switcheroo_client_ops * ops ;
38
39
int id ;
39
40
bool active ;
41
+ bool driver_power_control ;
40
42
struct list_head list ;
41
43
};
42
44
@@ -132,7 +134,7 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_handler);
132
134
133
135
static int register_client (struct pci_dev * pdev ,
134
136
const struct vga_switcheroo_client_ops * ops ,
135
- int id , bool active )
137
+ int id , bool active , bool driver_power_control )
136
138
{
137
139
struct vga_switcheroo_client * client ;
138
140
@@ -145,6 +147,7 @@ static int register_client(struct pci_dev *pdev,
145
147
client -> ops = ops ;
146
148
client -> id = id ;
147
149
client -> active = active ;
150
+ client -> driver_power_control = driver_power_control ;
148
151
149
152
mutex_lock (& vgasr_mutex );
150
153
list_add_tail (& client -> list , & vgasr_priv .clients );
@@ -160,18 +163,19 @@ static int register_client(struct pci_dev *pdev,
160
163
}
161
164
162
165
int vga_switcheroo_register_client (struct pci_dev * pdev ,
163
- const struct vga_switcheroo_client_ops * ops )
166
+ const struct vga_switcheroo_client_ops * ops ,
167
+ bool driver_power_control )
164
168
{
165
169
return register_client (pdev , ops , -1 ,
166
- pdev == vga_default_device ());
170
+ pdev == vga_default_device (), driver_power_control );
167
171
}
168
172
EXPORT_SYMBOL (vga_switcheroo_register_client );
169
173
170
174
int vga_switcheroo_register_audio_client (struct pci_dev * pdev ,
171
175
const struct vga_switcheroo_client_ops * ops ,
172
176
int id , bool active )
173
177
{
174
- return register_client (pdev , ops , id | ID_BIT_AUDIO , active );
178
+ return register_client (pdev , ops , id | ID_BIT_AUDIO , active , false );
175
179
}
176
180
EXPORT_SYMBOL (vga_switcheroo_register_audio_client );
177
181
@@ -258,10 +262,11 @@ static int vga_switcheroo_show(struct seq_file *m, void *v)
258
262
int i = 0 ;
259
263
mutex_lock (& vgasr_mutex );
260
264
list_for_each_entry (client , & vgasr_priv .clients , list ) {
261
- seq_printf (m , "%d:%s%s:%c:%s:%s\n" , i ,
265
+ seq_printf (m , "%d:%s%s:%c:%s%s :%s\n" , i ,
262
266
client_id (client ) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD" ,
263
267
client_is_vga (client ) ? "" : "-Audio" ,
264
268
client -> active ? '+' : ' ' ,
269
+ client -> driver_power_control ? "Dyn" : "" ,
265
270
client -> pwr_state ? "Pwr" : "Off" ,
266
271
pci_name (client -> pdev ));
267
272
i ++ ;
@@ -277,6 +282,8 @@ static int vga_switcheroo_debugfs_open(struct inode *inode, struct file *file)
277
282
278
283
static int vga_switchon (struct vga_switcheroo_client * client )
279
284
{
285
+ if (client -> driver_power_control )
286
+ return 0 ;
280
287
if (vgasr_priv .handler -> power_state )
281
288
vgasr_priv .handler -> power_state (client -> id , VGA_SWITCHEROO_ON );
282
289
/* call the driver callback to turn on device */
@@ -287,6 +294,8 @@ static int vga_switchon(struct vga_switcheroo_client *client)
287
294
288
295
static int vga_switchoff (struct vga_switcheroo_client * client )
289
296
{
297
+ if (client -> driver_power_control )
298
+ return 0 ;
290
299
/* call the driver callback to turn off device */
291
300
client -> ops -> set_gpu_state (client -> pdev , VGA_SWITCHEROO_OFF );
292
301
if (vgasr_priv .handler -> power_state )
@@ -402,6 +411,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
402
411
list_for_each_entry (client , & vgasr_priv .clients , list ) {
403
412
if (client -> active || client_is_audio (client ))
404
413
continue ;
414
+ if (client -> driver_power_control )
415
+ continue ;
405
416
set_audio_state (client -> id , VGA_SWITCHEROO_OFF );
406
417
if (client -> pwr_state == VGA_SWITCHEROO_ON )
407
418
vga_switchoff (client );
@@ -413,6 +424,8 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf,
413
424
list_for_each_entry (client , & vgasr_priv .clients , list ) {
414
425
if (client -> active || client_is_audio (client ))
415
426
continue ;
427
+ if (client -> driver_power_control )
428
+ continue ;
416
429
if (client -> pwr_state == VGA_SWITCHEROO_OFF )
417
430
vga_switchon (client );
418
431
set_audio_state (client -> id , VGA_SWITCHEROO_ON );
@@ -565,3 +578,127 @@ int vga_switcheroo_process_delayed_switch(void)
565
578
return err ;
566
579
}
567
580
EXPORT_SYMBOL (vga_switcheroo_process_delayed_switch );
581
+
582
+ static void vga_switcheroo_power_switch (struct pci_dev * pdev , enum vga_switcheroo_state state )
583
+ {
584
+ struct vga_switcheroo_client * client ;
585
+
586
+ if (!vgasr_priv .handler -> power_state )
587
+ return ;
588
+
589
+ client = find_client_from_pci (& vgasr_priv .clients , pdev );
590
+ if (!client )
591
+ return ;
592
+
593
+ if (!client -> driver_power_control )
594
+ return ;
595
+
596
+ vgasr_priv .handler -> power_state (client -> id , state );
597
+ }
598
+
599
+ /* force a PCI device to a certain state - mainly to turn off audio clients */
600
+
601
+ void vga_switcheroo_set_dynamic_switch (struct pci_dev * pdev , enum vga_switcheroo_state dynamic )
602
+ {
603
+ struct vga_switcheroo_client * client ;
604
+
605
+ client = find_client_from_pci (& vgasr_priv .clients , pdev );
606
+ if (!client )
607
+ return ;
608
+
609
+ if (!client -> driver_power_control )
610
+ return ;
611
+
612
+ client -> pwr_state = dynamic ;
613
+ set_audio_state (client -> id , dynamic );
614
+ }
615
+ EXPORT_SYMBOL (vga_switcheroo_set_dynamic_switch );
616
+
617
+ /* switcheroo power domain */
618
+ static int vga_switcheroo_runtime_suspend (struct device * dev )
619
+ {
620
+ struct pci_dev * pdev = to_pci_dev (dev );
621
+ int ret ;
622
+
623
+ ret = dev -> bus -> pm -> runtime_suspend (dev );
624
+ if (ret )
625
+ return ret ;
626
+
627
+ vga_switcheroo_power_switch (pdev , VGA_SWITCHEROO_OFF );
628
+ return 0 ;
629
+ }
630
+
631
+ static int vga_switcheroo_runtime_resume (struct device * dev )
632
+ {
633
+ struct pci_dev * pdev = to_pci_dev (dev );
634
+ int ret ;
635
+
636
+ vga_switcheroo_power_switch (pdev , VGA_SWITCHEROO_ON );
637
+ ret = dev -> bus -> pm -> runtime_resume (dev );
638
+ if (ret )
639
+ return ret ;
640
+
641
+ return 0 ;
642
+ }
643
+
644
+ /* this version is for the case where the power switch is separate
645
+ to the device being powered down. */
646
+ int vga_switcheroo_init_domain_pm_ops (struct device * dev , struct dev_pm_domain * domain )
647
+ {
648
+ /* copy over all the bus versions */
649
+ if (dev -> bus && dev -> bus -> pm ) {
650
+ domain -> ops = * dev -> bus -> pm ;
651
+ domain -> ops .runtime_suspend = vga_switcheroo_runtime_suspend ;
652
+ domain -> ops .runtime_resume = vga_switcheroo_runtime_resume ;
653
+
654
+ dev -> pm_domain = domain ;
655
+ return 0 ;
656
+ }
657
+ dev -> pm_domain = NULL ;
658
+ return - EINVAL ;
659
+ }
660
+ EXPORT_SYMBOL (vga_switcheroo_init_domain_pm_ops );
661
+
662
+ static int vga_switcheroo_runtime_resume_hdmi_audio (struct device * dev )
663
+ {
664
+ struct pci_dev * pdev = to_pci_dev (dev );
665
+ int ret ;
666
+ struct vga_switcheroo_client * client , * found = NULL ;
667
+
668
+ /* we need to check if we have to switch back on the video
669
+ device so the audio device can come back */
670
+ list_for_each_entry (client , & vgasr_priv .clients , list ) {
671
+ if (PCI_SLOT (client -> pdev -> devfn ) == PCI_SLOT (pdev -> devfn ) && client_is_vga (client )) {
672
+ found = client ;
673
+ ret = pm_runtime_get_sync (& client -> pdev -> dev );
674
+ if (ret ) {
675
+ if (ret != 1 )
676
+ return ret ;
677
+ }
678
+ break ;
679
+ }
680
+ }
681
+ ret = dev -> bus -> pm -> runtime_resume (dev );
682
+
683
+ /* put the reference for the gpu */
684
+ if (found ) {
685
+ pm_runtime_mark_last_busy (& found -> pdev -> dev );
686
+ pm_runtime_put_autosuspend (& found -> pdev -> dev );
687
+ }
688
+ return ret ;
689
+ }
690
+
691
+ int vga_switcheroo_init_domain_pm_optimus_hdmi_audio (struct device * dev , struct dev_pm_domain * domain )
692
+ {
693
+ /* copy over all the bus versions */
694
+ if (dev -> bus && dev -> bus -> pm ) {
695
+ domain -> ops = * dev -> bus -> pm ;
696
+ domain -> ops .runtime_resume = vga_switcheroo_runtime_resume_hdmi_audio ;
697
+
698
+ dev -> pm_domain = domain ;
699
+ return 0 ;
700
+ }
701
+ dev -> pm_domain = NULL ;
702
+ return - EINVAL ;
703
+ }
704
+ EXPORT_SYMBOL (vga_switcheroo_init_domain_pm_optimus_hdmi_audio );
0 commit comments