aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/drm_fb_helper.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r--drivers/gpu/drm/drm_fb_helper.c207
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
42static LIST_HEAD(kernel_fb_helper_list); 42static LIST_HEAD(kernel_fb_helper_list);
43 43
44static struct slow_work_ops output_status_change_ops;
45
44/* simple single crtc case helper function */ 46/* simple single crtc case helper function */
45int drm_fb_helper_single_add_all_connectors(struct drm_fb_helper *fb_helper) 47int 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
423int drm_fb_helper_init_crtc_count(struct drm_device *dev, 425int 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;
466out_free: 476out_free:
467 drm_fb_helper_crtc_free(helper); 477 drm_fb_helper_crtc_free(fb_helper);
468 return -ENOMEM; 478 return -ENOMEM;
469} 479}
470EXPORT_SYMBOL(drm_fb_helper_init_crtc_count); 480EXPORT_SYMBOL(drm_fb_helper_init);
481
482void 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}
499EXPORT_SYMBOL(drm_fb_helper_fini);
471 500
472static int setcolreg(struct drm_crtc *crtc, u16 red, u16 green, 501static 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}
715EXPORT_SYMBOL(drm_fb_helper_set_par); 749EXPORT_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}
871EXPORT_SYMBOL(drm_fb_helper_single_fb_probe); 901EXPORT_SYMBOL(drm_fb_helper_single_fb_probe);
872 902
873void 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}
888EXPORT_SYMBOL(drm_fb_helper_free);
889
890void drm_fb_helper_fill_fix(struct fb_info *info, uint32_t pitch, 903void 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 */
1294bool drm_fb_helper_initial_config(struct drm_fb_helper *fb_helper) 1307bool 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}
1323EXPORT_SYMBOL(drm_fb_helper_initial_config); 1335EXPORT_SYMBOL(drm_fb_helper_initial_config);
1324 1336
1325bool 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) 1338void 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}
1347EXPORT_SYMBOL(drm_helper_fb_hpd_irq_event);
1348
1349bool 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}
1345EXPORT_SYMBOL(drm_helper_fb_hotplug_event); 1376EXPORT_SYMBOL(drm_helper_fb_hotplug_event);
1346 1377
1347static 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 */
1386static 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
1432requeue:
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
1382struct slow_work_ops output_poll_ops = { 1440static struct slow_work_ops output_status_change_ops = {
1383 .execute = output_poll_execute, 1441 .execute = output_status_change_execute,
1384}; 1442};
1385 1443
1386void 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}
1395EXPORT_SYMBOL(drm_fb_helper_poll_init);
1396
1397void 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}
1402EXPORT_SYMBOL(drm_fb_helper_poll_fini);