Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/fb: add support for tiled monitor configurations. (v2)

This adds fbdev/con support for tiled monitors, so that we
only set a mode on the correct half of the monitor, or
span the two halves if needed.

v2: remove unneeded ERROR, fix | vs ||

Signed-off-by: Dave Airlie <airlied@redhat.com>

+128 -23
+101 -20
drivers/gpu/drm/drm_fb_helper.c
··· 1000 1000 crtc_count = 0; 1001 1001 for (i = 0; i < fb_helper->crtc_count; i++) { 1002 1002 struct drm_display_mode *desired_mode; 1003 + int x, y; 1003 1004 desired_mode = fb_helper->crtc_info[i].desired_mode; 1004 - 1005 + x = fb_helper->crtc_info[i].x; 1006 + y = fb_helper->crtc_info[i].y; 1005 1007 if (desired_mode) { 1006 1008 if (gamma_size == 0) 1007 1009 gamma_size = fb_helper->crtc_info[i].mode_set.crtc->gamma_size; 1008 - if (desired_mode->hdisplay < sizes.fb_width) 1009 - sizes.fb_width = desired_mode->hdisplay; 1010 - if (desired_mode->vdisplay < sizes.fb_height) 1011 - sizes.fb_height = desired_mode->vdisplay; 1012 - if (desired_mode->hdisplay > sizes.surface_width) 1013 - sizes.surface_width = desired_mode->hdisplay; 1014 - if (desired_mode->vdisplay > sizes.surface_height) 1015 - sizes.surface_height = desired_mode->vdisplay; 1010 + if (desired_mode->hdisplay + x < sizes.fb_width) 1011 + sizes.fb_width = desired_mode->hdisplay + x; 1012 + if (desired_mode->vdisplay + y < sizes.fb_height) 1013 + sizes.fb_height = desired_mode->vdisplay + y; 1014 + if (desired_mode->hdisplay + x > sizes.surface_width) 1015 + sizes.surface_width = desired_mode->hdisplay + x; 1016 + if (desired_mode->vdisplay + y > sizes.surface_height) 1017 + sizes.surface_height = desired_mode->vdisplay + y; 1016 1018 crtc_count++; 1017 1019 } 1018 1020 } ··· 1314 1312 1315 1313 static bool drm_target_cloned(struct drm_fb_helper *fb_helper, 1316 1314 struct drm_display_mode **modes, 1315 + struct drm_fb_offset *offsets, 1317 1316 bool *enabled, int width, int height) 1318 1317 { 1319 1318 int count, i, j; ··· 1386 1383 return false; 1387 1384 } 1388 1385 1386 + static int drm_get_tile_offsets(struct drm_fb_helper *fb_helper, 1387 + struct drm_display_mode **modes, 1388 + struct drm_fb_offset *offsets, 1389 + int idx, 1390 + int h_idx, int v_idx) 1391 + { 1392 + struct drm_fb_helper_connector *fb_helper_conn; 1393 + int i; 1394 + int hoffset = 0, voffset = 0; 1395 + 1396 + for (i = 0; i < fb_helper->connector_count; i++) { 1397 + fb_helper_conn = fb_helper->connector_info[i]; 1398 + if (!fb_helper_conn->connector->has_tile) 1399 + continue; 1400 + 1401 + if (!modes[i] && (h_idx || v_idx)) { 1402 + DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, 1403 + fb_helper_conn->connector->base.id); 1404 + continue; 1405 + } 1406 + if (fb_helper_conn->connector->tile_h_loc < h_idx) 1407 + hoffset += modes[i]->hdisplay; 1408 + 1409 + if (fb_helper_conn->connector->tile_v_loc < v_idx) 1410 + voffset += modes[i]->vdisplay; 1411 + } 1412 + offsets[idx].x = hoffset; 1413 + offsets[idx].y = voffset; 1414 + DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); 1415 + return 0; 1416 + } 1417 + 1389 1418 static bool drm_target_preferred(struct drm_fb_helper *fb_helper, 1390 1419 struct drm_display_mode **modes, 1420 + struct drm_fb_offset *offsets, 1391 1421 bool *enabled, int width, int height) 1392 1422 { 1393 1423 struct drm_fb_helper_connector *fb_helper_conn; 1394 1424 int i; 1395 - 1425 + uint64_t conn_configured = 0, mask; 1426 + int tile_pass = 0; 1427 + mask = (1 << fb_helper->connector_count) - 1; 1428 + retry: 1396 1429 for (i = 0; i < fb_helper->connector_count; i++) { 1397 1430 fb_helper_conn = fb_helper->connector_info[i]; 1398 1431 1399 - if (enabled[i] == false) 1432 + if (conn_configured & (1 << i)) 1400 1433 continue; 1401 1434 1435 + if (enabled[i] == false) { 1436 + conn_configured |= (1 << i); 1437 + continue; 1438 + } 1439 + 1440 + /* first pass over all the untiled connectors */ 1441 + if (tile_pass == 0 && fb_helper_conn->connector->has_tile) 1442 + continue; 1443 + 1444 + if (tile_pass == 1) { 1445 + if (fb_helper_conn->connector->tile_h_loc != 0 || 1446 + fb_helper_conn->connector->tile_v_loc != 0) 1447 + continue; 1448 + 1449 + } else { 1450 + if (fb_helper_conn->connector->tile_h_loc != tile_pass -1 && 1451 + fb_helper_conn->connector->tile_v_loc != tile_pass - 1) 1452 + /* if this tile_pass doesn't cover any of the tiles - keep going */ 1453 + continue; 1454 + 1455 + /* find the tile offsets for this pass - need 1456 + to find all tiles left and above */ 1457 + drm_get_tile_offsets(fb_helper, modes, offsets, 1458 + i, fb_helper_conn->connector->tile_h_loc, fb_helper_conn->connector->tile_v_loc); 1459 + } 1402 1460 DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", 1403 1461 fb_helper_conn->connector->base.id); 1404 1462 1405 1463 /* got for command line mode first */ 1406 1464 modes[i] = drm_pick_cmdline_mode(fb_helper_conn, width, height); 1407 1465 if (!modes[i]) { 1408 - DRM_DEBUG_KMS("looking for preferred mode on connector %d\n", 1409 - fb_helper_conn->connector->base.id); 1466 + DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", 1467 + fb_helper_conn->connector->base.id, fb_helper_conn->connector->tile_group ? fb_helper_conn->connector->tile_group->id : 0); 1410 1468 modes[i] = drm_has_preferred_mode(fb_helper_conn, width, height); 1411 1469 } 1412 1470 /* No preferred modes, pick one off the list */ ··· 1477 1413 } 1478 1414 DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : 1479 1415 "none"); 1416 + conn_configured |= (1 << i); 1417 + } 1418 + 1419 + if ((conn_configured & mask) != mask) { 1420 + tile_pass++; 1421 + goto retry; 1480 1422 } 1481 1423 return true; 1482 1424 } ··· 1572 1502 struct drm_device *dev = fb_helper->dev; 1573 1503 struct drm_fb_helper_crtc **crtcs; 1574 1504 struct drm_display_mode **modes; 1505 + struct drm_fb_offset *offsets; 1575 1506 struct drm_mode_set *modeset; 1576 1507 bool *enabled; 1577 1508 int width, height; ··· 1587 1516 sizeof(struct drm_fb_helper_crtc *), GFP_KERNEL); 1588 1517 modes = kcalloc(dev->mode_config.num_connector, 1589 1518 sizeof(struct drm_display_mode *), GFP_KERNEL); 1519 + offsets = kcalloc(dev->mode_config.num_connector, 1520 + sizeof(struct drm_fb_offset), GFP_KERNEL); 1590 1521 enabled = kcalloc(dev->mode_config.num_connector, 1591 1522 sizeof(bool), GFP_KERNEL); 1592 - if (!crtcs || !modes || !enabled) { 1523 + if (!crtcs || !modes || !enabled || !offsets) { 1593 1524 DRM_ERROR("Memory allocation failed\n"); 1594 1525 goto out; 1595 1526 } ··· 1601 1528 1602 1529 if (!(fb_helper->funcs->initial_config && 1603 1530 fb_helper->funcs->initial_config(fb_helper, crtcs, modes, 1531 + offsets, 1604 1532 enabled, width, height))) { 1605 1533 memset(modes, 0, dev->mode_config.num_connector*sizeof(modes[0])); 1606 1534 memset(crtcs, 0, dev->mode_config.num_connector*sizeof(crtcs[0])); 1535 + memset(offsets, 0, dev->mode_config.num_connector*sizeof(offsets[0])); 1607 1536 1608 - if (!drm_target_cloned(fb_helper, 1609 - modes, enabled, width, height) && 1610 - !drm_target_preferred(fb_helper, 1611 - modes, enabled, width, height)) 1537 + if (!drm_target_cloned(fb_helper, modes, offsets, 1538 + enabled, width, height) && 1539 + !drm_target_preferred(fb_helper, modes, offsets, 1540 + enabled, width, height)) 1612 1541 DRM_ERROR("Unable to find initial modes\n"); 1613 1542 1614 1543 DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", ··· 1630 1555 for (i = 0; i < fb_helper->connector_count; i++) { 1631 1556 struct drm_display_mode *mode = modes[i]; 1632 1557 struct drm_fb_helper_crtc *fb_crtc = crtcs[i]; 1558 + struct drm_fb_offset *offset = &offsets[i]; 1633 1559 modeset = &fb_crtc->mode_set; 1634 1560 1635 1561 if (mode && fb_crtc) { 1636 - DRM_DEBUG_KMS("desired mode %s set on crtc %d\n", 1637 - mode->name, fb_crtc->mode_set.crtc->base.id); 1562 + DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", 1563 + mode->name, fb_crtc->mode_set.crtc->base.id, offset->x, offset->y); 1638 1564 fb_crtc->desired_mode = mode; 1565 + fb_crtc->x = offset->x; 1566 + fb_crtc->y = offset->y; 1639 1567 if (modeset->mode) 1640 1568 drm_mode_destroy(dev, modeset->mode); 1641 1569 modeset->mode = drm_mode_duplicate(dev, 1642 1570 fb_crtc->desired_mode); 1643 1571 modeset->connectors[modeset->num_connectors++] = fb_helper->connector_info[i]->connector; 1644 1572 modeset->fb = fb_helper->fb; 1573 + modeset->x = offset->x; 1574 + modeset->y = offset->y; 1645 1575 } 1646 1576 } 1647 1577 ··· 1663 1583 out: 1664 1584 kfree(crtcs); 1665 1585 kfree(modes); 1586 + kfree(offsets); 1666 1587 kfree(enabled); 1667 1588 } 1668 1589
+21 -3
drivers/gpu/drm/i915/intel_fbdev.c
··· 324 324 static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, 325 325 struct drm_fb_helper_crtc **crtcs, 326 326 struct drm_display_mode **modes, 327 + struct drm_fb_offset *offsets, 327 328 bool *enabled, int width, int height) 328 329 { 329 330 struct drm_device *dev = fb_helper->dev; ··· 333 332 bool fallback = true; 334 333 int num_connectors_enabled = 0; 335 334 int num_connectors_detected = 0; 335 + uint64_t conn_configured = 0, mask; 336 + int pass = 0; 336 337 337 338 save_enabled = kcalloc(dev->mode_config.num_connector, sizeof(bool), 338 339 GFP_KERNEL); ··· 342 339 return false; 343 340 344 341 memcpy(save_enabled, enabled, dev->mode_config.num_connector); 345 - 342 + mask = (1 << fb_helper->connector_count) - 1; 343 + retry: 346 344 for (i = 0; i < fb_helper->connector_count; i++) { 347 345 struct drm_fb_helper_connector *fb_conn; 348 346 struct drm_connector *connector; ··· 353 349 fb_conn = fb_helper->connector_info[i]; 354 350 connector = fb_conn->connector; 355 351 352 + if (conn_configured & (1 << i)) 353 + continue; 354 + 355 + if (pass == 0 && !connector->has_tile) 356 + continue; 357 + 356 358 if (connector->status == connector_status_connected) 357 359 num_connectors_detected++; 358 360 359 361 if (!enabled[i]) { 360 362 DRM_DEBUG_KMS("connector %s not enabled, skipping\n", 361 363 connector->name); 364 + conn_configured |= (1 << i); 362 365 continue; 363 366 } 364 367 ··· 384 373 DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", 385 374 connector->name); 386 375 enabled[i] = false; 376 + conn_configured |= (1 << i); 387 377 continue; 388 378 } 389 379 ··· 412 400 413 401 /* try for preferred next */ 414 402 if (!modes[i]) { 415 - DRM_DEBUG_KMS("looking for preferred mode on connector %s\n", 416 - connector->name); 403 + DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", 404 + connector->name, connector->has_tile); 417 405 modes[i] = drm_has_preferred_mode(fb_conn, width, 418 406 height); 419 407 } ··· 456 444 modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :""); 457 445 458 446 fallback = false; 447 + conn_configured |= (1 << i); 448 + } 449 + 450 + if ((conn_configured & mask) != mask) { 451 + pass++; 452 + goto retry; 459 453 } 460 454 461 455 /*
+6
include/drm/drm_fb_helper.h
··· 34 34 35 35 #include <linux/kgdb.h> 36 36 37 + struct drm_fb_offset { 38 + int x, y; 39 + }; 40 + 37 41 struct drm_fb_helper_crtc { 38 42 struct drm_mode_set mode_set; 39 43 struct drm_display_mode *desired_mode; 44 + int x, y; 40 45 }; 41 46 42 47 struct drm_fb_helper_surface_size { ··· 77 72 bool (*initial_config)(struct drm_fb_helper *fb_helper, 78 73 struct drm_fb_helper_crtc **crtcs, 79 74 struct drm_display_mode **modes, 75 + struct drm_fb_offset *offsets, 80 76 bool *enabled, int width, int height); 81 77 }; 82 78