diff --git a/Documentation/gpu/drm-kms-helpers.rst b/Documentation/gpu/drm-kms-helpers.rst index 58b375e47615b7852b97c0b8259e51846440a24c..c067a196902dbbc1c9f624e5c331f03f6efb30a4 100644 --- a/Documentation/gpu/drm-kms-helpers.rst +++ b/Documentation/gpu/drm-kms-helpers.rst @@ -53,12 +53,18 @@ Overview .. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c :doc: overview -Implementing Asynchronous Atomic Commit +Implementing Nonblocking Atomic Commit --------------------------------------- .. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c :doc: implementing nonblocking commit +Amend Mode Atomic Commit +------------------------ + +.. kernel-doc:: drivers/gpu/drm/drm_atomic_helper.c + :doc: amend mode atomic commit + Helper Functions Reference -------------------------- diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 2f26581b93ff5c4bacf77f3ca9d5619a2d979e32..711e7715e1120cfcfeaad045d7bddaa14710c293 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -3779,8 +3779,12 @@ static const struct drm_plane_helper_funcs dm_plane_helper_funcs = { .prepare_fb = dm_plane_helper_prepare_fb, .cleanup_fb = dm_plane_helper_cleanup_fb, .atomic_check = dm_plane_atomic_check, - .atomic_async_check = dm_plane_atomic_async_check, - .atomic_async_update = dm_plane_atomic_async_update + /* + * FIXME: ideally, instead of hooking async updates to amend, + * we could avoid tearing by modifying the pending commit. + */ + .atomic_amend_check = dm_plane_atomic_async_check, + .atomic_amend_update = dm_plane_atomic_async_update }; /* @@ -6140,7 +6144,7 @@ static int amdgpu_dm_atomic_check(struct drm_device *dev, * helper, check if it can be done asynchronously for better * performance. */ - state->async_update = !drm_atomic_helper_async_check(dev, state); + state->amend_update = !drm_atomic_helper_amend_check(dev, state); } /* Must be success */ diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index 2453678d11869b899483a47924a04ea6149fc0c6..eb5dcd84fea7aa22d48cc581dc5cfab1644e702c 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -948,7 +948,7 @@ int drm_atomic_helper_check(struct drm_device *dev, return ret; if (state->legacy_cursor_update) - state->async_update = !drm_atomic_helper_async_check(dev, state); + state->amend_update = !drm_atomic_helper_amend_check(dev, state); return ret; } @@ -1569,19 +1569,68 @@ static void commit_work(struct work_struct *work) } /** - * drm_atomic_helper_async_check - check if state can be commited asynchronously + * DOC: amend mode atomic commit + * + * The amend feature provides a way to perform 1000 updates to be applied as + * soon as possible without waiting for 1000 vblanks. + + * Currently, only the legacy cursor update uses amend mode, where historically, + * userspace performs several updates before the next vblank and don't want to + * see a delay in the cursor's movement. + * If amend is not supported, legacy cursor falls back to a normal sync update. + * + * To implement the legacy cursor update, drivers should provide + * &drm_plane_helper_funcs.atomic_amend_check() and + * &drm_plane_helper_funcs.atomic_amend_update() + * + * Drivers just need to make sure the last state overrides the previous one, so + * that if X updates were performed, then, in some point in the near future, + * preferentially in the next vblank, the Xth state will be the hardware state. + * + * If the hardware supports asynchronous update, i.e, changing its state without + * waiting for vblank, then &drm_plane_helper_funcs.atomic_amend_update() can be + * implemented using asynchronous update (the amend mode property is held), but + * it can cause tearing in the image. + * + * Otherwise (if async is not supported by the hw), drivers need to override the + * commit to be applied in the next vblank, and also they need to take care of + * framebuffer references when programming a new framebuffer, as hw can still be + * scanning out the old framebuffer. For now drivers must implement their own + * workers for deferring if needed, until a common solution is created. + * + * + * Notes / highlights: + * + * - amend update is performed on legacy cursor updates. + * + * - amend update won't happen if there is an outstanding commit modifying the + * same plane. + * + * - amend update won't happen if atomic_amend_check() returns false. + * + * - if atomic_amend_check() fails, it falls back to a normal synchronous + * update. + * + * - if userspace wants to ensure an asynchronous page flip, i.e. change hw + * state immediately, see DRM_MODE_PAGE_FLIP_ASYNC flag + * (asynchronous page flip maintains the amend property by definition). + * + * - Asynchronous modeset doesn't make sense, only asynchronous page flip. + */ + +/** + * drm_atomic_helper_amend_check - check if state can be amended. * @dev: DRM device * @state: the driver state object * - * This helper will check if it is possible to commit the state asynchronously. - * Async commits are not supposed to swap the states like normal sync commits - * but just do in-place changes on the current state. + * This helper will check if it is possible perform a commit in amend mode. + * For amend mode definition see :doc: amend mode atomic commit * - * It will return 0 if the commit can happen in an asynchronous fashion or error - * if not. Note that error just mean it can't be commited asynchronously, if it - * fails the commit should be treated like a normal synchronous commit. + * It will return 0 if the commit can happen in an amend fashion or error + * if not. Note that error just mean it can't be committed in amend mode, if it + * fails the commit should be treated like a normal commit. */ -int drm_atomic_helper_async_check(struct drm_device *dev, +int drm_atomic_helper_amend_check(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_crtc *crtc; @@ -1610,7 +1659,7 @@ int drm_atomic_helper_async_check(struct drm_device *dev, /* * FIXME: Since prepare_fb and cleanup_fb are always called on - * the new_plane_state for async updates we need to block framebuffer + * the new_plane_state for amend updates, we need to block framebuffer * changes. This prevents use of a fb that's been cleaned up and * double cleanups from occuring. */ @@ -1618,37 +1667,43 @@ int drm_atomic_helper_async_check(struct drm_device *dev, return -EINVAL; funcs = plane->helper_private; - if (!funcs->atomic_async_update) + if (!funcs->atomic_amend_update) return -EINVAL; if (new_plane_state->fence) return -EINVAL; /* - * Don't do an async update if there is an outstanding commit modifying - * the plane. This prevents our async update's changes from getting - * overridden by a previous synchronous update's state. + * Don't do an amend update if there is an outstanding commit modifying + * the plane. + * TODO: add support for modifying the outstanding commit. */ if (old_plane_state->commit && !try_wait_for_completion(&old_plane_state->commit->hw_done)) return -EBUSY; - return funcs->atomic_async_check(plane, new_plane_state); + return funcs->atomic_amend_check(plane, new_plane_state); } -EXPORT_SYMBOL(drm_atomic_helper_async_check); +EXPORT_SYMBOL(drm_atomic_helper_amend_check); /** - * drm_atomic_helper_async_commit - commit state asynchronously + * drm_atomic_helper_amend_commit - commit state in amend mode * @dev: DRM device * @state: the driver state object * - * This function commits a state asynchronously, i.e., not vblank - * synchronized. It should be used on a state only when - * drm_atomic_async_check() succeeds. Async commits are not supposed to swap - * the states like normal sync commits, but just do in-place changes on the - * current state. + * This function commits a state in amend mode. + * For amend mode definition see :doc: amend mode atomic commit + * + * It should be used on a state only when drm_atomic_amend_check() succeeds. + * + * Amend commits are not supposed to swap the states like normal sync commits, + * but just do in-place changes on the current state. + * + * TODO: instead of doing in-place changes, modify the new_state and perform an + * immediate flip. Drivers could reuse the page_flip code and even use the + * DRM_MODE_PAGE_FLIP_ASYNC flag if the hardware supports asyncronous update. */ -void drm_atomic_helper_async_commit(struct drm_device *dev, +void drm_atomic_helper_amend_commit(struct drm_device *dev, struct drm_atomic_state *state) { struct drm_plane *plane; @@ -1658,10 +1713,10 @@ void drm_atomic_helper_async_commit(struct drm_device *dev, for_each_new_plane_in_state(state, plane, plane_state, i) { funcs = plane->helper_private; - funcs->atomic_async_update(plane, plane_state); + funcs->atomic_amend_update(plane, plane_state); /* - * ->atomic_async_update() is supposed to update the + * ->atomic_amend_update() is supposed to update the * plane->state in-place, make sure at least common * properties have been properly updated. */ @@ -1672,7 +1727,7 @@ void drm_atomic_helper_async_commit(struct drm_device *dev, WARN_ON_ONCE(plane->state->src_y != plane_state->src_y); } } -EXPORT_SYMBOL(drm_atomic_helper_async_commit); +EXPORT_SYMBOL(drm_atomic_helper_amend_commit); /** * drm_atomic_helper_commit - commit validated state object @@ -1698,12 +1753,12 @@ int drm_atomic_helper_commit(struct drm_device *dev, { int ret; - if (state->async_update) { + if (state->amend_update) { ret = drm_atomic_helper_prepare_planes(dev, state); if (ret) return ret; - drm_atomic_helper_async_commit(dev, state); + drm_atomic_helper_amend_commit(dev, state); drm_atomic_helper_cleanup_planes(dev, state); return 0; diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c index be13140967b4e8a294263ed2632673aee73368b0..814e8230cec6f7d635971b99fbe320c7d28981cf 100644 --- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c +++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_plane.c @@ -531,8 +531,12 @@ static const struct drm_plane_helper_funcs mdp5_plane_helper_funcs = { .cleanup_fb = mdp5_plane_cleanup_fb, .atomic_check = mdp5_plane_atomic_check, .atomic_update = mdp5_plane_atomic_update, - .atomic_async_check = mdp5_plane_atomic_async_check, - .atomic_async_update = mdp5_plane_atomic_async_update, + /* + * FIXME: ideally, instead of hooking async updates to amend, + * we could avoid tearing by modifying the pending commit. + */ + .atomic_amend_check = mdp5_plane_atomic_async_check, + .atomic_amend_update = mdp5_plane_atomic_async_update, }; static void set_scanout_locked(struct mdp5_kms *mdp5_kms, diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c index a7cbf6c9a1531722b79e044bed478d6bdc724a4f..216ad76118dc4495b826cce0102943af3449ea85 100644 --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c @@ -877,7 +877,7 @@ static void vop_plane_atomic_update(struct drm_plane *plane, spin_unlock(&vop->reg_lock); } -static int vop_plane_atomic_async_check(struct drm_plane *plane, +static int vop_plane_atomic_amend_check(struct drm_plane *plane, struct drm_plane_state *state) { struct vop_win *vop_win = to_vop_win(plane); @@ -908,7 +908,7 @@ static int vop_plane_atomic_async_check(struct drm_plane *plane, true, true); } -static void vop_plane_atomic_async_update(struct drm_plane *plane, +static void vop_plane_atomic_amend_update(struct drm_plane *plane, struct drm_plane_state *new_state) { struct vop *vop = to_vop(plane->state->crtc); @@ -952,8 +952,8 @@ static const struct drm_plane_helper_funcs plane_helper_funcs = { .atomic_check = vop_plane_atomic_check, .atomic_update = vop_plane_atomic_update, .atomic_disable = vop_plane_atomic_disable, - .atomic_async_check = vop_plane_atomic_async_check, - .atomic_async_update = vop_plane_atomic_async_update, + .atomic_amend_check = vop_plane_atomic_amend_check, + .atomic_amend_update = vop_plane_atomic_amend_update, .prepare_fb = drm_gem_fb_prepare_fb, }; diff --git a/drivers/gpu/drm/vc4/vc4_kms.c b/drivers/gpu/drm/vc4/vc4_kms.c index 295dacc8bcb939ddf94e0b1dc4d447fd98a835ad..229032b1b31507201ab7c6dc35fe5e9c8ad309a2 100644 --- a/drivers/gpu/drm/vc4/vc4_kms.c +++ b/drivers/gpu/drm/vc4/vc4_kms.c @@ -216,7 +216,7 @@ static int vc4_atomic_commit(struct drm_device *dev, struct vc4_dev *vc4 = to_vc4_dev(dev); int ret; - if (state->async_update) { + if (state->amend_update) { ret = down_interruptible(&vc4->async_modeset); if (ret) return ret; @@ -227,7 +227,7 @@ static int vc4_atomic_commit(struct drm_device *dev, return ret; } - drm_atomic_helper_async_commit(dev, state); + drm_atomic_helper_amend_commit(dev, state); drm_atomic_helper_cleanup_planes(dev, state); diff --git a/drivers/gpu/drm/vc4/vc4_plane.c b/drivers/gpu/drm/vc4/vc4_plane.c index 4d918d3e4858dcc859c4a110adcf9f5fe856e369..ea560000222daba1fc2275724242eda4db35c101 100644 --- a/drivers/gpu/drm/vc4/vc4_plane.c +++ b/drivers/gpu/drm/vc4/vc4_plane.c @@ -1169,8 +1169,12 @@ static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = { .atomic_update = vc4_plane_atomic_update, .prepare_fb = vc4_prepare_fb, .cleanup_fb = vc4_cleanup_fb, - .atomic_async_check = vc4_plane_atomic_async_check, - .atomic_async_update = vc4_plane_atomic_async_update, + /* + * FIXME: ideally, instead of hooking async updates to amend, + * we could avoid tearing by modifying the pending commit. + */ + .atomic_amend_check = vc4_plane_atomic_async_check, + .atomic_amend_update = vc4_plane_atomic_async_update, }; static void vc4_plane_destroy(struct drm_plane *plane) diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h index 824a5ed4e2165b1dcb061f1f809bf4abbabd3a73..b1ced069ffc134d2381c13aef2b0595968477001 100644 --- a/include/drm/drm_atomic.h +++ b/include/drm/drm_atomic.h @@ -300,7 +300,7 @@ struct __drm_private_objs_state { * @ref: count of all references to this state (will not be freed until zero) * @dev: parent DRM device * @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics - * @async_update: hint for asynchronous plane update + * @amend_update: hint for amend plane update * @planes: pointer to array of structures with per-plane data * @crtcs: pointer to array of CRTC pointers * @num_connector: size of the @connectors and @connector_states arrays @@ -328,7 +328,7 @@ struct drm_atomic_state { */ bool allow_modeset : 1; bool legacy_cursor_update : 1; - bool async_update : 1; + bool amend_update : 1; /** * @duplicated: * diff --git a/include/drm/drm_atomic_helper.h b/include/drm/drm_atomic_helper.h index 58214be3bf3d8393418b7294a6b3d3b4414d18a2..8ce0594ccfb9bdcd9da4f5214bacef31905094ac 100644 --- a/include/drm/drm_atomic_helper.h +++ b/include/drm/drm_atomic_helper.h @@ -55,9 +55,9 @@ void drm_atomic_helper_commit_tail_rpm(struct drm_atomic_state *state); int drm_atomic_helper_commit(struct drm_device *dev, struct drm_atomic_state *state, bool nonblock); -int drm_atomic_helper_async_check(struct drm_device *dev, +int drm_atomic_helper_amend_check(struct drm_device *dev, struct drm_atomic_state *state); -void drm_atomic_helper_async_commit(struct drm_device *dev, +void drm_atomic_helper_amend_commit(struct drm_device *dev, struct drm_atomic_state *state); int drm_atomic_helper_wait_for_fences(struct drm_device *dev, diff --git a/include/drm/drm_modeset_helper_vtables.h b/include/drm/drm_modeset_helper_vtables.h index cfb7be40bed7a55a453757b30bf20772590b718d..d92e62dd76c4b5dcb683819db03290564a95d401 100644 --- a/include/drm/drm_modeset_helper_vtables.h +++ b/include/drm/drm_modeset_helper_vtables.h @@ -1136,53 +1136,55 @@ struct drm_plane_helper_funcs { struct drm_plane_state *old_state); /** - * @atomic_async_check: + * @atomic_amend_check: * * Drivers should set this function pointer to check if the plane state - * can be updated in a async fashion. Here async means "not vblank - * synchronized". + * can be updated in amend mode. + * For amend mode definition see :doc: amend mode atomic commit * - * This hook is called by drm_atomic_async_check() to establish if a - * given update can be committed asynchronously, that is, if it can + * This hook is called by drm_atomic_amend_check() to establish if a + * given update can be committed in amend mode, that is, if it can * jump ahead of the state currently queued for update. * * RETURNS: * * Return 0 on success and any error returned indicates that the update - * can not be applied in asynchronous manner. + * can not be applied in amend mode. */ - int (*atomic_async_check)(struct drm_plane *plane, + int (*atomic_amend_check)(struct drm_plane *plane, struct drm_plane_state *state); /** - * @atomic_async_update: + * @atomic_amend_update: * - * Drivers should set this function pointer to perform asynchronous - * updates of planes, that is, jump ahead of the currently queued - * state and update the plane. Here async means "not vblank - * synchronized". + * Drivers should set this function pointer to perform amend + * updates of planes. + * The amend feature provides a way to perform 1000 commits, without + * waiting for 1000 vblanks to get the last state applied. * - * This hook is called by drm_atomic_helper_async_commit(). + * For amend mode definition see :doc: amend mode atomic commit * - * An async update will happen on legacy cursor updates. An async + * This hook is called by drm_atomic_helper_amend_commit(). + * + * An amend update will happen on legacy cursor updates. An amend * update won't happen if there is an outstanding commit modifying * the same plane. * * Note that unlike &drm_plane_helper_funcs.atomic_update this hook - * takes the new &drm_plane_state as parameter. When doing async_update + * takes the new &drm_plane_state as parameter. When doing amend_update * drivers shouldn't replace the &drm_plane_state but update the * current one with the new plane configurations in the new * plane_state. * * FIXME: * - It only works for single plane updates - * - Async Pageflips are not supported yet - * - Some hw might still scan out the old buffer until the next + * - If hardware don't support asyncronous update to implement amend, + * some hw might still scan out the old buffer until the next * vblank, however we let go of the fb references as soon as * we run this hook. For now drivers must implement their own workers * for deferring if needed, until a common solution is created. */ - void (*atomic_async_update)(struct drm_plane *plane, + void (*atomic_amend_update)(struct drm_plane *plane, struct drm_plane_state *new_state); };