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.c134
1 files changed, 110 insertions, 24 deletions
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 09d47e9ba026..52ce26d6b4fb 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -347,9 +347,18 @@ bool drm_fb_helper_restore_fbdev_mode_unlocked(struct drm_fb_helper *fb_helper)
347{ 347{
348 struct drm_device *dev = fb_helper->dev; 348 struct drm_device *dev = fb_helper->dev;
349 bool ret; 349 bool ret;
350 bool do_delayed = false;
351
350 drm_modeset_lock_all(dev); 352 drm_modeset_lock_all(dev);
351 ret = restore_fbdev_mode(fb_helper); 353 ret = restore_fbdev_mode(fb_helper);
354
355 do_delayed = fb_helper->delayed_hotplug;
356 if (do_delayed)
357 fb_helper->delayed_hotplug = false;
352 drm_modeset_unlock_all(dev); 358 drm_modeset_unlock_all(dev);
359
360 if (do_delayed)
361 drm_fb_helper_hotplug_event(fb_helper);
353 return ret; 362 return ret;
354} 363}
355EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); 364EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked);
@@ -888,10 +897,6 @@ int drm_fb_helper_set_par(struct fb_info *info)
888 897
889 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper); 898 drm_fb_helper_restore_fbdev_mode_unlocked(fb_helper);
890 899
891 if (fb_helper->delayed_hotplug) {
892 fb_helper->delayed_hotplug = false;
893 drm_fb_helper_hotplug_event(fb_helper);
894 }
895 return 0; 900 return 0;
896} 901}
897EXPORT_SYMBOL(drm_fb_helper_set_par); 902EXPORT_SYMBOL(drm_fb_helper_set_par);
@@ -995,19 +1000,21 @@ static int drm_fb_helper_single_fb_probe(struct drm_fb_helper *fb_helper,
995 crtc_count = 0; 1000 crtc_count = 0;
996 for (i = 0; i < fb_helper->crtc_count; i++) { 1001 for (i = 0; i < fb_helper->crtc_count; i++) {
997 struct drm_display_mode *desired_mode; 1002 struct drm_display_mode *desired_mode;
1003 int x, y;
998 desired_mode = fb_helper->crtc_info[i].desired_mode; 1004 desired_mode = fb_helper->crtc_info[i].desired_mode;
999 1005 x = fb_helper->crtc_info[i].x;
1006 y = fb_helper->crtc_info[i].y;
1000 if (desired_mode) { 1007 if (desired_mode) {
1001 if (gamma_size == 0) 1008 if (gamma_size == 0)
1002 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; 1009 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size;
1003 if (desired_mode->hdisplay < sizes.fb_width) 1010 if (desired_mode->hdisplay + x < sizes.fb_width)
1004 sizes.fb_width = desired_mode->hdisplay; 1011 sizes.fb_width = desired_mode->hdisplay + x;
1005 if (desired_mode->vdisplay < sizes.fb_height) 1012 if (desired_mode->vdisplay + y < sizes.fb_height)
1006 sizes.fb_height = desired_mode->vdisplay; 1013 sizes.fb_height = desired_mode->vdisplay + y;
1007 if (desired_mode->hdisplay > sizes.surface_width) 1014 if (desired_mode->hdisplay + x > sizes.surface_width)
1008 sizes.surface_width = desired_mode->hdisplay; 1015 sizes.surface_width = desired_mode->hdisplay + x;
1009 if (desired_mode->vdisplay > sizes.surface_height) 1016 if (desired_mode->vdisplay + y > sizes.surface_height)
1010 sizes.surface_height = desired_mode->vdisplay; 1017 sizes.surface_height = desired_mode->vdisplay + y;
1011 crtc_count++; 1018 crtc_count++;
1012 } 1019 }
1013 } 1020 }
@@ -1307,6 +1314,7 @@ static void drm_enable_connectors(struct drm_fb_helper *fb_helper,
1307 1314
1308static bool drm_target_cloned(struct drm_fb_helper *fb_helper, 1315static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1309 struct drm_display_mode **modes, 1316 struct drm_display_mode **modes,
1317 struct drm_fb_offset *offsets,
1310 bool *enabled, int width, int height) 1318 bool *enabled, int width, int height)
1311{ 1319{
1312 int count, i, j; 1320 int count, i, j;
@@ -1378,27 +1386,88 @@ static bool drm_target_cloned(struct drm_fb_helper *fb_helper,
1378 return false; 1386 return false;
1379} 1387}
1380 1388
1389static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper,
1390 struct drm_display_mode **modes,
1391 struct drm_fb_offset *offsets,
1392 int idx,
1393 int h_idx, int v_idx)
1394{
1395 struct drm_fb_helper_connector *fb_helper_conn;
1396 int i;
1397 int hoffset = 0, voffset = 0;
1398
1399 for (i = 0; i < fb_helper->connector_count; i++) {
1400 fb_helper_conn = fb_helper->connector_info[i];
1401 if (!fb_helper_conn->connector->has_tile)
1402 continue;
1403
1404 if (!modes[i] && (h_idx || v_idx)) {
1405 DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i,
1406 fb_helper_conn->connector->base.id);
1407 continue;
1408 }
1409 if (fb_helper_conn->connector->tile_h_loc < h_idx)
1410 hoffset += modes[i]->hdisplay;
1411
1412 if (fb_helper_conn->connector->tile_v_loc < v_idx)
1413 voffset += modes[i]->vdisplay;
1414 }
1415 offsets[idx].x = hoffset;
1416 offsets[idx].y = voffset;
1417 DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
1418 return 0;
1419}
1420
1381static bool drm_target_preferred(struct drm_fb_helper *fb_helper, 1421static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1382 struct drm_display_mode **modes, 1422 struct drm_display_mode **modes,
1423 struct drm_fb_offset *offsets,
1383 bool *enabled, int width, int height) 1424 bool *enabled, int width, int height)
1384{ 1425{
1385 struct drm_fb_helper_connector *fb_helper_conn; 1426 struct drm_fb_helper_connector *fb_helper_conn;
1386 int i; 1427 int i;
1387 1428 uint64_t conn_configured = 0, mask;
1429 int tile_pass = 0;
1430 mask = (1 << fb_helper->connector_count) - 1;
1431retry:
1388 for (i = 0; i < fb_helper->connector_count; i++) { 1432 for (i = 0; i < fb_helper->connector_count; i++) {
1389 fb_helper_conn = fb_helper->connector_info[i]; 1433 fb_helper_conn = fb_helper->connector_info[i];
1390 1434
1391 if (enabled[i] == false) 1435 if (conn_configured & (1 << i))
1392 continue; 1436 continue;
1393 1437
1438 if (enabled[i] == false) {
1439 conn_configured |= (1 << i);
1440 continue;
1441 }
1442
1443 /* first pass over all the untiled connectors */
1444 if (tile_pass == 0 && fb_helper_conn->connector->has_tile)
1445 continue;
1446
1447 if (tile_pass == 1) {
1448 if (fb_helper_conn->connector->tile_h_loc != 0 ||
1449 fb_helper_conn->connector->tile_v_loc != 0)
1450 continue;
1451
1452 } else {
1453 if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 &&
1454 fb_helper_conn->connector->tile_v_loc != tile_pass - 1)
1455 /* if this tile_pass doesn't cover any of the tiles - keep going */
1456 continue;
1457
1458 /* find the tile offsets for this pass - need
1459 to find all tiles left and above */
1460 drm_get_tile_offsets(fb_helper, modes, offsets,
1461 i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc);
1462 }
1394 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", 1463 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n",
1395 fb_helper_conn->connector->base.id); 1464 fb_helper_conn->connector->base.id);
1396 1465
1397 /* got for command line mode first */ 1466 /* got for command line mode first */
1398 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1467 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height);
1399 if (!modes[i]) { 1468 if (!modes[i]) {
1400 DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", 1469 DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n",
1401 fb_helper_conn->connector->base.id); 1470 fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0);
1402 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); 1471 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height);
1403 } 1472 }
1404 /* No preferred modes, pick one off the list */ 1473 /* No preferred modes, pick one off the list */
@@ -1408,6 +1477,12 @@ static bool drm_target_preferred(struct drm_fb_helper *fb_helper,
1408 } 1477 }
1409 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : 1478 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name :
1410 "none"); 1479 "none");
1480 conn_configured |= (1 << i);
1481 }
1482
1483 if ((conn_configured & mask) != mask) {
1484 tile_pass++;
1485 goto retry;
1411 } 1486 }
1412 return true; 1487 return true;
1413} 1488}
@@ -1497,6 +1572,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1497 struct drm_device *dev = fb_helper->dev; 1572 struct drm_device *dev = fb_helper->dev;
1498 struct drm_fb_helper_crtc **crtcs; 1573 struct drm_fb_helper_crtc **crtcs;
1499 struct drm_display_mode **modes; 1574 struct drm_display_mode **modes;
1575 struct drm_fb_offset *offsets;
1500 struct drm_mode_set *modeset; 1576 struct drm_mode_set *modeset;
1501 bool *enabled; 1577 bool *enabled;
1502 int width, height; 1578 int width, height;
@@ -1511,9 +1587,11 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1511 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); 1587 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL);
1512 modes = kcalloc(dev->mode_config.num_connector, 1588 modes = kcalloc(dev->mode_config.num_connector,
1513 sizeof(struct drm_display_mode *), GFP_KERNEL); 1589 sizeof(struct drm_display_mode *), GFP_KERNEL);
1590 offsets = kcalloc(dev->mode_config.num_connector,
1591 sizeof(struct drm_fb_offset), GFP_KERNEL);
1514 enabled = kcalloc(dev->mode_config.num_connector, 1592 enabled = kcalloc(dev->mode_config.num_connector,
1515 sizeof(bool), GFP_KERNEL); 1593 sizeof(bool), GFP_KERNEL);
1516 if (!crtcs || !modes || !enabled) { 1594 if (!crtcs || !modes || !enabled || !offsets) {
1517 DRM_ERROR("Memory allocation failed\n"); 1595 DRM_ERROR("Memory allocation failed\n");
1518 goto out; 1596 goto out;
1519 } 1597 }
@@ -1523,14 +1601,16 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1523 1601
1524 if (!(fb_helper->funcs->initial_config && 1602 if (!(fb_helper->funcs->initial_config &&
1525 fb_helper->funcs->initial_config(fb_helper, crtcs, modes, 1603 fb_helper->funcs->initial_config(fb_helper, crtcs, modes,
1604 offsets,
1526 enabled, width, height))) { 1605 enabled, width, height))) {
1527 memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0])); 1606 memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0]));
1528 memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0])); 1607 memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0]));
1608 memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0]));
1529 1609
1530 if (!drm_target_cloned(fb_helper, 1610 if (!drm_target_cloned(fb_helper, modes, offsets,
1531 modes, enabled, width, height) && 1611 enabled, width, height) &&
1532 !drm_target_preferred(fb_helper, 1612 !drm_target_preferred(fb_helper, modes, offsets,
1533 modes, enabled, width, height)) 1613 enabled, width, height))
1534 DRM_ERROR("Unable to find initial modes\n"); 1614 DRM_ERROR("Unable to find initial modes\n");
1535 1615
1536 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", 1616 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n",
@@ -1550,18 +1630,23 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1550 for (i = 0; i < fb_helper->connector_count; i++) { 1630 for (i = 0; i < fb_helper->connector_count; i++) {
1551 struct drm_display_mode *mode = modes[i]; 1631 struct drm_display_mode *mode = modes[i];
1552 struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; 1632 struct drm_fb_helper_crtc *fb_crtc = crtcs[i];
1633 struct drm_fb_offset *offset = &offsets[i];
1553 modeset = &fb_crtc->mode_set; 1634 modeset = &fb_crtc->mode_set;
1554 1635
1555 if (mode && fb_crtc) { 1636 if (mode && fb_crtc) {
1556 DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", 1637 DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n",
1557 mode->name, fb_crtc->mode_set.crtc->base.id); 1638 mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y);
1558 fb_crtc->desired_mode = mode; 1639 fb_crtc->desired_mode = mode;
1640 fb_crtc->x = offset->x;
1641 fb_crtc->y = offset->y;
1559 if (modeset->mode) 1642 if (modeset->mode)
1560 drm_mode_destroy(dev, modeset->mode); 1643 drm_mode_destroy(dev, modeset->mode);
1561 modeset->mode = drm_mode_duplicate(dev, 1644 modeset->mode = drm_mode_duplicate(dev,
1562 fb_crtc->desired_mode); 1645 fb_crtc->desired_mode);
1563 modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; 1646 modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector;
1564 modeset->fb = fb_helper->fb; 1647 modeset->fb = fb_helper->fb;
1648 modeset->x = offset->x;
1649 modeset->y = offset->y;
1565 } 1650 }
1566 } 1651 }
1567 1652
@@ -1578,6 +1663,7 @@ static void drm_setup_crtcs(struct drm_fb_helper *fb_helper)
1578out: 1663out:
1579 kfree(crtcs); 1664 kfree(crtcs);
1580 kfree(modes); 1665 kfree(modes);
1666 kfree(offsets);
1581 kfree(enabled); 1667 kfree(enabled);
1582} 1668}
1583 1669