diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 207 |
1 files changed, 124 insertions, 83 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c index 3312092f2c7e..b889eb0aaf5f 100644 --- a/drivers/gpu/drm/drm_fb_helper.c +++ b/drivers/gpu/drm/drm_fb_helper.c | |||
@@ -41,6 +41,8 @@ MODULE_LICENSE("GPL and additional rights"); | |||
41 | 41 | ||
42 | static LIST_HEAD(kernel_fb_helper_list); | 42 | static LIST_HEAD(kernel_fb_helper_list); |
43 | 43 | ||
44 | static struct slow_work_ops output_status_change_ops; | ||
45 | |||
44 | /* simple single crtc case helper function */ | 46 | /* simple single crtc case helper function */ |
45 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) | 47 | int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) |
46 | { | 48 | { |
@@ -420,54 +422,81 @@ static void drm_fb_helper_crtc_free(struct drm_fb_helper *helper) | |||
420 | kfree(helper->crtc_info); | 422 | kfree(helper->crtc_info); |
421 | } | 423 | } |
422 | 424 | ||
423 | int drm_fb_helper_init_crtc_count(struct drm_device *dev, | 425 | int drm_fb_helper_init(struct drm_device *dev, |
424 | struct drm_fb_helper *helper, | 426 | struct drm_fb_helper *fb_helper, |
425 | int crtc_count, int max_conn_count) | 427 | int crtc_count, int max_conn_count, |
428 | bool polled) | ||
426 | { | 429 | { |
427 | struct drm_crtc *crtc; | 430 | struct drm_crtc *crtc; |
428 | int ret = 0; | 431 | int ret = 0; |
429 | int i; | 432 | int i; |
430 | 433 | ||
431 | INIT_LIST_HEAD(&helper->kernel_fb_list); | 434 | fb_helper->dev = dev; |
432 | helper->dev = dev; | 435 | fb_helper->poll_enabled = polled; |
433 | helper->crtc_info = kcalloc(crtc_count, sizeof(struct drm_fb_helper_crtc), GFP_KERNEL); | 436 | |
434 | if (!helper->crtc_info) | 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 | return -ENOMEM; | 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); | 447 | fb_helper->crtc_count = crtc_count; |
439 | if (!helper->connector_info) { | 448 | fb_helper->connector_info = kcalloc(dev->mode_config.num_connector, sizeof(struct drm_fb_helper_connector *), GFP_KERNEL); |
440 | kfree(helper->crtc_info); | 449 | if (!fb_helper->connector_info) { |
450 | kfree(fb_helper->crtc_info); | ||
441 | return -ENOMEM; | 451 | return -ENOMEM; |
442 | } | 452 | } |
443 | helper->connector_count = 0; | 453 | fb_helper->connector_count = 0; |
444 | 454 | ||
445 | for (i = 0; i < crtc_count; i++) { | 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 | kcalloc(max_conn_count, | 457 | kcalloc(max_conn_count, |
448 | sizeof(struct drm_connector *), | 458 | sizeof(struct drm_connector *), |
449 | GFP_KERNEL); | 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 | ret = -ENOMEM; | 462 | ret = -ENOMEM; |
453 | goto out_free; | 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 | i = 0; | 468 | i = 0; |
459 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { | 469 | list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { |
460 | helper->crtc_info[i].crtc_id = crtc->base.id; | 470 | fb_helper->crtc_info[i].crtc_id = crtc->base.id; |
461 | helper->crtc_info[i].mode_set.crtc = crtc; | 471 | fb_helper->crtc_info[i].mode_set.crtc = crtc; |
462 | i++; | 472 | i++; |
463 | } | 473 | } |
464 | helper->conn_limit = max_conn_count; | 474 | fb_helper->conn_limit = max_conn_count; |
465 | return 0; | 475 | return 0; |
466 | out_free: | 476 | out_free: |
467 | drm_fb_helper_crtc_free(helper); | 477 | drm_fb_helper_crtc_free(fb_helper); |
468 | return -ENOMEM; | 478 | return -ENOMEM; |
469 | } | 479 | } |
470 | EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); | 480 | EXPORT_SYMBOL(drm_fb_helper_init); |
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); | ||
471 | 500 | ||
472 | static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, | 501 | static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, |
473 | u16 blue, u16 regno, struct fb_info *info) | 502 | u16 blue, u16 regno, struct fb_info *info) |
@@ -710,6 +739,11 @@ int drm_fb_helper_set_par(struct fb_info *info) | |||
710 | } | 739 | } |
711 | } | 740 | } |
712 | mutex_unlock(&dev->mode_config.mutex); | 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 | return 0; | 747 | return 0; |
714 | } | 748 | } |
715 | EXPORT_SYMBOL(drm_fb_helper_set_par); | 749 | EXPORT_SYMBOL(drm_fb_helper_set_par); |
@@ -751,7 +785,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
751 | { | 785 | { |
752 | int new_fb = 0; | 786 | int new_fb = 0; |
753 | int crtc_count = 0; | 787 | int crtc_count = 0; |
754 | int ret, i; | 788 | int i; |
755 | struct fb_info *info; | 789 | struct fb_info *info; |
756 | struct drm_fb_helper_surface_size sizes; | 790 | struct drm_fb_helper_surface_size sizes; |
757 | int gamma_size = 0; | 791 | int gamma_size = 0; |
@@ -827,7 +861,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
827 | } | 861 | } |
828 | 862 | ||
829 | /* push down into drivers */ | 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 | if (new_fb < 0) | 865 | if (new_fb < 0) |
832 | return new_fb; | 866 | return new_fb; |
833 | 867 | ||
@@ -840,11 +874,7 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
840 | 874 | ||
841 | if (new_fb) { | 875 | if (new_fb) { |
842 | info->var.pixclock = 0; | 876 | info->var.pixclock = 0; |
843 | ret = fb_alloc_cmap(&info->cmap, gamma_size, 0); | ||
844 | if (ret) | ||
845 | return ret; | ||
846 | if (register_framebuffer(info) < 0) { | 877 | if (register_framebuffer(info) < 0) { |
847 | fb_dealloc_cmap(&info->cmap); | ||
848 | return -EINVAL; | 878 | return -EINVAL; |
849 | } | 879 | } |
850 | 880 | ||
@@ -870,23 +900,6 @@ int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper, | |||
870 | } | 900 | } |
871 | EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); | 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 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, | 903 | void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, |
891 | uint32_t depth) | 904 | uint32_t depth) |
892 | { | 905 | { |
@@ -1291,7 +1304,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper) | |||
1291 | * RETURNS: | 1304 | * RETURNS: |
1292 | * Zero if everything went ok, nonzero otherwise. | 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 | struct drm_device *dev = fb_helper->dev; | 1309 | struct drm_device *dev = fb_helper->dev; |
1297 | int count = 0; | 1310 | int count = 0; |
@@ -1304,13 +1317,12 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) | |||
1304 | count = drm_fb_helper_probe_connector_modes(fb_helper, | 1317 | count = drm_fb_helper_probe_connector_modes(fb_helper, |
1305 | dev->mode_config.max_width, | 1318 | dev->mode_config.max_width, |
1306 | dev->mode_config.max_height); | 1319 | dev->mode_config.max_height); |
1307 | |||
1308 | /* | 1320 | /* |
1309 | * we shouldn't end up with no modes here. | 1321 | * we shouldn't end up with no modes here. |
1310 | */ | 1322 | */ |
1311 | if (count == 0) { | 1323 | if (count == 0) { |
1312 | if (fb_helper->poll_enabled) { | 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 | 5*HZ); | 1326 | 5*HZ); |
1315 | printk(KERN_INFO "No connectors reported connected with modes - started polling\n"); | 1327 | printk(KERN_INFO "No connectors reported connected with modes - started polling\n"); |
1316 | } else | 1328 | } else |
@@ -1318,85 +1330,114 @@ bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) | |||
1318 | } | 1330 | } |
1319 | drm_setup_crtcs(fb_helper); | 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 | EXPORT_SYMBOL(drm_fb_helper_initial_config); | 1335 | EXPORT_SYMBOL(drm_fb_helper_initial_config); |
1324 | 1336 | ||
1325 | bool drm_helper_fb_hotplug_event(struct drm_fb_helper *fb_helper, | 1337 | /* we got a hotplug irq - need to update fbcon */ |
1326 | u32 max_width, u32 max_height, bool polled) | 1338 | void drm_helper_fb_hpd_irq_event(struct drm_fb_helper *fb_helper) |
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) | ||
1327 | { | 1350 | { |
1328 | int count = 0; | 1351 | int count = 0; |
1329 | int ret; | 1352 | int ret; |
1353 | u32 max_width, max_height, bpp_sel; | ||
1354 | |||
1355 | if (!fb_helper->fb) | ||
1356 | return false; | ||
1330 | DRM_DEBUG_KMS("\n"); | 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 | count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, | 1363 | count = drm_fb_helper_probe_connector_modes(fb_helper, max_width, |
1333 | max_height); | 1364 | max_height); |
1334 | if (fb_helper->poll_enabled && !polled) { | 1365 | if (fb_helper->poll_enabled && !polled) { |
1335 | if (count) { | 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 | } else { | 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 | drm_setup_crtcs(fb_helper); | 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 | EXPORT_SYMBOL(drm_helper_fb_hotplug_event); | 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 | struct delayed_slow_work *delayed_work = container_of(work, struct delayed_slow_work, work); | 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); | 1389 | struct drm_fb_helper *fb_helper = container_of(delayed_work, struct drm_fb_helper, output_status_change_slow_work); |
1351 | struct drm_device *dev = fb_helper->dev; | ||
1352 | struct drm_connector *connector; | 1390 | struct drm_connector *connector; |
1353 | enum drm_connector_status old_status, status; | 1391 | enum drm_connector_status old_status, status; |
1354 | bool repoll = true, changed = false; | 1392 | bool repoll, changed = false; |
1355 | int ret; | 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 | old_status = connector->status; | 1416 | old_status = connector->status; |
1359 | status = connector->funcs->detect(connector); | 1417 | status = connector->funcs->detect(connector); |
1360 | if (old_status != status) { | 1418 | if (old_status != status) { |
1361 | changed = true; | 1419 | changed = true; |
1362 | /* something changed */ | ||
1363 | } | 1420 | } |
1364 | if (status == connector_status_connected) { | 1421 | if (status == connector_status_connected && repoll) { |
1365 | DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector)); | 1422 | DRM_DEBUG("%s is connected - stop polling\n", drm_get_connector_name(connector)); |
1366 | repoll = false; | 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 | if (repoll) { | 1433 | if (repoll) { |
1371 | ret = delayed_slow_work_enqueue(delayed_work, 5*HZ); | 1434 | ret = delayed_slow_work_enqueue(delayed_work, 5*HZ); |
1372 | if (ret) | 1435 | if (ret) |
1373 | DRM_ERROR("delayed enqueue failed %d\n", ret); | 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 = { | 1440 | static struct slow_work_ops output_status_change_ops = { |
1383 | .execute = output_poll_execute, | 1441 | .execute = output_status_change_execute, |
1384 | }; | 1442 | }; |
1385 | 1443 | ||
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); | ||