diff options
Diffstat (limited to 'drivers/gpu/drm/drm_fb_helper.c')
-rw-r--r-- | drivers/gpu/drm/drm_fb_helper.c | 134 |
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 | } |
355 | EXPORT_SYMBOL(drm_fb_helper_restore_fbdev_mode_unlocked); | 364 | EXPORT_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 | } |
897 | EXPORT_SYMBOL(drm_fb_helper_set_par); | 902 | EXPORT_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 | ||
1308 | static bool drm_target_cloned(struct drm_fb_helper *fb_helper, | 1315 | static 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 | ||
1389 | static 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 | |||
1381 | static bool drm_target_preferred(struct drm_fb_helper *fb_helper, | 1421 | static 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; | ||
1431 | retry: | ||
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) | |||
1578 | out: | 1663 | out: |
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 | ||