@@ -643,6 +643,73 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc,
return 0;
}
+static bool drm_atomic_async_check(struct drm_atomic_state *state)
+{
+ struct drm_crtc *crtc;
+ struct drm_crtc_state *crtc_state;
+ struct drm_crtc_commit *commit;
+ struct drm_plane *__plane, *plane = NULL;
+ struct drm_plane_state *__plane_state, *plane_state = NULL;
+ const struct drm_plane_helper_funcs *funcs;
+ int i, j, n_planes = 0;
+
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+ if (drm_atomic_crtc_needs_modeset(crtc_state))
+ return false;
+ }
+
+ for_each_new_plane_in_state(state, __plane, __plane_state, i) {
+ n_planes++;
+ plane = __plane;
+ plane_state = __plane_state;
+ }
+
+ /* FIXME: we support only single plane updates for now */
+ if (!plane || n_planes != 1)
+ return false;
+
+ if (!plane_state->crtc)
+ return false;
+
+ funcs = plane->helper_private;
+ if (!funcs->atomic_async_update)
+ return false;
+
+ if (plane_state->fence)
+ return false;
+
+ /*
+ * 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.
+ */
+ for_each_new_crtc_in_state(state, crtc, crtc_state, i) {
+ if (plane->crtc != crtc)
+ continue;
+
+ spin_lock(&crtc->commit_lock);
+ commit = list_first_entry_or_null(&crtc->commit_list,
+ struct drm_crtc_commit,
+ commit_entry);
+ if (!commit) {
+ spin_unlock(&crtc->commit_lock);
+ continue;
+ }
+ spin_unlock(&crtc->commit_lock);
+
+ if (!crtc->state->state)
+ continue;
+
+ for_each_plane_in_state(crtc->state->state, __plane,
+ __plane_state, j) {
+ if (__plane == plane)
+ return false;
+ }
+ }
+
+ return !funcs->atomic_async_check(plane, plane_state);
+}
+
static void drm_atomic_crtc_print_state(struct drm_printer *p,
const struct drm_crtc_state *state)
{
@@ -1664,6 +1731,9 @@ int drm_atomic_check_only(struct drm_atomic_state *state)
}
}
+ if (state->legacy_cursor_update)
+ state->async_update = drm_atomic_async_check(state);
+
return ret;
}
EXPORT_SYMBOL(drm_atomic_check_only);
@@ -1305,6 +1305,30 @@ static void commit_work(struct work_struct *work)
}
/**
+ * drm_atomic_helper_async_commit - commit state asynchronously
+ *
+ * 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.
+ */
+void drm_atomic_helper_async_commit(struct drm_device *dev,
+ struct drm_atomic_state *state)
+{
+ struct drm_plane *plane;
+ struct drm_plane_state *plane_state;
+ const struct drm_plane_helper_funcs *funcs;
+ int i;
+
+ for_each_new_plane_in_state(state, plane, plane_state, i) {
+ funcs = plane->helper_private;
+ funcs->atomic_async_update(plane, plane_state);
+ }
+}
+EXPORT_SYMBOL(drm_atomic_helper_async_commit);
+
+/**
* drm_atomic_helper_commit - commit validated state object
* @dev: DRM device
* @state: the driver state object
@@ -1328,6 +1352,17 @@ int drm_atomic_helper_commit(struct drm_device *dev,
{
int ret;
+ if (state->async_update) {
+ ret = drm_atomic_helper_prepare_planes(dev, state);
+ if (ret)
+ return ret;
+
+ drm_atomic_helper_async_commit(dev, state);
+ drm_atomic_helper_cleanup_planes(dev, state);
+
+ return 0;
+ }
+
ret = drm_atomic_helper_setup_commit(state, nonblock);
if (ret)
return ret;
@@ -207,6 +207,7 @@ struct __drm_private_objs_state {
* @dev: parent DRM device
* @allow_modeset: allow full modeset
* @legacy_cursor_update: hint to enforce legacy cursor IOCTL semantics
+ * @async_update: hint for asynchronous 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
@@ -221,6 +222,7 @@ struct drm_atomic_state {
struct drm_device *dev;
bool allow_modeset : 1;
bool legacy_cursor_update : 1;
+ bool async_update : 1;
struct __drm_planes_state *planes;
struct __drm_crtcs_state *crtcs;
int num_connector;
@@ -44,6 +44,8 @@ void drm_atomic_helper_commit_tail(struct drm_atomic_state *state);
int drm_atomic_helper_commit(struct drm_device *dev,
struct drm_atomic_state *state,
bool nonblock);
+void drm_atomic_helper_async_commit(struct drm_device *dev,
+ struct drm_atomic_state *state);
int drm_atomic_helper_wait_for_fences(struct drm_device *dev,
struct drm_atomic_state *state,
@@ -1129,6 +1129,56 @@ struct drm_plane_helper_funcs {
*/
void (*atomic_disable)(struct drm_plane *plane,
struct drm_plane_state *old_state);
+
+ /**
+ * @atomic_async_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".
+ *
+ * This hook is called by drm_atomic_async_check() to establish if a
+ * given update can be committed asynchronously, 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.
+ */
+ int (*atomic_async_check)(struct drm_plane *plane,
+ struct drm_plane_state *state);
+
+ /**
+ * @atomic_async_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".
+ *
+ * This hook is called by drm_atomic_helper_async_commit().
+ *
+ * An async update will happen on legacy cursor updates. An async
+ * 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
+ * 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
+ * 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,
+ struct drm_plane_state *new_state);
};
/**