aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2016-06-08 11:15:36 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2016-06-10 10:58:27 -0400
commita095caa7f5ec54b51a1aa8a5c692b2a8c1609f5d (patch)
treea94258aacc11bc2261fc52509ba28607a586bf36
parent3b24f7d6758165919ba7b83b3c8365c38ffacc0b (diff)
drm/atomic-helper: roll out commit synchronization
To facilitate easier reviewing this is split out from the overall nonblocking commit rework. It just rolls out the helper functions and uses them in the main drm_atomic_helper_commit() function to make it clear where in the flow they're used. The next patch will actually split drm_atomic_helper_commit() into 2 pieces, with the tail being run asynchronously from a worker. v2: Improve kerneldocs (Maarten). v3: Don't convert ERESTARTSYS to EINTR (Maarten). Also don't fail if the wait succeed in stall_check - we need to convert that case (it returns the remaining jiffies) to 0 for success. v4: Switch to long for wait_for_completion_timeout return value everywhere (Maarten). v5: Fix miscaped function in kerneldoc (Maarten). Reviewed-by: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Tested-by: Tomeu Vizoso <tomeu.vizoso@collabora.com> Cc: Maarten Lankhorst <maarten.lankhorst@linux.intel.com> Cc: Tomeu Vizoso <tomeu.vizoso@gmail.com> Cc: Daniel Stone <daniels@collabora.com> Tested-by: Liviu Dudau <Liviu.Dudau@arm.com> Signed-off-by: Daniel Vetter <daniel.vetter@intel.com> Link: http://patchwork.freedesktop.org/patch/msgid/1465398936-22305-1-git-send-email-daniel.vetter@ffwll.ch
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c347
-rw-r--r--include/drm/drm_atomic_helper.h7
2 files changed, 354 insertions, 0 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index aa2cad922791..6dee09cfbf93 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -1157,6 +1157,10 @@ int drm_atomic_helper_commit(struct drm_device *dev,
1157 if (nonblock) 1157 if (nonblock)
1158 return -EBUSY; 1158 return -EBUSY;
1159 1159
1160 ret = drm_atomic_helper_setup_commit(state, nonblock);
1161 if (ret)
1162 return ret;
1163
1160 ret = drm_atomic_helper_prepare_planes(dev, state); 1164 ret = drm_atomic_helper_prepare_planes(dev, state);
1161 if (ret) 1165 if (ret)
1162 return ret; 1166 return ret;
@@ -1187,16 +1191,22 @@ int drm_atomic_helper_commit(struct drm_device *dev,
1187 1191
1188 drm_atomic_helper_wait_for_fences(dev, state); 1192 drm_atomic_helper_wait_for_fences(dev, state);
1189 1193
1194 drm_atomic_helper_wait_for_dependencies(state);
1195
1190 drm_atomic_helper_commit_modeset_disables(dev, state); 1196 drm_atomic_helper_commit_modeset_disables(dev, state);
1191 1197
1192 drm_atomic_helper_commit_planes(dev, state, false); 1198 drm_atomic_helper_commit_planes(dev, state, false);
1193 1199
1194 drm_atomic_helper_commit_modeset_enables(dev, state); 1200 drm_atomic_helper_commit_modeset_enables(dev, state);
1195 1201
1202 drm_atomic_helper_commit_hw_done(state);
1203
1196 drm_atomic_helper_wait_for_vblanks(dev, state); 1204 drm_atomic_helper_wait_for_vblanks(dev, state);
1197 1205
1198 drm_atomic_helper_cleanup_planes(dev, state); 1206 drm_atomic_helper_cleanup_planes(dev, state);
1199 1207
1208 drm_atomic_helper_commit_cleanup_done(state);
1209
1200 drm_atomic_state_free(state); 1210 drm_atomic_state_free(state);
1201 1211
1202 return 0; 1212 return 0;
@@ -1241,6 +1251,306 @@ EXPORT_SYMBOL(drm_atomic_helper_commit);
1241 * being displayed. 1251 * being displayed.
1242 */ 1252 */
1243 1253
1254static int stall_checks(struct drm_crtc *crtc, bool nonblock)
1255{
1256 struct drm_crtc_commit *commit, *stall_commit = NULL;
1257 bool completed = true;
1258 int i;
1259 long ret = 0;
1260
1261 spin_lock(&crtc->commit_lock);
1262 i = 0;
1263 list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
1264 if (i == 0) {
1265 completed = try_wait_for_completion(&commit->flip_done);
1266 /* Userspace is not allowed to get ahead of the previous
1267 * commit with nonblocking ones. */
1268 if (!completed && nonblock) {
1269 spin_unlock(&crtc->commit_lock);
1270 return -EBUSY;
1271 }
1272 } else if (i == 1) {
1273 stall_commit = commit;
1274 drm_crtc_commit_get(stall_commit);
1275 } else
1276 break;
1277
1278 i++;
1279 }
1280 spin_unlock(&crtc->commit_lock);
1281
1282 if (!stall_commit)
1283 return 0;
1284
1285 /* We don't want to let commits get ahead of cleanup work too much,
1286 * stalling on 2nd previous commit means triple-buffer won't ever stall.
1287 */
1288 ret = wait_for_completion_interruptible_timeout(&commit->cleanup_done,
1289 10*HZ);
1290 if (ret == 0)
1291 DRM_ERROR("[CRTC:%d:%s] cleanup_done timed out\n",
1292 crtc->base.id, crtc->name);
1293
1294 drm_crtc_commit_put(stall_commit);
1295
1296 return ret < 0 ? ret : 0;
1297}
1298
1299/**
1300 * drm_atomic_helper_setup_commit - setup possibly nonblocking commit
1301 * @state: new modeset state to be committed
1302 * @nonblock: whether nonblocking behavior is requested.
1303 *
1304 * This function prepares @state to be used by the atomic helper's support for
1305 * nonblocking commits. Drivers using the nonblocking commit infrastructure
1306 * should always call this function from their ->atomic_commit hook.
1307 *
1308 * To be able to use this support drivers need to use a few more helper
1309 * functions. drm_atomic_helper_wait_for_dependencies() must be called before
1310 * actually committing the hardware state, and for nonblocking commits this call
1311 * must be placed in the async worker. See also drm_atomic_helper_swap_state()
1312 * and it's stall parameter, for when a driver's commit hooks look at the
1313 * ->state pointers of struct &drm_crtc, &drm_plane or &drm_connector directly.
1314 *
1315 * Completion of the hardware commit step must be signalled using
1316 * drm_atomic_helper_commit_hw_done(). After this step the driver is not allowed
1317 * to read or change any permanent software or hardware modeset state. The only
1318 * exception is state protected by other means than &drm_modeset_lock locks.
1319 * Only the free standing @state with pointers to the old state structures can
1320 * be inspected, e.g. to clean up old buffers using
1321 * drm_atomic_helper_cleanup_planes().
1322 *
1323 * At the very end, before cleaning up @state drivers must call
1324 * drm_atomic_helper_commit_cleanup_done().
1325 *
1326 * This is all implemented by in drm_atomic_helper_commit(), giving drivers a
1327 * complete and esay-to-use default implementation of the atomic_commit() hook.
1328 *
1329 * The tracking of asynchronously executed and still pending commits is done
1330 * using the core structure &drm_crtc_commit.
1331 *
1332 * By default there's no need to clean up resources allocated by this function
1333 * explicitly: drm_atomic_state_default_clear() will take care of that
1334 * automatically.
1335 *
1336 * Returns:
1337 *
1338 * 0 on success. -EBUSY when userspace schedules nonblocking commits too fast,
1339 * -ENOMEM on allocation failures and -EINTR when a signal is pending.
1340 */
1341int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
1342 bool nonblock)
1343{
1344 struct drm_crtc *crtc;
1345 struct drm_crtc_state *crtc_state;
1346 struct drm_crtc_commit *commit;
1347 int i, ret;
1348
1349 for_each_crtc_in_state(state, crtc, crtc_state, i) {
1350 commit = kzalloc(sizeof(*commit), GFP_KERNEL);
1351 if (!commit)
1352 return -ENOMEM;
1353
1354 init_completion(&commit->flip_done);
1355 init_completion(&commit->hw_done);
1356 init_completion(&commit->cleanup_done);
1357 INIT_LIST_HEAD(&commit->commit_entry);
1358 kref_init(&commit->ref);
1359 commit->crtc = crtc;
1360
1361 state->crtcs[i].commit = commit;
1362
1363 ret = stall_checks(crtc, nonblock);
1364 if (ret)
1365 return ret;
1366
1367 /* Drivers only send out events when at least either current or
1368 * new CRTC state is active. Complete right away if everything
1369 * stays off. */
1370 if (!crtc->state->active && !crtc_state->active) {
1371 complete_all(&commit->flip_done);
1372 continue;
1373 }
1374
1375 /* Legacy cursor updates are fully unsynced. */
1376 if (state->legacy_cursor_update) {
1377 complete_all(&commit->flip_done);
1378 continue;
1379 }
1380
1381 if (!crtc_state->event) {
1382 commit->event = kzalloc(sizeof(*commit->event),
1383 GFP_KERNEL);
1384 if (!commit->event)
1385 return -ENOMEM;
1386
1387 crtc_state->event = commit->event;
1388 }
1389
1390 crtc_state->event->base.completion = &commit->flip_done;
1391 }
1392
1393 return 0;
1394}
1395EXPORT_SYMBOL(drm_atomic_helper_setup_commit);
1396
1397
1398static struct drm_crtc_commit *preceeding_commit(struct drm_crtc *crtc)
1399{
1400 struct drm_crtc_commit *commit;
1401 int i = 0;
1402
1403 list_for_each_entry(commit, &crtc->commit_list, commit_entry) {
1404 /* skip the first entry, that's the current commit */
1405 if (i == 1)
1406 return commit;
1407 i++;
1408 }
1409
1410 return NULL;
1411}
1412
1413/**
1414 * drm_atomic_helper_wait_for_dependencies - wait for required preceeding commits
1415 * @state: new modeset state to be committed
1416 *
1417 * This function waits for all preceeding commits that touch the same CRTC as
1418 * @state to both be committed to the hardware (as signalled by
1419 * drm_atomic_helper_commit_hw_done) and executed by the hardware (as signalled
1420 * by calling drm_crtc_vblank_send_event on the event member of
1421 * &drm_crtc_state).
1422 *
1423 * This is part of the atomic helper support for nonblocking commits, see
1424 * drm_atomic_helper_setup_commit() for an overview.
1425 */
1426void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state)
1427{
1428 struct drm_crtc *crtc;
1429 struct drm_crtc_state *crtc_state;
1430 struct drm_crtc_commit *commit;
1431 int i;
1432 long ret;
1433
1434 for_each_crtc_in_state(state, crtc, crtc_state, i) {
1435 spin_lock(&crtc->commit_lock);
1436 commit = preceeding_commit(crtc);
1437 if (commit)
1438 drm_crtc_commit_get(commit);
1439 spin_unlock(&crtc->commit_lock);
1440
1441 if (!commit)
1442 continue;
1443
1444 ret = wait_for_completion_timeout(&commit->hw_done,
1445 10*HZ);
1446 if (ret == 0)
1447 DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n",
1448 crtc->base.id, crtc->name);
1449
1450 /* Currently no support for overwriting flips, hence
1451 * stall for previous one to execute completely. */
1452 ret = wait_for_completion_timeout(&commit->flip_done,
1453 10*HZ);
1454 if (ret == 0)
1455 DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
1456 crtc->base.id, crtc->name);
1457
1458 drm_crtc_commit_put(commit);
1459 }
1460}
1461EXPORT_SYMBOL(drm_atomic_helper_wait_for_dependencies);
1462
1463/**
1464 * drm_atomic_helper_commit_hw_done - setup possible nonblocking commit
1465 * @state: new modeset state to be committed
1466 *
1467 * This function is used to signal completion of the hardware commit step. After
1468 * this step the driver is not allowed to read or change any permanent software
1469 * or hardware modeset state. The only exception is state protected by other
1470 * means than &drm_modeset_lock locks.
1471 *
1472 * Drivers should try to postpone any expensive or delayed cleanup work after
1473 * this function is called.
1474 *
1475 * This is part of the atomic helper support for nonblocking commits, see
1476 * drm_atomic_helper_setup_commit() for an overview.
1477 */
1478void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state)
1479{
1480 struct drm_crtc *crtc;
1481 struct drm_crtc_state *crtc_state;
1482 struct drm_crtc_commit *commit;
1483 int i;
1484
1485 for_each_crtc_in_state(state, crtc, crtc_state, i) {
1486 commit = state->crtcs[i].commit;
1487 if (!commit)
1488 continue;
1489
1490 /* backend must have consumed any event by now */
1491 WARN_ON(crtc->state->event);
1492 spin_lock(&crtc->commit_lock);
1493 complete_all(&commit->hw_done);
1494 spin_unlock(&crtc->commit_lock);
1495 }
1496}
1497EXPORT_SYMBOL(drm_atomic_helper_commit_hw_done);
1498
1499/**
1500 * drm_atomic_helper_commit_cleanup_done - signal completion of commit
1501 * @state: new modeset state to be committed
1502 *
1503 * This signals completion of the atomic update @state, including any cleanup
1504 * work. If used, it must be called right before calling
1505 * drm_atomic_state_free().
1506 *
1507 * This is part of the atomic helper support for nonblocking commits, see
1508 * drm_atomic_helper_setup_commit() for an overview.
1509 */
1510void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state)
1511{
1512 struct drm_crtc *crtc;
1513 struct drm_crtc_state *crtc_state;
1514 struct drm_crtc_commit *commit;
1515 int i;
1516 long ret;
1517
1518 for_each_crtc_in_state(state, crtc, crtc_state, i) {
1519 commit = state->crtcs[i].commit;
1520 if (WARN_ON(!commit))
1521 continue;
1522
1523 spin_lock(&crtc->commit_lock);
1524 complete_all(&commit->cleanup_done);
1525 WARN_ON(!try_wait_for_completion(&commit->hw_done));
1526
1527 /* commit_list borrows our reference, need to remove before we
1528 * clean up our drm_atomic_state. But only after it actually
1529 * completed, otherwise subsequent commits won't stall properly. */
1530 if (try_wait_for_completion(&commit->flip_done)) {
1531 list_del(&commit->commit_entry);
1532 spin_unlock(&crtc->commit_lock);
1533 continue;
1534 }
1535
1536 spin_unlock(&crtc->commit_lock);
1537
1538 /* We must wait for the vblank event to signal our completion
1539 * before releasing our reference, since the vblank work does
1540 * not hold a reference of its own. */
1541 ret = wait_for_completion_timeout(&commit->flip_done,
1542 10*HZ);
1543 if (ret == 0)
1544 DRM_ERROR("[CRTC:%d:%s] flip_done timed out\n",
1545 crtc->base.id, crtc->name);
1546
1547 spin_lock(&crtc->commit_lock);
1548 list_del(&commit->commit_entry);
1549 spin_unlock(&crtc->commit_lock);
1550 }
1551}
1552EXPORT_SYMBOL(drm_atomic_helper_commit_cleanup_done);
1553
1244/** 1554/**
1245 * drm_atomic_helper_prepare_planes - prepare plane resources before commit 1555 * drm_atomic_helper_prepare_planes - prepare plane resources before commit
1246 * @dev: DRM device 1556 * @dev: DRM device
@@ -1560,17 +1870,45 @@ EXPORT_SYMBOL(drm_atomic_helper_cleanup_planes);
1560 * 1870 *
1561 * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3 1871 * 5. Call drm_atomic_helper_cleanup_planes() with @state, which since step 3
1562 * contains the old state. Also do any other cleanup required with that state. 1872 * contains the old state. Also do any other cleanup required with that state.
1873 *
1874 * @stall must be set when nonblocking commits for this driver directly access
1875 * the ->state pointer of &drm_plane, &drm_crtc or &drm_connector. With the
1876 * current atomic helpers this is almost always the case, since the helpers
1877 * don't pass the right state structures to the callbacks.
1563 */ 1878 */
1564void drm_atomic_helper_swap_state(struct drm_atomic_state *state, 1879void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
1565 bool stall) 1880 bool stall)
1566{ 1881{
1567 int i; 1882 int i;
1883 long ret;
1568 struct drm_connector *connector; 1884 struct drm_connector *connector;
1569 struct drm_connector_state *conn_state; 1885 struct drm_connector_state *conn_state;
1570 struct drm_crtc *crtc; 1886 struct drm_crtc *crtc;
1571 struct drm_crtc_state *crtc_state; 1887 struct drm_crtc_state *crtc_state;
1572 struct drm_plane *plane; 1888 struct drm_plane *plane;
1573 struct drm_plane_state *plane_state; 1889 struct drm_plane_state *plane_state;
1890 struct drm_crtc_commit *commit;
1891
1892 if (stall) {
1893 for_each_crtc_in_state(state, crtc, crtc_state, i) {
1894 spin_lock(&crtc->commit_lock);
1895 commit = list_first_entry_or_null(&crtc->commit_list,
1896 struct drm_crtc_commit, commit_entry);
1897 if (commit)
1898 drm_crtc_commit_get(commit);
1899 spin_unlock(&crtc->commit_lock);
1900
1901 if (!commit)
1902 continue;
1903
1904 ret = wait_for_completion_timeout(&commit->hw_done,
1905 10*HZ);
1906 if (ret == 0)
1907 DRM_ERROR("[CRTC:%d:%s] hw_done timed out\n",
1908 crtc->base.id, crtc->name);
1909 drm_crtc_commit_put(commit);
1910 }
1911 }
1574 1912
1575 for_each_connector_in_state(state, connector, conn_state, i) { 1913 for_each_connector_in_state(state, connector, conn_state, i) {
1576 connector->state->state = state; 1914 connector->state->state = state;
@@ -1582,6 +1920,15 @@ void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
1582 crtc->state->state = state; 1920 crtc->state->state = state;
1583 swap(state->crtcs[i].state, crtc->state); 1921 swap(state->crtcs[i].state, crtc->state);
1584 crtc->state->state = NULL; 1922 crtc->state->state = NULL;
1923
1924 if (state->crtcs[i].commit) {
1925 spin_lock(&crtc->commit_lock);
1926 list_add(&state->crtcs[i].commit->commit_entry,
1927 &crtc->commit_list);
1928 spin_unlock(&crtc->commit_lock);
1929
1930 state->crtcs[i].commit->event = NULL;
1931 }
1585 } 1932 }
1586 1933
1587 for_each_plane_in_state(state, plane, plane_state, i) { 1934 for_each_plane_in_state(state, plane, plane_state, i) {
diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h
index 51c57eaa2c8f..d90b3a60db45 100644
--- a/include/drm/drm_atomic_helper.h
+++ b/include/drm/drm_atomic_helper.h
@@ -74,6 +74,13 @@ void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc,
74void drm_atomic_helper_swap_state(struct drm_atomic_state *state, 74void drm_atomic_helper_swap_state(struct drm_atomic_state *state,
75 bool stall); 75 bool stall);
76 76
77/* nonblocking commit helpers */
78int drm_atomic_helper_setup_commit(struct drm_atomic_state *state,
79 bool nonblock);
80void drm_atomic_helper_wait_for_dependencies(struct drm_atomic_state *state);
81void drm_atomic_helper_commit_hw_done(struct drm_atomic_state *state);
82void drm_atomic_helper_commit_cleanup_done(struct drm_atomic_state *state);
83
77/* implementations for legacy interfaces */ 84/* implementations for legacy interfaces */
78int drm_atomic_helper_update_plane(struct drm_plane *plane, 85int drm_atomic_helper_update_plane(struct drm_plane *plane,
79 struct drm_crtc *crtc, 86 struct drm_crtc *crtc,