Commit 4abe35204af82a018ca3ce6db4102aa09719698e
1 parent
5c4426a782
Exists in
master
and in
4 other branches
drm/kms/fb: use slow work mechanism for normal hotplug also.
a) slow work is always used now for any fbcon hotplug, as its not a fast task and is more suited to being ran under slow work. b) attempt to not do any fbdev changes when X is running as we'll just mess it up. This hooks set_par to hopefully do the changes once X hands control to fbdev. This also adds the nouveau/intel hotplug support. Signed-off-by: Dave Airlie <airlied@redhat.com>
Showing 9 changed files with 241 additions and 182 deletions Side-by-side Diff
- drivers/gpu/drm/drm_fb_helper.c
- drivers/gpu/drm/i915/i915_irq.c
- drivers/gpu/drm/i915/intel_drv.h
- drivers/gpu/drm/i915/intel_fb.c
- drivers/gpu/drm/nouveau/nouveau_fbcon.c
- drivers/gpu/drm/nouveau/nouveau_fbcon.h
- drivers/gpu/drm/nouveau/nv50_display.c
- drivers/gpu/drm/radeon/radeon_fb.c
- include/drm/drm_fb_helper.h
drivers/gpu/drm/drm_fb_helper.c
... | ... | @@ -41,6 +41,8 @@ |
41 | 41 | |
42 | 42 | static LIST_HEAD(kernel_fb_helper_list); |
43 | 43 | |
44 | +static struct slow_work_ops output_status_change_ops; | |
45 | + | |
44 | 46 | /* simple single crtc case helper function */ |
45 | 47 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) |
46 | 48 | { |
47 | 49 | |
48 | 50 | |
49 | 51 | |
50 | 52 | |
51 | 53 | |
52 | 54 | |
53 | 55 | |
54 | 56 | |
55 | 57 | |
56 | 58 | |
57 | 59 | |
58 | 60 | |
... | ... | @@ -420,55 +422,82 @@ |
420 | 422 | kfree(helper->crtc_info); |
421 | 423 | } |
422 | 424 | |
423 | -int drm_fb_helper_init_crtc_count(struct drm_device *dev, | |
424 | - struct drm_fb_helper *helper, | |
425 | - int crtc_count, int max_conn_count) | |
425 | +int drm_fb_helper_init(struct drm_device *dev, | |
426 | + struct drm_fb_helper *fb_helper, | |
427 | + int crtc_count, int max_conn_count, | |
428 | + bool polled) | |
426 | 429 | { |
427 | 430 | struct drm_crtc *crtc; |
428 | 431 | int ret = 0; |
429 | 432 | int i; |
430 | 433 | |
431 | - INIT_LIST_HEAD(&helper->kernel_fb_list); | |
432 | - helper->dev = dev; | |
433 | - helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); | |
434 | - if (!helper->crtc_info) | |
434 | + fb_helper->dev = dev; | |
435 | + fb_helper->poll_enabled = polled; | |
436 | + | |
437 | + slow_work_register_user(THIS_MODULE); | |
438 | + delayed_slow_work_init(&fb_helper->output_status_change_slow_work, | |
439 | + &output_status_change_ops); | |
440 | + | |
441 | + INIT_LIST_HEAD(&fb_helper->kernel_fb_list); | |
442 | + | |
443 | + fb_helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); | |
444 | + if (!fb_helper->crtc_info) | |
435 | 445 | return -ENOMEM; |
436 | - helper->crtc_count = crtc_count; | |
437 | 446 | |
438 | - helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); | |
439 | - if (!helper->connector_info) { | |
440 | - kfree(helper->crtc_info); | |
447 | + fb_helper->crtc_count = crtc_count; | |
448 | + fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); | |
449 | + if (!fb_helper->connector_info) { | |
450 | + kfree(fb_helper->crtc_info); | |
441 | 451 | return -ENOMEM; |
442 | 452 | } |
443 | - helper->connector_count = 0; | |
453 | + fb_helper->connector_count = 0; | |
444 | 454 | |
445 | 455 | for (i = 0; i < crtc_count; i++) { |
446 | - helper->crtc_info[i].mode_set.connectors = | |
456 | + fb_helper->crtc_info[i].mode_set.connectors = | |
447 | 457 | kcalloc(max_conn_count, |
448 | 458 | sizeof(struct drm_connector *), |
449 | 459 | GFP_KERNEL); |
450 | 460 | |
451 | - if (!helper->crtc_info[i].mode_set.connectors) { | |
461 | + if (!fb_helper->crtc_info[i].mode_set.connectors) { | |
452 | 462 | ret = -ENOMEM; |
453 | 463 | goto out_free; |
454 | 464 | } |
455 | - helper->crtc_info[i].mode_set.num_connectors = 0; | |
465 | + fb_helper->crtc_info[i].mode_set.num_connectors = 0; | |
456 | 466 | } |
457 | 467 | |
458 | 468 | i = 0; |
459 | 469 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
460 | - helper->crtc_info[i].crtc_id = crtc->base.id; | |
461 | - helper->crtc_info[i].mode_set.crtc = crtc; | |
470 | + fb_helper->crtc_info[i].crtc_id = crtc->base.id; | |
471 | + fb_helper->crtc_info[i].mode_set.crtc = crtc; | |
462 | 472 | i++; |
463 | 473 | } |
464 | - helper->conn_limit = max_conn_count; | |
474 | + fb_helper->conn_limit = max_conn_count; | |
465 | 475 | return 0; |
466 | 476 | out_free: |
467 | - drm_fb_helper_crtc_free(helper); | |
477 | + drm_fb_helper_crtc_free(fb_helper); | |
468 | 478 | return -ENOMEM; |
469 | 479 | } |
470 | -EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); | |
480 | +EXPORT_SYMBOL(drm_fb_helper_init); | |
471 | 481 | |
482 | +void drm_fb_helper_fini(struct drm_fb_helper *fb_helper) | |
483 | +{ | |
484 | + if (!list_empty(&fb_helper->kernel_fb_list)) { | |
485 | + list_del(&fb_helper->kernel_fb_list); | |
486 | + if (list_empty(&kernel_fb_helper_list)) { | |
487 | + printk(KERN_INFO "unregistered panic notifier\n"); | |
488 | + atomic_notifier_chain_unregister(&panic_notifier_list, | |
489 | + &paniced); | |
490 | + unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); | |
491 | + } | |
492 | + } | |
493 | + | |
494 | + drm_fb_helper_crtc_free(fb_helper); | |
495 | + | |
496 | + delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work); | |
497 | + slow_work_unregister_user(THIS_MODULE); | |
498 | +} | |
499 | +EXPORT_SYMBOL(drm_fb_helper_fini); | |
500 | + | |
472 | 501 | static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, |
473 | 502 | u16 blue, u16 regno, struct fb_info *info) |
474 | 503 | { |
... | ... | @@ -710,6 +739,11 @@ |
710 | 739 | } |
711 | 740 | } |
712 | 741 | mutex_unlock(&dev->mode_config.mutex); |
742 | + | |
743 | + if (fb_helper->delayed_hotplug) { | |
744 | + fb_helper->delayed_hotplug = false; | |
745 | + delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0); | |
746 | + } | |
713 | 747 | return 0; |
714 | 748 | } |
715 | 749 | EXPORT_SYMBOL(drm_fb_helper_set_par); |
... | ... | @@ -751,7 +785,7 @@ |
751 | 785 | { |
752 | 786 | int new_fb = 0; |
753 | 787 | int crtc_count = 0; |
754 | - int ret, i; | |
788 | + int i; | |
755 | 789 | struct fb_info *info; |
756 | 790 | struct drm_fb_helper_surface_size sizes; |
757 | 791 | int gamma_size = 0; |
... | ... | @@ -827,7 +861,7 @@ |
827 | 861 | } |
828 | 862 | |
829 | 863 | /* push down into drivers */ |
830 | - new_fb = (*fb_helper->fb_probe)(fb_helper, &sizes); | |
864 | + new_fb = (*fb_helper->funcs->fb_probe)(fb_helper, &sizes); | |
831 | 865 | if (new_fb < 0) |
832 | 866 | return new_fb; |
833 | 867 | |
834 | 868 | |
... | ... | @@ -840,11 +874,7 @@ |
840 | 874 | |
841 | 875 | if (new_fb) { |
842 | 876 | info->var.pixclock = 0; |
843 | - ret = fb_alloc_cmap(&info->cmap, gamma_size, 0); | |
844 | - if (ret) | |
845 | - return ret; | |
846 | 877 | if (register_framebuffer(info) < 0) { |
847 | - fb_dealloc_cmap(&info->cmap); | |
848 | 878 | return -EINVAL; |
849 | 879 | } |
850 | 880 | |
... | ... | @@ -870,23 +900,6 @@ |
870 | 900 | } |
871 | 901 | EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); |
872 | 902 | |
873 | -void drm_fb_helper_free(struct drm_fb_helper *helper) | |
874 | -{ | |
875 | - if (!list_empty(&helper->kernel_fb_list)) { | |
876 | - list_del(&helper->kernel_fb_list); | |
877 | - if (list_empty(&kernel_fb_helper_list)) { | |
878 | - printk(KERN_INFO "unregistered panic notifier\n"); | |
879 | - atomic_notifier_chain_unregister(&panic_notifier_list, | |
880 | - &paniced); | |
881 | - unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op); | |
882 | - } | |
883 | - } | |
884 | - drm_fb_helper_crtc_free(helper); | |
885 | - if (helper->fbdev->cmap.len) | |
886 | - fb_dealloc_cmap(&helper->fbdev->cmap); | |
887 | -} | |
888 | -EXPORT_SYMBOL(drm_fb_helper_free); | |
889 | - | |
890 | 903 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
891 | 904 | uint32_t depth) |
892 | 905 | { |
... | ... | @@ -1291,7 +1304,7 @@ |
1291 | 1304 | * RETURNS: |
1292 | 1305 | * Zero if everything went ok, nonzero otherwise. |
1293 | 1306 | */ |
1294 | -bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) | |
1307 | +bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel) | |
1295 | 1308 | { |
1296 | 1309 | struct drm_device *dev = fb_helper->dev; |
1297 | 1310 | int count = 0; |
1298 | 1311 | |
... | ... | @@ -1304,13 +1317,12 @@ |
1304 | 1317 | count = drm_fb_helper_probe_connector_modes(fb_helper, |
1305 | 1318 | dev->mode_config.max_width, |
1306 | 1319 | dev->mode_config.max_height); |
1307 | - | |
1308 | 1320 | /* |
1309 | 1321 | * we shouldn't end up with no modes here. |
1310 | 1322 | */ |
1311 | 1323 | if (count == 0) { |
1312 | 1324 | if (fb_helper->poll_enabled) { |
1313 | - delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work, | |
1325 | + delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, | |
1314 | 1326 | 5*HZ); |
1315 | 1327 | printk(KERN_INFO "No connectors reported connected with modes - started polling\n"); |
1316 | 1328 | } else |
1317 | 1329 | |
1318 | 1330 | |
1319 | 1331 | |
1320 | 1332 | |
1321 | 1333 | |
1322 | 1334 | |
1323 | 1335 | |
1324 | 1336 | |
1325 | 1337 | |
1326 | 1338 | |
1327 | 1339 | |
1328 | 1340 | |
1329 | 1341 | |
1330 | 1342 | |
1331 | 1343 | |
1332 | 1344 | |
1333 | 1345 | |
1334 | 1346 | |
... | ... | @@ -1318,86 +1330,114 @@ |
1318 | 1330 | } |
1319 | 1331 | drm_setup_crtcs(fb_helper); |
1320 | 1332 | |
1321 | - return 0; | |
1333 | + return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); | |
1322 | 1334 | } |
1323 | 1335 | EXPORT_SYMBOL(drm_fb_helper_initial_config); |
1324 | 1336 | |
1325 | -bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, | |
1326 | - u32 max_width, u32 max_height, bool polled) | |
1337 | +/* we got a hotplug irq - need to update fbcon */ | |
1338 | +void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper) | |
1327 | 1339 | { |
1340 | + /* if we don't have the fbdev registered yet do nothing */ | |
1341 | + if (!fb_helper->fbdev) | |
1342 | + return; | |
1343 | + | |
1344 | + /* schedule a slow work asap */ | |
1345 | + delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 0); | |
1346 | +} | |
1347 | +EXPORT_SYMBOL(drm_helper_fb_hpd_irq_event); | |
1348 | + | |
1349 | +bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, bool polled) | |
1350 | +{ | |
1328 | 1351 | int count = 0; |
1329 | 1352 | int ret; |
1353 | + u32 max_width, max_height, bpp_sel; | |
1354 | + | |
1355 | + if (!fb_helper->fb) | |
1356 | + return false; | |
1330 | 1357 | DRM_DEBUG_KMS("\n"); |
1331 | 1358 | |
1359 | + max_width = fb_helper->fb->width; | |
1360 | + max_height = fb_helper->fb->height; | |
1361 | + bpp_sel = fb_helper->fb->bits_per_pixel; | |
1362 | + | |
1332 | 1363 | count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, |
1333 | 1364 | max_height); |
1334 | 1365 | if (fb_helper->poll_enabled && !polled) { |
1335 | 1366 | if (count) { |
1336 | - delayed_slow_work_cancel(&fb_helper->output_poll_slow_work); | |
1367 | + delayed_slow_work_cancel(&fb_helper->output_status_change_slow_work); | |
1337 | 1368 | } else { |
1338 | - ret = delayed_slow_work_enqueue(&fb_helper->output_poll_slow_work, 5*HZ); | |
1369 | + ret = delayed_slow_work_enqueue(&fb_helper->output_status_change_slow_work, 5*HZ); | |
1339 | 1370 | } |
1340 | 1371 | } |
1341 | 1372 | drm_setup_crtcs(fb_helper); |
1342 | 1373 | |
1343 | - return true; | |
1374 | + return drm_fb_helper_single_fb_probe(fb_helper, bpp_sel); | |
1344 | 1375 | } |
1345 | 1376 | EXPORT_SYMBOL(drm_helper_fb_hotplug_event); |
1346 | 1377 | |
1347 | -static void output_poll_execute(struct slow_work *work) | |
1378 | +/* | |
1379 | + * delayed work queue execution function | |
1380 | + * - check if fbdev is actually in use on the gpu | |
1381 | + * - if not set delayed flag and repoll if necessary | |
1382 | + * - check for connector status change | |
1383 | + * - repoll if 0 modes found | |
1384 | + *- call driver output status changed notifier | |
1385 | + */ | |
1386 | +static void output_status_change_execute(struct slow_work *work) | |
1348 | 1387 | { |
1349 | 1388 | struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work); |
1350 | - struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_poll_slow_work); | |
1351 | - struct drm_device *dev = fb_helper->dev; | |
1389 | + struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_status_change_slow_work); | |
1352 | 1390 | struct drm_connector *connector; |
1353 | 1391 | enum drm_connector_status old_status, status; |
1354 | - bool repoll = true, changed = false; | |
1392 | + bool repoll, changed = false; | |
1355 | 1393 | int ret; |
1394 | + int i; | |
1395 | + bool bound = false, crtcs_bound = false; | |
1396 | + struct drm_crtc *crtc; | |
1356 | 1397 | |
1357 | - list_for_each_entry(connector, &dev->mode_config.connector_list, head) { | |
1398 | + repoll = fb_helper->poll_enabled; | |
1399 | + | |
1400 | + /* first of all check the fbcon framebuffer is actually bound to any crtc */ | |
1401 | + /* take into account that no crtc at all maybe bound */ | |
1402 | + list_for_each_entry(crtc, &fb_helper->dev->mode_config.crtc_list, head) { | |
1403 | + if (crtc->fb) | |
1404 | + crtcs_bound = true; | |
1405 | + if (crtc->fb == fb_helper->fb) | |
1406 | + bound = true; | |
1407 | + } | |
1408 | + | |
1409 | + if (bound == false && crtcs_bound) { | |
1410 | + fb_helper->delayed_hotplug = true; | |
1411 | + goto requeue; | |
1412 | + } | |
1413 | + | |
1414 | + for (i = 0; i < fb_helper->connector_count; i++) { | |
1415 | + connector = fb_helper->connector_info[i]->connector; | |
1358 | 1416 | old_status = connector->status; |
1359 | 1417 | status = connector->funcs->detect(connector); |
1360 | 1418 | if (old_status != status) { |
1361 | 1419 | changed = true; |
1362 | - /* something changed */ | |
1363 | 1420 | } |
1364 | - if (status == connector_status_connected) { | |
1421 | + if (status == connector_status_connected && repoll) { | |
1365 | 1422 | DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector)); |
1366 | 1423 | repoll = false; |
1367 | 1424 | } |
1368 | 1425 | } |
1369 | 1426 | |
1427 | + if (changed) { | |
1428 | + if (fb_helper->funcs->fb_output_status_changed) | |
1429 | + fb_helper->funcs->fb_output_status_changed(fb_helper); | |
1430 | + } | |
1431 | + | |
1432 | +requeue: | |
1370 | 1433 | if (repoll) { |
1371 | 1434 | ret = delayed_slow_work_enqueue(delayed_work, 5*HZ); |
1372 | 1435 | if (ret) |
1373 | 1436 | DRM_ERROR("delayed enqueue failed %d\n", ret); |
1374 | 1437 | } |
1375 | - | |
1376 | - if (changed) { | |
1377 | - if (fb_helper->fb_poll_changed) | |
1378 | - fb_helper->fb_poll_changed(fb_helper); | |
1379 | - } | |
1380 | 1438 | } |
1381 | 1439 | |
1382 | -struct slow_work_ops output_poll_ops = { | |
1383 | - .execute = output_poll_execute, | |
1440 | +static struct slow_work_ops output_status_change_ops = { | |
1441 | + .execute = output_status_change_execute, | |
1384 | 1442 | }; |
1385 | - | |
1386 | -void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper) | |
1387 | -{ | |
1388 | - int ret; | |
1389 | - | |
1390 | - ret = slow_work_register_user(THIS_MODULE); | |
1391 | - | |
1392 | - delayed_slow_work_init(&fb_helper->output_poll_slow_work, &output_poll_ops); | |
1393 | - fb_helper->poll_enabled = true; | |
1394 | -} | |
1395 | -EXPORT_SYMBOL(drm_fb_helper_poll_init); | |
1396 | - | |
1397 | -void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper) | |
1398 | -{ | |
1399 | - delayed_slow_work_cancel(&fb_helper->output_poll_slow_work); | |
1400 | - slow_work_unregister_user(THIS_MODULE); | |
1401 | -} | |
1402 | -EXPORT_SYMBOL(drm_fb_helper_poll_fini); |
drivers/gpu/drm/i915/i915_irq.c
drivers/gpu/drm/i915/intel_drv.h
drivers/gpu/drm/i915/intel_fb.c
... | ... | @@ -65,12 +65,6 @@ |
65 | 65 | .fb_setcmap = drm_fb_helper_setcmap, |
66 | 66 | }; |
67 | 67 | |
68 | -static struct drm_fb_helper_funcs intel_fb_helper_funcs = { | |
69 | - .gamma_set = intel_crtc_fb_gamma_set, | |
70 | - .gamma_get = intel_crtc_fb_gamma_get, | |
71 | -}; | |
72 | - | |
73 | - | |
74 | 68 | static int intelfb_create(struct intel_fbdev *ifbdev, |
75 | 69 | struct drm_fb_helper_surface_size *sizes) |
76 | 70 | { |
... | ... | @@ -129,7 +123,6 @@ |
129 | 123 | |
130 | 124 | ifbdev->helper.fb = fb; |
131 | 125 | ifbdev->helper.fbdev = info; |
132 | - ifbdev->helper.funcs = &intel_fb_helper_funcs; | |
133 | 126 | |
134 | 127 | strcpy(info->fix.id, "inteldrmfb"); |
135 | 128 | |
... | ... | @@ -154,6 +147,12 @@ |
154 | 147 | ret = -ENOSPC; |
155 | 148 | goto out_unpin; |
156 | 149 | } |
150 | + | |
151 | + ret = fb_alloc_cmap(&info->cmap, 256, 0); | |
152 | + if (ret) { | |
153 | + ret = -ENOMEM; | |
154 | + goto out_unpin; | |
155 | + } | |
157 | 156 | info->screen_size = size; |
158 | 157 | |
159 | 158 | // memset(info->screen_base, 0, size); |
160 | 159 | |
161 | 160 | |
... | ... | @@ -205,15 +204,18 @@ |
205 | 204 | return new_fb; |
206 | 205 | } |
207 | 206 | |
208 | -static int intelfb_probe(struct intel_fbdev *ifbdev) | |
207 | +void intelfb_hotplug(struct drm_device *dev, bool polled) | |
209 | 208 | { |
210 | - int ret; | |
211 | - | |
212 | - DRM_DEBUG_KMS("\n"); | |
213 | - ret = drm_fb_helper_single_fb_probe(&ifbdev->helper, 32); | |
214 | - return ret; | |
209 | + drm_i915_private_t *dev_priv = dev->dev_private; | |
210 | + drm_helper_fb_hpd_irq_event(&dev_priv->fbdev->helper); | |
215 | 211 | } |
216 | 212 | |
213 | +static struct drm_fb_helper_funcs intel_fb_helper_funcs = { | |
214 | + .gamma_set = intel_crtc_fb_gamma_set, | |
215 | + .gamma_get = intel_crtc_fb_gamma_get, | |
216 | + .fb_probe = intel_fb_find_or_create_single, | |
217 | +}; | |
218 | + | |
217 | 219 | int intel_fbdev_destroy(struct drm_device *dev, |
218 | 220 | struct intel_fbdev *ifbdev) |
219 | 221 | { |
220 | 222 | |
... | ... | @@ -224,10 +226,12 @@ |
224 | 226 | info = ifbdev->helper.fbdev; |
225 | 227 | unregister_framebuffer(info); |
226 | 228 | iounmap(info->screen_base); |
229 | + if (info->cmap.len) | |
230 | + fb_dealloc_cmap(&info->cmap); | |
227 | 231 | framebuffer_release(info); |
228 | 232 | } |
229 | 233 | |
230 | - drm_fb_helper_free(&ifbdev->helper); | |
234 | + drm_fb_helper_fini(&ifbdev->helper); | |
231 | 235 | |
232 | 236 | drm_framebuffer_cleanup(&ifb->base); |
233 | 237 | if (ifb->obj) |
234 | 238 | |
235 | 239 | |
... | ... | @@ -246,13 +250,13 @@ |
246 | 250 | return -ENOMEM; |
247 | 251 | |
248 | 252 | dev_priv->fbdev = ifbdev; |
253 | + ifbdev->helper.funcs = &intel_fb_helper_funcs; | |
249 | 254 | |
250 | - drm_fb_helper_init_crtc_count(dev, &ifbdev->helper, 2, | |
251 | - INTELFB_CONN_LIMIT); | |
255 | + drm_fb_helper_init(dev, &ifbdev->helper, 2, | |
256 | + INTELFB_CONN_LIMIT, false); | |
257 | + | |
252 | 258 | drm_fb_helper_single_add_all_connectors(&ifbdev->helper); |
253 | - ifbdev->helper.fb_probe = intel_fb_find_or_create_single; | |
254 | - drm_fb_helper_initial_config(&ifbdev->helper); | |
255 | - intelfb_probe(ifbdev); | |
259 | + drm_fb_helper_initial_config(&ifbdev->helper, 32); | |
256 | 260 | return 0; |
257 | 261 | } |
258 | 262 |
drivers/gpu/drm/nouveau/nouveau_fbcon.c
... | ... | @@ -156,11 +156,6 @@ |
156 | 156 | *blue = nv_crtc->lut.b[regno]; |
157 | 157 | } |
158 | 158 | |
159 | -static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { | |
160 | - .gamma_set = nouveau_fbcon_gamma_set, | |
161 | - .gamma_get = nouveau_fbcon_gamma_get | |
162 | -}; | |
163 | - | |
164 | 159 | #if defined(__i386__) || defined(__x86_64__) |
165 | 160 | static bool |
166 | 161 | nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev) |
... | ... | @@ -272,6 +267,12 @@ |
272 | 267 | goto out_unref; |
273 | 268 | } |
274 | 269 | |
270 | + ret = fb_alloc_cmap(&info->cmap, 256, 0); | |
271 | + if (ret) { | |
272 | + ret = -ENOMEM; | |
273 | + goto out_unref; | |
274 | + } | |
275 | + | |
275 | 276 | info->par = nfbdev; |
276 | 277 | |
277 | 278 | nouveau_framebuffer_init(dev, &nfbdev->nouveau_fb, &mode_cmd, nvbo); |
... | ... | @@ -282,7 +283,6 @@ |
282 | 283 | /* setup helper */ |
283 | 284 | nfbdev->helper.fb = fb; |
284 | 285 | nfbdev->helper.fbdev = info; |
285 | - nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs; | |
286 | 286 | |
287 | 287 | strcpy(info->fix.id, "nouveaufb"); |
288 | 288 | if (nouveau_nofbaccel) |
289 | 289 | |
290 | 290 | |
... | ... | @@ -381,12 +381,15 @@ |
381 | 381 | return new_fb; |
382 | 382 | } |
383 | 383 | |
384 | -static int | |
385 | -nouveau_fbcon_probe(struct nouveau_fbdev *nfbdev) | |
384 | +void nouveau_fbcon_hotplug(struct drm_device *dev) | |
386 | 385 | { |
387 | - NV_DEBUG_KMS(nfbdev->dev, "\n"); | |
386 | + struct drm_nouveau_private *dev_priv = dev->dev_private; | |
387 | + drm_helper_fb_hpd_irq_event(&dev_priv->nfbdev->helper); | |
388 | +} | |
388 | 389 | |
389 | - return drm_fb_helper_single_fb_probe(&nfbdev->helper, 32); | |
390 | +static void nouveau_fbcon_output_status_changed(struct drm_fb_helper *fb_helper) | |
391 | +{ | |
392 | + drm_helper_fb_hotplug_event(fb_helper, true); | |
390 | 393 | } |
391 | 394 | |
392 | 395 | int |
... | ... | @@ -398,6 +401,8 @@ |
398 | 401 | if (nfbdev->helper.fbdev) { |
399 | 402 | info = nfbdev->helper.fbdev; |
400 | 403 | unregister_framebuffer(info); |
404 | + if (info->cmap.len) | |
405 | + fb_dealloc_cmap(&info->cmap); | |
401 | 406 | framebuffer_release(info); |
402 | 407 | } |
403 | 408 | |
... | ... | @@ -406,7 +411,7 @@ |
406 | 411 | drm_gem_object_unreference_unlocked(nouveau_fb->nvbo->gem); |
407 | 412 | nouveau_fb->nvbo = NULL; |
408 | 413 | } |
409 | - drm_fb_helper_free(&nfbdev->helper); | |
414 | + drm_fb_helper_fini(&nfbdev->helper); | |
410 | 415 | drm_framebuffer_cleanup(&nouveau_fb->base); |
411 | 416 | return 0; |
412 | 417 | } |
... | ... | @@ -420,6 +425,14 @@ |
420 | 425 | info->flags |= FBINFO_HWACCEL_DISABLED; |
421 | 426 | } |
422 | 427 | |
428 | +static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = { | |
429 | + .gamma_set = nouveau_fbcon_gamma_set, | |
430 | + .gamma_get = nouveau_fbcon_gamma_get, | |
431 | + .fb_probe = nouveau_fbcon_find_or_create_single, | |
432 | + .fb_output_status_changed = nouveau_fbcon_output_status_changed, | |
433 | +}; | |
434 | + | |
435 | + | |
423 | 436 | int nouveau_fbcon_init(struct drm_device *dev) |
424 | 437 | { |
425 | 438 | struct drm_nouveau_private *dev_priv = dev->dev_private; |
426 | 439 | |
427 | 440 | |
... | ... | @@ -431,14 +444,12 @@ |
431 | 444 | |
432 | 445 | nfbdev->dev = dev; |
433 | 446 | dev_priv->nfbdev = nfbdev; |
447 | + nfbdev->helper.funcs = &nouveau_fbcon_helper_funcs; | |
434 | 448 | |
435 | - drm_fb_helper_init_crtc_count(dev, &nfbdev->helper, | |
436 | - 2, 4); | |
437 | - nfbdev->helper.fb_probe = nouveau_fbcon_find_or_create_single; | |
449 | + drm_fb_helper_init(dev, &nfbdev->helper, | |
450 | + 2, 4, true); | |
438 | 451 | drm_fb_helper_single_add_all_connectors(&nfbdev->helper); |
439 | - | |
440 | - drm_fb_helper_initial_config(&nfbdev->helper); | |
441 | - nouveau_fbcon_probe(nfbdev); | |
452 | + drm_fb_helper_initial_config(&nfbdev->helper, 32); | |
442 | 453 | return 0; |
443 | 454 | } |
444 | 455 |
drivers/gpu/drm/nouveau/nouveau_fbcon.h
... | ... | @@ -57,5 +57,7 @@ |
57 | 57 | void nouveau_fbcon_zfill_all(struct drm_device *dev); |
58 | 58 | void nouveau_fbcon_save_disable_accel(struct drm_device *dev); |
59 | 59 | void nouveau_fbcon_restore_accel(struct drm_device *dev); |
60 | + | |
61 | +void nouveau_fbcon_hotplug(struct drm_device *dev); | |
60 | 62 | #endif /* __NV50_FBCON_H__ */ |
drivers/gpu/drm/nouveau/nv50_display.c
... | ... | @@ -29,6 +29,7 @@ |
29 | 29 | #include "nouveau_encoder.h" |
30 | 30 | #include "nouveau_connector.h" |
31 | 31 | #include "nouveau_fb.h" |
32 | +#include "nouveau_fbcon.h" | |
32 | 33 | #include "drm_crtc_helper.h" |
33 | 34 | |
34 | 35 | static void |
... | ... | @@ -941,6 +942,8 @@ |
941 | 942 | nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054)); |
942 | 943 | if (dev_priv->chipset >= 0x90) |
943 | 944 | nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074)); |
945 | + | |
946 | + nouveau_fbcon_hotplug(dev); | |
944 | 947 | } |
945 | 948 | |
946 | 949 | void |
drivers/gpu/drm/radeon/radeon_fb.c
... | ... | @@ -86,11 +86,6 @@ |
86 | 86 | return aligned; |
87 | 87 | } |
88 | 88 | |
89 | -static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { | |
90 | - .gamma_set = radeon_crtc_fb_gamma_set, | |
91 | - .gamma_get = radeon_crtc_fb_gamma_get, | |
92 | -}; | |
93 | - | |
94 | 89 | static void radeonfb_destroy_pinned_object(struct drm_gem_object *gobj) |
95 | 90 | { |
96 | 91 | struct radeon_bo *rbo = gobj->driver_private; |
... | ... | @@ -222,7 +217,6 @@ |
222 | 217 | /* setup helper */ |
223 | 218 | rfbdev->helper.fb = fb; |
224 | 219 | rfbdev->helper.fbdev = info; |
225 | - rfbdev->helper.funcs = &radeon_fb_helper_funcs; | |
226 | 220 | |
227 | 221 | memset_io(rbo->kptr, 0x0, radeon_bo_size(rbo)); |
228 | 222 | |
229 | 223 | |
... | ... | @@ -252,10 +246,18 @@ |
252 | 246 | info->pixmap.access_align = 32; |
253 | 247 | info->pixmap.flags = FB_PIXMAP_SYSTEM; |
254 | 248 | info->pixmap.scan_align = 1; |
249 | + | |
255 | 250 | if (info->screen_base == NULL) { |
256 | 251 | ret = -ENOSPC; |
257 | 252 | goto out_unref; |
258 | 253 | } |
254 | + | |
255 | + ret = fb_alloc_cmap(&info->cmap, 256, 0); | |
256 | + if (ret) { | |
257 | + ret = -ENOMEM; | |
258 | + goto out_unref; | |
259 | + } | |
260 | + | |
259 | 261 | DRM_INFO("fb mappable at 0x%lX\n", info->fix.smem_start); |
260 | 262 | DRM_INFO("vram apper at 0x%lX\n", (unsigned long)rdev->mc.aper_base); |
261 | 263 | DRM_INFO("size %lu\n", (unsigned long)radeon_bo_size(rbo)); |
262 | 264 | |
263 | 265 | |
264 | 266 | |
265 | 267 | |
... | ... | @@ -309,33 +311,16 @@ |
309 | 311 | return 0; |
310 | 312 | } |
311 | 313 | |
312 | -static int radeonfb_probe(struct radeon_fbdev *rfbdev) | |
313 | -{ | |
314 | - struct radeon_device *rdev = rfbdev->rdev; | |
315 | - int bpp_sel = 32; | |
316 | - | |
317 | - /* select 8 bpp console on RN50 or 16MB cards */ | |
318 | - if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) | |
319 | - bpp_sel = 8; | |
320 | - | |
321 | - return drm_fb_helper_single_fb_probe(&rfbdev->helper, bpp_sel); | |
322 | -} | |
323 | - | |
324 | 314 | void radeonfb_hotplug(struct drm_device *dev, bool polled) |
325 | 315 | { |
326 | 316 | struct radeon_device *rdev = dev->dev_private; |
327 | - int max_width, max_height; | |
328 | 317 | |
329 | - max_width = rdev->mode_info.rfbdev->rfb.base.width; | |
330 | - max_height = rdev->mode_info.rfbdev->rfb.base.height; | |
331 | - drm_helper_fb_hotplug_event(&rdev->mode_info.rfbdev->helper, max_width, max_height, polled); | |
332 | - | |
333 | - radeonfb_probe(rdev->mode_info.rfbdev); | |
318 | + drm_helper_fb_hpd_irq_event(&rdev->mode_info.rfbdev->helper); | |
334 | 319 | } |
335 | 320 | |
336 | -static void radeon_fb_poll_changed(struct drm_fb_helper *fb_helper) | |
321 | +static void radeon_fb_output_status_changed(struct drm_fb_helper *fb_helper) | |
337 | 322 | { |
338 | - radeonfb_hotplug(fb_helper->dev, true); | |
323 | + drm_helper_fb_hotplug_event(fb_helper, true); | |
339 | 324 | } |
340 | 325 | |
341 | 326 | static int radeon_fbdev_destroy(struct drm_device *dev, struct radeon_fbdev *rfbdev) |
342 | 327 | |
... | ... | @@ -347,7 +332,10 @@ |
347 | 332 | |
348 | 333 | if (rfbdev->helper.fbdev) { |
349 | 334 | info = rfbdev->helper.fbdev; |
335 | + | |
350 | 336 | unregister_framebuffer(info); |
337 | + if (info->cmap.len) | |
338 | + fb_dealloc_cmap(&info->cmap); | |
351 | 339 | framebuffer_release(info); |
352 | 340 | } |
353 | 341 | |
354 | 342 | |
355 | 343 | |
356 | 344 | |
357 | 345 | |
358 | 346 | |
359 | 347 | |
360 | 348 | |
... | ... | @@ -361,37 +349,41 @@ |
361 | 349 | } |
362 | 350 | drm_gem_object_unreference_unlocked(rfb->obj); |
363 | 351 | } |
364 | - drm_fb_helper_free(&rfbdev->helper); | |
352 | + drm_fb_helper_fini(&rfbdev->helper); | |
365 | 353 | drm_framebuffer_cleanup(&rfb->base); |
366 | 354 | |
367 | 355 | return 0; |
368 | 356 | } |
369 | -MODULE_LICENSE("GPL"); | |
370 | 357 | |
358 | +static struct drm_fb_helper_funcs radeon_fb_helper_funcs = { | |
359 | + .gamma_set = radeon_crtc_fb_gamma_set, | |
360 | + .gamma_get = radeon_crtc_fb_gamma_get, | |
361 | + .fb_probe = radeon_fb_find_or_create_single, | |
362 | + .fb_output_status_changed = radeon_fb_output_status_changed, | |
363 | +}; | |
364 | + | |
371 | 365 | int radeon_fbdev_init(struct radeon_device *rdev) |
372 | 366 | { |
373 | 367 | struct radeon_fbdev *rfbdev; |
368 | + int bpp_sel = 32; | |
374 | 369 | |
370 | + /* select 8 bpp console on RN50 or 16MB cards */ | |
371 | + if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024)) | |
372 | + bpp_sel = 8; | |
373 | + | |
375 | 374 | rfbdev = kzalloc(sizeof(struct radeon_fbdev), GFP_KERNEL); |
376 | 375 | if (!rfbdev) |
377 | 376 | return -ENOMEM; |
378 | 377 | |
379 | 378 | rfbdev->rdev = rdev; |
380 | 379 | rdev->mode_info.rfbdev = rfbdev; |
380 | + rfbdev->helper.funcs = &radeon_fb_helper_funcs; | |
381 | 381 | |
382 | - drm_fb_helper_init_crtc_count(rdev->ddev, &rfbdev->helper, | |
383 | - rdev->num_crtc, | |
384 | - RADEONFB_CONN_LIMIT); | |
385 | - rfbdev->helper.fb_probe = radeon_fb_find_or_create_single; | |
386 | - | |
382 | + drm_fb_helper_init(rdev->ddev, &rfbdev->helper, | |
383 | + rdev->num_crtc, | |
384 | + RADEONFB_CONN_LIMIT, true); | |
387 | 385 | drm_fb_helper_single_add_all_connectors(&rfbdev->helper); |
388 | - | |
389 | - rfbdev->helper.fb_poll_changed = radeon_fb_poll_changed; | |
390 | - drm_fb_helper_poll_init(&rfbdev->helper); | |
391 | - | |
392 | - drm_fb_helper_initial_config(&rfbdev->helper); | |
393 | - radeonfb_probe(rfbdev); | |
394 | - | |
386 | + drm_fb_helper_initial_config(&rfbdev->helper, bpp_sel); | |
395 | 387 | return 0; |
396 | 388 | |
397 | 389 | } |
... | ... | @@ -401,7 +393,6 @@ |
401 | 393 | if (!rdev->mode_info.rfbdev) |
402 | 394 | return; |
403 | 395 | |
404 | - drm_fb_helper_poll_fini(&rdev->mode_info.rfbdev->helper); | |
405 | 396 | radeon_fbdev_destroy(rdev->ddev, rdev->mode_info.rfbdev); |
406 | 397 | kfree(rdev->mode_info.rfbdev); |
407 | 398 | rdev->mode_info.rfbdev = NULL; |
include/drm/drm_fb_helper.h
... | ... | @@ -32,20 +32,14 @@ |
32 | 32 | |
33 | 33 | #include <linux/slow-work.h> |
34 | 34 | |
35 | +struct drm_fb_helper; | |
36 | + | |
35 | 37 | struct drm_fb_helper_crtc { |
36 | 38 | uint32_t crtc_id; |
37 | 39 | struct drm_mode_set mode_set; |
38 | 40 | struct drm_display_mode *desired_mode; |
39 | 41 | }; |
40 | 42 | |
41 | - | |
42 | -struct drm_fb_helper_funcs { | |
43 | - void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, | |
44 | - u16 blue, int regno); | |
45 | - void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green, | |
46 | - u16 *blue, int regno); | |
47 | -}; | |
48 | - | |
49 | 43 | /* mode specified on the command line */ |
50 | 44 | struct drm_fb_helper_cmdline_mode { |
51 | 45 | bool specified; |
... | ... | @@ -69,6 +63,19 @@ |
69 | 63 | u32 surface_depth; |
70 | 64 | }; |
71 | 65 | |
66 | +struct drm_fb_helper_funcs { | |
67 | + void (*gamma_set)(struct drm_crtc *crtc, u16 red, u16 green, | |
68 | + u16 blue, int regno); | |
69 | + void (*gamma_get)(struct drm_crtc *crtc, u16 *red, u16 *green, | |
70 | + u16 *blue, int regno); | |
71 | + | |
72 | + int (*fb_probe)(struct drm_fb_helper *helper, | |
73 | + struct drm_fb_helper_surface_size *sizes); | |
74 | + | |
75 | + void (*fb_output_status_changed)(struct drm_fb_helper *helper); | |
76 | + | |
77 | +}; | |
78 | + | |
72 | 79 | struct drm_fb_helper_connector { |
73 | 80 | struct drm_fb_helper_cmdline_mode cmdline_mode; |
74 | 81 | struct drm_connector *connector; |
75 | 82 | |
76 | 83 | |
... | ... | @@ -88,21 +95,20 @@ |
88 | 95 | u32 pseudo_palette[17]; |
89 | 96 | struct list_head kernel_fb_list; |
90 | 97 | |
91 | - struct delayed_slow_work output_poll_slow_work; | |
98 | + struct delayed_slow_work output_status_change_slow_work; | |
92 | 99 | bool poll_enabled; |
93 | - int (*fb_probe)(struct drm_fb_helper *helper, | |
94 | - struct drm_fb_helper_surface_size *sizes); | |
95 | - | |
96 | - void (*fb_poll_changed)(struct drm_fb_helper *helper); | |
100 | + /* we got a hotplug but fbdev wasn't running the console | |
101 | + delay until next set_par */ | |
102 | + bool delayed_hotplug; | |
97 | 103 | }; |
98 | 104 | |
99 | 105 | int drm_fb_helper_single_fb_probe(struct drm_fb_helper *helper, |
100 | 106 | int preferred_bpp); |
101 | 107 | |
102 | -int drm_fb_helper_init_crtc_count(struct drm_device *dev, | |
103 | - struct drm_fb_helper *helper, int crtc_count, | |
104 | - int max_conn); | |
105 | -void drm_fb_helper_free(struct drm_fb_helper *helper); | |
108 | +int drm_fb_helper_init(struct drm_device *dev, | |
109 | + struct drm_fb_helper *helper, int crtc_count, | |
110 | + int max_conn, bool polled); | |
111 | +void drm_fb_helper_fini(struct drm_fb_helper *helper); | |
106 | 112 | int drm_fb_helper_blank(int blank, struct fb_info *info); |
107 | 113 | int drm_fb_helper_pan_display(struct fb_var_screeninfo *var, |
108 | 114 | struct fb_info *info); |
109 | 115 | |
... | ... | @@ -125,11 +131,10 @@ |
125 | 131 | int drm_fb_helper_setcmap(struct fb_cmap *cmap, struct fb_info *info); |
126 | 132 | |
127 | 133 | bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, |
128 | - u32 max_width, u32 max_height, bool polled); | |
129 | -bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper); | |
134 | + bool polled); | |
135 | +bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper, int bpp_sel); | |
130 | 136 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper); |
131 | 137 | |
132 | -void drm_fb_helper_poll_init(struct drm_fb_helper *fb_helper); | |
133 | -void drm_fb_helper_poll_fini(struct drm_fb_helper *fb_helper); | |
138 | +void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper); | |
134 | 139 | #endif |