diff options
author | Takashi Iwai <tiwai@suse.de> | 2012-04-26 06:55:59 -0400 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2012-05-13 05:21:56 -0400 |
commit | 79721e0a91b5e8f662f12eeb50ea205c761e6bf8 (patch) | |
tree | 5ab91e6e5342b7b844ace65cda62db364b17c2fe /drivers/gpu/vga | |
parent | 218c872bf8285af7aaa50f1f83312020e05451bf (diff) |
vga_switcheroo: Refactor using linked list
Refactor the code base a bit for the further work to adapt more clients.
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Diffstat (limited to 'drivers/gpu/vga')
-rw-r--r-- | drivers/gpu/vga/vga_switcheroo.c | 209 |
1 files changed, 110 insertions, 99 deletions
diff --git a/drivers/gpu/vga/vga_switcheroo.c b/drivers/gpu/vga/vga_switcheroo.c index 9d830286f883..da29da6aadac 100644 --- a/drivers/gpu/vga/vga_switcheroo.c +++ b/drivers/gpu/vga/vga_switcheroo.c | |||
@@ -39,6 +39,7 @@ struct vga_switcheroo_client { | |||
39 | bool (*can_switch)(struct pci_dev *pdev); | 39 | bool (*can_switch)(struct pci_dev *pdev); |
40 | int id; | 40 | int id; |
41 | bool active; | 41 | bool active; |
42 | struct list_head list; | ||
42 | }; | 43 | }; |
43 | 44 | ||
44 | static DEFINE_MUTEX(vgasr_mutex); | 45 | static DEFINE_MUTEX(vgasr_mutex); |
@@ -53,7 +54,7 @@ struct vgasr_priv { | |||
53 | struct dentry *switch_file; | 54 | struct dentry *switch_file; |
54 | 55 | ||
55 | int registered_clients; | 56 | int registered_clients; |
56 | struct vga_switcheroo_client clients[VGA_SWITCHEROO_MAX_CLIENTS]; | 57 | struct list_head clients; |
57 | 58 | ||
58 | struct vga_switcheroo_handler *handler; | 59 | struct vga_switcheroo_handler *handler; |
59 | }; | 60 | }; |
@@ -62,7 +63,9 @@ static int vga_switcheroo_debugfs_init(struct vgasr_priv *priv); | |||
62 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); | 63 | static void vga_switcheroo_debugfs_fini(struct vgasr_priv *priv); |
63 | 64 | ||
64 | /* only one switcheroo per system */ | 65 | /* only one switcheroo per system */ |
65 | static struct vgasr_priv vgasr_priv; | 66 | static struct vgasr_priv vgasr_priv = { |
67 | .clients = LIST_HEAD_INIT(vgasr_priv.clients), | ||
68 | }; | ||
66 | 69 | ||
67 | int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) | 70 | int vga_switcheroo_register_handler(struct vga_switcheroo_handler *handler) |
68 | { | 71 | { |
@@ -88,17 +91,18 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_handler); | |||
88 | 91 | ||
89 | static void vga_switcheroo_enable(void) | 92 | static void vga_switcheroo_enable(void) |
90 | { | 93 | { |
91 | int i; | ||
92 | int ret; | 94 | int ret; |
95 | struct vga_switcheroo_client *client; | ||
96 | |||
93 | /* call the handler to init */ | 97 | /* call the handler to init */ |
94 | vgasr_priv.handler->init(); | 98 | vgasr_priv.handler->init(); |
95 | 99 | ||
96 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 100 | list_for_each_entry(client, &vgasr_priv.clients, list) { |
97 | ret = vgasr_priv.handler->get_client_id(vgasr_priv.clients[i].pdev); | 101 | ret = vgasr_priv.handler->get_client_id(client->pdev); |
98 | if (ret < 0) | 102 | if (ret < 0) |
99 | return; | 103 | return; |
100 | 104 | ||
101 | vgasr_priv.clients[i].id = ret; | 105 | client->id = ret; |
102 | } | 106 | } |
103 | vga_switcheroo_debugfs_init(&vgasr_priv); | 107 | vga_switcheroo_debugfs_init(&vgasr_priv); |
104 | vgasr_priv.active = true; | 108 | vgasr_priv.active = true; |
@@ -109,28 +113,27 @@ int vga_switcheroo_register_client(struct pci_dev *pdev, | |||
109 | void (*reprobe)(struct pci_dev *pdev), | 113 | void (*reprobe)(struct pci_dev *pdev), |
110 | bool (*can_switch)(struct pci_dev *pdev)) | 114 | bool (*can_switch)(struct pci_dev *pdev)) |
111 | { | 115 | { |
112 | int index; | 116 | struct vga_switcheroo_client *client; |
113 | 117 | ||
114 | mutex_lock(&vgasr_mutex); | 118 | client = kzalloc(sizeof(*client), GFP_KERNEL); |
115 | /* don't do IGD vs DIS here */ | 119 | if (!client) |
116 | if (vgasr_priv.registered_clients & 1) | 120 | return -ENOMEM; |
117 | index = 1; | 121 | |
118 | else | 122 | client->pwr_state = VGA_SWITCHEROO_ON; |
119 | index = 0; | 123 | client->pdev = pdev; |
120 | 124 | client->set_gpu_state = set_gpu_state; | |
121 | vgasr_priv.clients[index].pwr_state = VGA_SWITCHEROO_ON; | 125 | client->reprobe = reprobe; |
122 | vgasr_priv.clients[index].pdev = pdev; | 126 | client->can_switch = can_switch; |
123 | vgasr_priv.clients[index].set_gpu_state = set_gpu_state; | 127 | client->id = -1; |
124 | vgasr_priv.clients[index].reprobe = reprobe; | ||
125 | vgasr_priv.clients[index].can_switch = can_switch; | ||
126 | vgasr_priv.clients[index].id = -1; | ||
127 | if (pdev == vga_default_device()) | 128 | if (pdev == vga_default_device()) |
128 | vgasr_priv.clients[index].active = true; | 129 | client->active = true; |
129 | 130 | ||
130 | vgasr_priv.registered_clients |= (1 << index); | 131 | mutex_lock(&vgasr_mutex); |
132 | list_add_tail(&client->list, &vgasr_priv.clients); | ||
133 | vgasr_priv.registered_clients++; | ||
131 | 134 | ||
132 | /* if we get two clients + handler */ | 135 | /* if we get two clients + handler */ |
133 | if (vgasr_priv.registered_clients == 0x3 && vgasr_priv.handler) { | 136 | if (vgasr_priv.registered_clients == 2 && vgasr_priv.handler) { |
134 | printk(KERN_INFO "vga_switcheroo: enabled\n"); | 137 | printk(KERN_INFO "vga_switcheroo: enabled\n"); |
135 | vga_switcheroo_enable(); | 138 | vga_switcheroo_enable(); |
136 | } | 139 | } |
@@ -139,18 +142,47 @@ int vga_switcheroo_register_client(struct pci_dev *pdev, | |||
139 | } | 142 | } |
140 | EXPORT_SYMBOL(vga_switcheroo_register_client); | 143 | EXPORT_SYMBOL(vga_switcheroo_register_client); |
141 | 144 | ||
145 | static struct vga_switcheroo_client * | ||
146 | find_client_from_pci(struct list_head *head, struct pci_dev *pdev) | ||
147 | { | ||
148 | struct vga_switcheroo_client *client; | ||
149 | list_for_each_entry(client, head, list) | ||
150 | if (client->pdev == pdev) | ||
151 | return client; | ||
152 | return NULL; | ||
153 | } | ||
154 | |||
155 | static struct vga_switcheroo_client * | ||
156 | find_client_from_id(struct list_head *head, int client_id) | ||
157 | { | ||
158 | struct vga_switcheroo_client *client; | ||
159 | list_for_each_entry(client, head, list) | ||
160 | if (client->id == client_id) | ||
161 | return client; | ||
162 | return NULL; | ||
163 | } | ||
164 | |||
165 | static struct vga_switcheroo_client * | ||
166 | find_active_client(struct list_head *head) | ||
167 | { | ||
168 | struct vga_switcheroo_client *client; | ||
169 | list_for_each_entry(client, head, list) | ||
170 | if (client->active == true) | ||
171 | return client; | ||
172 | return NULL; | ||
173 | } | ||
174 | |||
142 | void vga_switcheroo_unregister_client(struct pci_dev *pdev) | 175 | void vga_switcheroo_unregister_client(struct pci_dev *pdev) |
143 | { | 176 | { |
144 | int i; | 177 | struct vga_switcheroo_client *client; |
145 | 178 | ||
146 | mutex_lock(&vgasr_mutex); | 179 | mutex_lock(&vgasr_mutex); |
147 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 180 | client = find_client_from_pci(&vgasr_priv.clients, pdev); |
148 | if (vgasr_priv.clients[i].pdev == pdev) { | 181 | if (client) { |
149 | vgasr_priv.registered_clients &= ~(1 << i); | 182 | list_del(&client->list); |
150 | break; | 183 | kfree(client); |
151 | } | 184 | vgasr_priv.registered_clients--; |
152 | } | 185 | } |
153 | |||
154 | printk(KERN_INFO "vga_switcheroo: disabled\n"); | 186 | printk(KERN_INFO "vga_switcheroo: disabled\n"); |
155 | vga_switcheroo_debugfs_fini(&vgasr_priv); | 187 | vga_switcheroo_debugfs_fini(&vgasr_priv); |
156 | vgasr_priv.active = false; | 188 | vgasr_priv.active = false; |
@@ -161,29 +193,28 @@ EXPORT_SYMBOL(vga_switcheroo_unregister_client); | |||
161 | void vga_switcheroo_client_fb_set(struct pci_dev *pdev, | 193 | void vga_switcheroo_client_fb_set(struct pci_dev *pdev, |
162 | struct fb_info *info) | 194 | struct fb_info *info) |
163 | { | 195 | { |
164 | int i; | 196 | struct vga_switcheroo_client *client; |
165 | 197 | ||
166 | mutex_lock(&vgasr_mutex); | 198 | mutex_lock(&vgasr_mutex); |
167 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 199 | client = find_client_from_pci(&vgasr_priv.clients, pdev); |
168 | if (vgasr_priv.clients[i].pdev == pdev) { | 200 | if (client) |
169 | vgasr_priv.clients[i].fb_info = info; | 201 | client->fb_info = info; |
170 | break; | ||
171 | } | ||
172 | } | ||
173 | mutex_unlock(&vgasr_mutex); | 202 | mutex_unlock(&vgasr_mutex); |
174 | } | 203 | } |
175 | EXPORT_SYMBOL(vga_switcheroo_client_fb_set); | 204 | EXPORT_SYMBOL(vga_switcheroo_client_fb_set); |
176 | 205 | ||
177 | static int vga_switcheroo_show(struct seq_file *m, void *v) | 206 | static int vga_switcheroo_show(struct seq_file *m, void *v) |
178 | { | 207 | { |
179 | int i; | 208 | struct vga_switcheroo_client *client; |
209 | int i = 0; | ||
180 | mutex_lock(&vgasr_mutex); | 210 | mutex_lock(&vgasr_mutex); |
181 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 211 | list_for_each_entry(client, &vgasr_priv.clients, list) { |
182 | seq_printf(m, "%d:%s:%c:%s:%s\n", i, | 212 | seq_printf(m, "%d:%s:%c:%s:%s\n", i, |
183 | vgasr_priv.clients[i].id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", | 213 | client->id == VGA_SWITCHEROO_DIS ? "DIS" : "IGD", |
184 | vgasr_priv.clients[i].active ? '+' : ' ', | 214 | client->active ? '+' : ' ', |
185 | vgasr_priv.clients[i].pwr_state ? "Pwr" : "Off", | 215 | client->pwr_state ? "Pwr" : "Off", |
186 | pci_name(vgasr_priv.clients[i].pdev)); | 216 | pci_name(client->pdev)); |
217 | i++; | ||
187 | } | 218 | } |
188 | mutex_unlock(&vgasr_mutex); | 219 | mutex_unlock(&vgasr_mutex); |
189 | return 0; | 220 | return 0; |
@@ -217,15 +248,9 @@ static int vga_switchoff(struct vga_switcheroo_client *client) | |||
217 | /* stage one happens before delay */ | 248 | /* stage one happens before delay */ |
218 | static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) | 249 | static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) |
219 | { | 250 | { |
220 | int i; | 251 | struct vga_switcheroo_client *active; |
221 | struct vga_switcheroo_client *active = NULL; | ||
222 | 252 | ||
223 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 253 | active = find_active_client(&vgasr_priv.clients); |
224 | if (vgasr_priv.clients[i].active == true) { | ||
225 | active = &vgasr_priv.clients[i]; | ||
226 | break; | ||
227 | } | ||
228 | } | ||
229 | if (!active) | 254 | if (!active) |
230 | return 0; | 255 | return 0; |
231 | 256 | ||
@@ -241,15 +266,9 @@ static int vga_switchto_stage1(struct vga_switcheroo_client *new_client) | |||
241 | static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) | 266 | static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) |
242 | { | 267 | { |
243 | int ret; | 268 | int ret; |
244 | int i; | 269 | struct vga_switcheroo_client *active; |
245 | struct vga_switcheroo_client *active = NULL; | ||
246 | 270 | ||
247 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 271 | active = find_active_client(&vgasr_priv.clients); |
248 | if (vgasr_priv.clients[i].active == true) { | ||
249 | active = &vgasr_priv.clients[i]; | ||
250 | break; | ||
251 | } | ||
252 | } | ||
253 | if (!active) | 272 | if (!active) |
254 | return 0; | 273 | return 0; |
255 | 274 | ||
@@ -275,13 +294,26 @@ static int vga_switchto_stage2(struct vga_switcheroo_client *new_client) | |||
275 | return 0; | 294 | return 0; |
276 | } | 295 | } |
277 | 296 | ||
297 | static bool check_can_switch(void) | ||
298 | { | ||
299 | struct vga_switcheroo_client *client; | ||
300 | |||
301 | list_for_each_entry(client, &vgasr_priv.clients, list) { | ||
302 | if (!client->can_switch(client->pdev)) { | ||
303 | printk(KERN_ERR "vga_switcheroo: client %x refused switch\n", client->id); | ||
304 | return false; | ||
305 | } | ||
306 | } | ||
307 | return true; | ||
308 | } | ||
309 | |||
278 | static ssize_t | 310 | static ssize_t |
279 | vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | 311 | vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, |
280 | size_t cnt, loff_t *ppos) | 312 | size_t cnt, loff_t *ppos) |
281 | { | 313 | { |
282 | char usercmd[64]; | 314 | char usercmd[64]; |
283 | const char *pdev_name; | 315 | const char *pdev_name; |
284 | int i, ret; | 316 | int ret; |
285 | bool delay = false, can_switch; | 317 | bool delay = false, can_switch; |
286 | bool just_mux = false; | 318 | bool just_mux = false; |
287 | int client_id = -1; | 319 | int client_id = -1; |
@@ -302,21 +334,21 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | |||
302 | 334 | ||
303 | /* pwr off the device not in use */ | 335 | /* pwr off the device not in use */ |
304 | if (strncmp(usercmd, "OFF", 3) == 0) { | 336 | if (strncmp(usercmd, "OFF", 3) == 0) { |
305 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 337 | list_for_each_entry(client, &vgasr_priv.clients, list) { |
306 | if (vgasr_priv.clients[i].active) | 338 | if (client->active) |
307 | continue; | 339 | continue; |
308 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_ON) | 340 | if (client->pwr_state == VGA_SWITCHEROO_ON) |
309 | vga_switchoff(&vgasr_priv.clients[i]); | 341 | vga_switchoff(client); |
310 | } | 342 | } |
311 | goto out; | 343 | goto out; |
312 | } | 344 | } |
313 | /* pwr on the device not in use */ | 345 | /* pwr on the device not in use */ |
314 | if (strncmp(usercmd, "ON", 2) == 0) { | 346 | if (strncmp(usercmd, "ON", 2) == 0) { |
315 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 347 | list_for_each_entry(client, &vgasr_priv.clients, list) { |
316 | if (vgasr_priv.clients[i].active) | 348 | if (client->active) |
317 | continue; | 349 | continue; |
318 | if (vgasr_priv.clients[i].pwr_state == VGA_SWITCHEROO_OFF) | 350 | if (client->pwr_state == VGA_SWITCHEROO_OFF) |
319 | vga_switchon(&vgasr_priv.clients[i]); | 351 | vga_switchon(client); |
320 | } | 352 | } |
321 | goto out; | 353 | goto out; |
322 | } | 354 | } |
@@ -349,13 +381,9 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | |||
349 | 381 | ||
350 | if (client_id == -1) | 382 | if (client_id == -1) |
351 | goto out; | 383 | goto out; |
352 | 384 | client = find_client_from_id(&vgasr_priv.clients, client_id); | |
353 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 385 | if (!client) |
354 | if (vgasr_priv.clients[i].id == client_id) { | 386 | goto out; |
355 | client = &vgasr_priv.clients[i]; | ||
356 | break; | ||
357 | } | ||
358 | } | ||
359 | 387 | ||
360 | vgasr_priv.delayed_switch_active = false; | 388 | vgasr_priv.delayed_switch_active = false; |
361 | 389 | ||
@@ -364,23 +392,16 @@ vga_switcheroo_debugfs_write(struct file *filp, const char __user *ubuf, | |||
364 | goto out; | 392 | goto out; |
365 | } | 393 | } |
366 | 394 | ||
367 | if (client->active == true) | 395 | if (client->active) |
368 | goto out; | 396 | goto out; |
369 | 397 | ||
370 | /* okay we want a switch - test if devices are willing to switch */ | 398 | /* okay we want a switch - test if devices are willing to switch */ |
371 | can_switch = true; | 399 | can_switch = check_can_switch(); |
372 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | ||
373 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
374 | if (can_switch == false) { | ||
375 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
376 | break; | ||
377 | } | ||
378 | } | ||
379 | 400 | ||
380 | if (can_switch == false && delay == false) | 401 | if (can_switch == false && delay == false) |
381 | goto out; | 402 | goto out; |
382 | 403 | ||
383 | if (can_switch == true) { | 404 | if (can_switch) { |
384 | pdev_name = pci_name(client->pdev); | 405 | pdev_name = pci_name(client->pdev); |
385 | ret = vga_switchto_stage1(client); | 406 | ret = vga_switchto_stage1(client); |
386 | if (ret) | 407 | if (ret) |
@@ -452,10 +473,8 @@ fail: | |||
452 | 473 | ||
453 | int vga_switcheroo_process_delayed_switch(void) | 474 | int vga_switcheroo_process_delayed_switch(void) |
454 | { | 475 | { |
455 | struct vga_switcheroo_client *client = NULL; | 476 | struct vga_switcheroo_client *client; |
456 | const char *pdev_name; | 477 | const char *pdev_name; |
457 | bool can_switch = true; | ||
458 | int i; | ||
459 | int ret; | 478 | int ret; |
460 | int err = -EINVAL; | 479 | int err = -EINVAL; |
461 | 480 | ||
@@ -465,17 +484,9 @@ int vga_switcheroo_process_delayed_switch(void) | |||
465 | 484 | ||
466 | printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); | 485 | printk(KERN_INFO "vga_switcheroo: processing delayed switch to %d\n", vgasr_priv.delayed_client_id); |
467 | 486 | ||
468 | for (i = 0; i < VGA_SWITCHEROO_MAX_CLIENTS; i++) { | 487 | client = find_client_from_id(&vgasr_priv.clients, |
469 | if (vgasr_priv.clients[i].id == vgasr_priv.delayed_client_id) | 488 | vgasr_priv.delayed_client_id); |
470 | client = &vgasr_priv.clients[i]; | 489 | if (!client || !check_can_switch()) |
471 | can_switch = vgasr_priv.clients[i].can_switch(vgasr_priv.clients[i].pdev); | ||
472 | if (can_switch == false) { | ||
473 | printk(KERN_ERR "vga_switcheroo: client %d refused switch\n", i); | ||
474 | break; | ||
475 | } | ||
476 | } | ||
477 | |||
478 | if (can_switch == false || client == NULL) | ||
479 | goto err; | 490 | goto err; |
480 | 491 | ||
481 | pdev_name = pci_name(client->pdev); | 492 | pdev_name = pci_name(client->pdev); |