aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/gpu/vga/vga_switcheroo.c70
-rw-r--r--include/linux/vga_switcheroo.h6
2 files changed, 62 insertions, 14 deletions
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c
index a049b743cad0..38f9534ac513 100644
--- a/drivers/gpu/vga/vga_switcheroo.c
+++ b/drivers/gpu/vga/vga_switcheroo.c
@@ -57,6 +57,11 @@ struct vgasr_priv {
57 struct vga_switcheroo_handler *handler; 57 struct vga_switcheroo_handler *handler;
58}; 58};
59 59
60#define ID_BIT_AUDIO 0x100
61#define client_is_audio(c) ((c)->id & ID_BIT_AUDIO)
62#define client_is_vga(c) ((c)->id == -1 || !client_is_audio(c))
63#define client_id(c) ((c)->id & ~ID_BIT_AUDIO)
64
60static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); 65static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv);
61static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); 66static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv);
62 67
@@ -96,6 +101,8 @@ static void vga_switcheroo_enable(void)
96 vgasr_priv.handler->init(); 101 vgasr_priv.handler->init();
97 102
98 list_for_each_entry(client, &vgasr_priv.clients, list) { 103 list_for_each_entry(client, &vgasr_priv.clients, list) {
104 if (client->id != -1)
105 continue;
99 ret = vgasr_priv.handler->get_client_id(client->pdev); 106 ret = vgasr_priv.handler->get_client_id(client->pdev);
100 if (ret < 0) 107 if (ret < 0)
101 return; 108 return;
@@ -106,8 +113,9 @@ static void vga_switcheroo_enable(void)
106 vgasr_priv.active = true; 113 vgasr_priv.active = true;
107} 114}
108 115
109int vga_switcheroo_register_client(struct pci_dev *pdev, 116static int register_client(struct pci_dev *pdev,
110 const struct vga_switcheroo_client_ops *ops) 117 const struct vga_switcheroo_client_ops *ops,
118 int id, bool active)
111{ 119{
112 struct vga_switcheroo_client *client; 120 struct vga_switcheroo_client *client;
113 121
@@ -118,24 +126,40 @@ int vga_switcheroo_register_client(struct pci_dev *pdev,
118 client->pwr_state = VGA_SWITCHEROO_ON; 126 client->pwr_state = VGA_SWITCHEROO_ON;
119 client->pdev = pdev; 127 client->pdev = pdev;
120 client->ops = ops; 128 client->ops = ops;
121 client->id = -1; 129 client->id = id;
122 if (pdev == vga_default_device()) 130 client->active = active;
123 client->active = true;
124 131
125 mutex_lock(&vgasr_mutex); 132 mutex_lock(&vgasr_mutex);
126 list_add_tail(&client->list, &vgasr_priv.clients); 133 list_add_tail(&client->list, &vgasr_priv.clients);
127 vgasr_priv.registered_clients++; 134 if (client_is_vga(client))
135 vgasr_priv.registered_clients++;
128 136
129 /* if we get two clients + handler */ 137 /* if we get two clients + handler */
130 if (vgasr_priv.registered_clients == 2 && vgasr_priv.handler) { 138 if (!vgasr_priv.active &&
139 vgasr_priv.registered_clients == 2 && vgasr_priv.handler) {
131 printk(KERN_INFO "vga_switcheroo: enabled\n"); 140 printk(KERN_INFO "vga_switcheroo: enabled\n");
132 vga_switcheroo_enable(); 141 vga_switcheroo_enable();
133 } 142 }
134 mutex_unlock(&vgasr_mutex); 143 mutex_unlock(&vgasr_mutex);
135 return 0; 144 return 0;
136} 145}
146
147int vga_switcheroo_register_client(struct pci_dev *pdev,
148 const struct vga_switcheroo_client_ops *ops)
149{
150 return register_client(pdev, ops, -1,
151 pdev == vga_default_device());
152}
137EXPORT_SYMBOL(vga_switcheroo_register_client); 153EXPORT_SYMBOL(vga_switcheroo_register_client);
138 154
155int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
156 const struct vga_switcheroo_client_ops *ops,
157 int id, bool active)
158{
159 return register_client(pdev, ops, id | ID_BIT_AUDIO, active);
160}
161EXPORT_SYMBOL(vga_switcheroo_register_audio_client);
162
139static struct vga_switcheroo_client * 163static struct vga_switcheroo_client *
140find_client_from_pci(struct list_head *head, struct pci_dev *pdev) 164find_client_from_pci(struct list_head *head, struct pci_dev *pdev)
141{ 165{
@@ -161,7 +185,7 @@ find_active_client(struct list_head *head)
161{ 185{
162 struct vga_switcheroo_client *client; 186 struct vga_switcheroo_client *client;
163 list_for_each_entry(client, head, list) 187 list_for_each_entry(client, head, list)
164 if (client->active == true) 188 if (client->active && client_is_vga(client))
165 return client; 189 return client;
166 return NULL; 190 return NULL;
167} 191}
@@ -173,13 +197,16 @@ void vga_switcheroo_unregister_client(struct pci_dev *pdev)
173 mutex_lock(&vgasr_mutex); 197 mutex_lock(&vgasr_mutex);
174 client = find_client_from_pci(&vgasr_priv.clients, pdev); 198 client = find_client_from_pci(&vgasr_priv.clients, pdev);
175 if (client) { 199 if (client) {
200 if (client_is_vga(client))
201 vgasr_priv.registered_clients--;
176 list_del(&client->list); 202 list_del(&client->list);
177 kfree(client); 203 kfree(client);
178 vgasr_priv.registered_clients--;
179 } 204 }
180 printk(KERN_INFO "vga_switcheroo: disabled\n"); 205 if (vgasr_priv.active && vgasr_priv.registered_clients < 2) {
181 vga_switcheroo_debugfs_fini(&vgasr_priv); 206 printk(KERN_INFO "vga_switcheroo: disabled\n");
182 vgasr_priv.active = false; 207 vga_switcheroo_debugfs_fini(&vgasr_priv);
208 vgasr_priv.active = false;
209 }
183 mutex_unlock(&vgasr_mutex); 210 mutex_unlock(&vgasr_mutex);
184} 211}
185EXPORT_SYMBOL(vga_switcheroo_unregister_client); 212EXPORT_SYMBOL(vga_switcheroo_unregister_client);
@@ -203,8 +230,9 @@ static int vga_switcheroo_show(struct seq_file *m, void *v)
203 int i = 0; 230 int i = 0;
204 mutex_lock(&vgasr_mutex); 231 mutex_lock(&vgasr_mutex);
205 list_for_each_entry(client, &vgasr_priv.clients, list) { 232 list_for_each_entry(client, &vgasr_priv.clients, list) {
206 seq_printf(m, "%d:%s:%c:%s:%s\n", i, 233 seq_printf(m, "%d:%s%s:%c:%s:%s\n", i,
207 client->id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", 234 client_id(client) == VGA_SWITCHEROO_DIS ? "DIS" : "IGD",
235 client_is_vga(client) ? "" : "-Audio",
208 client->active ? '+' : ' ', 236 client->active ? '+' : ' ',
209 client->pwr_state ? "Pwr" : "Off", 237 client->pwr_state ? "Pwr" : "Off",
210 pci_name(client->pdev)); 238 pci_name(client->pdev));
@@ -239,6 +267,17 @@ static int vga_switchoff(struct vga_switcheroo_client *client)
239 return 0; 267 return 0;
240} 268}
241 269
270static void set_audio_state(int id, int state)
271{
272 struct vga_switcheroo_client *client;
273
274 client = find_client_from_id(&vgasr_priv.clients, id | ID_BIT_AUDIO);
275 if (client && client->pwr_state != state) {
276 client->ops->set_gpu_state(client->pdev, state);
277 client->pwr_state = state;
278 }
279}
280
242/* stage one happens before delay */ 281/* stage one happens before delay */
243static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) 282static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
244{ 283{
@@ -252,6 +291,7 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client)
252 vga_switchon(new_client); 291 vga_switchon(new_client);
253 292
254 vga_set_default_device(new_client->pdev); 293 vga_set_default_device(new_client->pdev);
294 set_audio_state(new_client->id, VGA_SWITCHEROO_ON);
255 295
256 return 0; 296 return 0;
257} 297}
@@ -281,6 +321,8 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client)
281 if (new_client->ops->reprobe) 321 if (new_client->ops->reprobe)
282 new_client->ops->reprobe(new_client->pdev); 322 new_client->ops->reprobe(new_client->pdev);
283 323
324 set_audio_state(active->id, VGA_SWITCHEROO_OFF);
325
284 if (active->pwr_state == VGA_SWITCHEROO_ON) 326 if (active->pwr_state == VGA_SWITCHEROO_ON)
285 vga_switchoff(active); 327 vga_switchoff(active);
286 328
diff --git a/include/linux/vga_switcheroo.h b/include/linux/vga_switcheroo.h
index 3c54ebc2e529..b455c7c212eb 100644
--- a/include/linux/vga_switcheroo.h
+++ b/include/linux/vga_switcheroo.h
@@ -38,6 +38,9 @@ struct vga_switcheroo_client_ops {
38void vga_switcheroo_unregister_client(struct pci_dev *dev); 38void vga_switcheroo_unregister_client(struct pci_dev *dev);
39int vga_switcheroo_register_client(struct pci_dev *dev, 39int vga_switcheroo_register_client(struct pci_dev *dev,
40 const struct vga_switcheroo_client_ops *ops); 40 const struct vga_switcheroo_client_ops *ops);
41int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
42 const struct vga_switcheroo_client_ops *ops,
43 int id, bool active);
41 44
42void vga_switcheroo_client_fb_set(struct pci_dev *dev, 45void vga_switcheroo_client_fb_set(struct pci_dev *dev,
43 struct fb_info *info); 46 struct fb_info *info);
@@ -54,6 +57,9 @@ static inline int vga_switcheroo_register_client(struct pci_dev *dev,
54 const struct vga_switcheroo_client_ops *ops) { return 0; } 57 const struct vga_switcheroo_client_ops *ops) { return 0; }
55static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {} 58static inline void vga_switcheroo_client_fb_set(struct pci_dev *dev, struct fb_info *info) {}
56static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; } 59static inline int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) { return 0; }
60static inline int vga_switcheroo_register_audio_client(struct pci_dev *pdev,
61 const struct vga_switcheroo_client_ops *ops,
62 int id, bool active) { return 0; }
57static inline void vga_switcheroo_unregister_handler(void) {} 63static inline void vga_switcheroo_unregister_handler(void) {}
58static inline int vga_switcheroo_process_delayed_switch(void) { return 0; } 64static inline int vga_switcheroo_process_delayed_switch(void) { return 0; }
59 65