* Atomic Check and Update
*/
-/*
- * Atomic hardware plane allocator
- *
- * The hardware plane allocator is solely based on the atomic plane states
- * without keeping any external state to avoid races between .atomic_check()
- * and .atomic_commit().
- *
- * The core idea is to avoid using a free planes bitmask that would need to be
- * shared between check and commit handlers with a collective knowledge based on
- * the allocated hardware plane(s) for each KMS plane. The allocator then loops
- * over all plane states to compute the free planes bitmask, allocates hardware
- * planes based on that bitmask, and stores the result back in the plane states.
- *
- * For this to work we need to access the current state of planes not touched by
- * the atomic update. To ensure that it won't be modified, we need to lock all
- * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
- * updates from .atomic_check() up to completion (when swapping the states if
- * the check step has succeeded) or rollback (when freeing the states if the
- * check step has failed).
- *
- * Allocation is performed in the .atomic_check() handler and applied
- * automatically when the core swaps the old and new states.
- */
-
-static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane,
- struct rcar_du_plane_state *new_state)
-{
- struct rcar_du_plane_state *cur_state;
-
- cur_state = to_rcar_plane_state(plane->plane.state);
-
- /* Lowering the number of planes doesn't strictly require reallocation
- * as the extra hardware plane will be freed when committing, but doing
- * so could lead to more fragmentation.
- */
- if (!cur_state->format ||
- cur_state->format->planes != new_state->format->planes)
- return true;
-
- /* Reallocate hardware planes if the source has changed. */
- if (cur_state->source != new_state->source)
- return true;
-
- return false;
-}
-
-static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
-{
- unsigned int mask;
-
- if (state->hwindex == -1)
- return 0;
-
- mask = 1 << state->hwindex;
- if (state->format->planes == 2)
- mask |= 1 << ((state->hwindex + 1) % 8);
-
- return mask;
-}
-
-/*
- * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
- * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
- * DU0/1 plane 1.
- *
- * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
- * and allocate planes in reverse index order otherwise to ensure maximum
- * availability of planes 0 and 1.
- *
- * The caller is responsible for ensuring that the requested source is
- * compatible with the DU revision.
- */
-static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
- struct rcar_du_plane_state *state,
- unsigned int free)
-{
- unsigned int num_planes = state->format->planes;
- int fixed = -1;
- int i;
-
- if (state->source == RCAR_DU_PLANE_VSPD0) {
- /* VSPD0 feeds plane 0 on DU0/1. */
- if (plane->group->index != 0)
- return -EINVAL;
-
- fixed = 0;
- } else if (state->source == RCAR_DU_PLANE_VSPD1) {
- /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
- fixed = plane->group->index == 0 ? 1 : 0;
- }
-
- if (fixed >= 0)
- return free & (1 << fixed) ? fixed : -EBUSY;
-
- for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
- if (!(free & (1 << i)))
- continue;
-
- if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
- break;
- }
-
- return i < 0 ? -EBUSY : i;
-}
-
static int rcar_du_atomic_check(struct drm_device *dev,
struct drm_atomic_state *state)
{
- struct rcar_du_device *rcdu = dev->dev_private;
- unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
- unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
- bool needs_realloc = false;
- unsigned int groups = 0;
- unsigned int i;
int ret;
ret = drm_atomic_helper_check(dev, state);
if (ret < 0)
return ret;
- /* Check if hardware planes need to be reallocated. */
- for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
- struct rcar_du_plane_state *plane_state;
- struct rcar_du_plane *plane;
- unsigned int index;
-
- if (!state->planes[i])
- continue;
-
- plane = to_rcar_plane(state->planes[i]);
- plane_state = to_rcar_plane_state(state->plane_states[i]);
-
- dev_dbg(rcdu->dev, "%s: checking plane (%u,%u)\n", __func__,
- plane->group->index, plane - plane->group->planes);
-
- /* If the plane is being disabled we don't need to go through
- * the full reallocation procedure. Just mark the hardware
- * plane(s) as freed.
- */
- if (!plane_state->format) {
- dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
- __func__);
- index = plane - plane->group->planes;
- group_freed_planes[plane->group->index] |= 1 << index;
- plane_state->hwindex = -1;
- continue;
- }
-
- /* If the plane needs to be reallocated mark it as such, and
- * mark the hardware plane(s) as free.
- */
- if (rcar_du_plane_needs_realloc(plane, plane_state)) {
- dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
- __func__);
- groups |= 1 << plane->group->index;
- needs_realloc = true;
-
- index = plane - plane->group->planes;
- group_freed_planes[plane->group->index] |= 1 << index;
- plane_state->hwindex = -1;
- }
- }
-
- if (!needs_realloc)
- return 0;
-
- /* Grab all plane states for the groups that need reallocation to ensure
- * locking and avoid racy updates. This serializes the update operation,
- * but there's not much we can do about it as that's the hardware
- * design.
- *
- * Compute the used planes mask for each group at the same time to avoid
- * looping over the planes separately later.
- */
- while (groups) {
- unsigned int index = ffs(groups) - 1;
- struct rcar_du_group *group = &rcdu->groups[index];
- unsigned int used_planes = 0;
-
- dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
- __func__, index);
-
- for (i = 0; i < group->num_planes; ++i) {
- struct rcar_du_plane *plane = &group->planes[i];
- struct rcar_du_plane_state *plane_state;
- struct drm_plane_state *s;
-
- s = drm_atomic_get_plane_state(state, &plane->plane);
- if (IS_ERR(s))
- return PTR_ERR(s);
-
- /* If the plane has been freed in the above loop its
- * hardware planes must not be added to the used planes
- * bitmask. However, the current state doesn't reflect
- * the free state yet, as we've modified the new state
- * above. Use the local freed planes list to check for
- * that condition instead.
- */
- if (group_freed_planes[index] & (1 << i)) {
- dev_dbg(rcdu->dev,
- "%s: plane (%u,%u) has been freed, skipping\n",
- __func__, plane->group->index,
- plane - plane->group->planes);
- continue;
- }
-
- plane_state = to_rcar_plane_state(plane->plane.state);
- used_planes |= rcar_du_plane_hwmask(plane_state);
-
- dev_dbg(rcdu->dev,
- "%s: plane (%u,%u) uses %u hwplanes (index %d)\n",
- __func__, plane->group->index,
- plane - plane->group->planes,
- plane_state->format ?
- plane_state->format->planes : 0,
- plane_state->hwindex);
- }
-
- group_free_planes[index] = 0xff & ~used_planes;
- groups &= ~(1 << index);
-
- dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
- __func__, index, group_free_planes[index]);
- }
-
- /* Reallocate hardware planes for each plane that needs it. */
- for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
- struct rcar_du_plane_state *plane_state;
- struct rcar_du_plane *plane;
- unsigned int crtc_planes;
- unsigned int free;
- int idx;
-
- if (!state->planes[i])
- continue;
-
- plane = to_rcar_plane(state->planes[i]);
- plane_state = to_rcar_plane_state(state->plane_states[i]);
-
- dev_dbg(rcdu->dev, "%s: allocating plane (%u,%u)\n", __func__,
- plane->group->index, plane - plane->group->planes);
-
- /* Skip planes that are being disabled or don't need to be
- * reallocated.
- */
- if (!plane_state->format ||
- !rcar_du_plane_needs_realloc(plane, plane_state))
- continue;
-
- /* Try to allocate the plane from the free planes currently
- * associated with the target CRTC to avoid restarting the CRTC
- * group and thus minimize flicker. If it fails fall back to
- * allocating from all free planes.
- */
- crtc_planes = to_rcar_crtc(plane_state->state.crtc)->index % 2
- ? plane->group->dptsr_planes
- : ~plane->group->dptsr_planes;
- free = group_free_planes[plane->group->index];
-
- idx = rcar_du_plane_hwalloc(plane, plane_state,
- free & crtc_planes);
- if (idx < 0)
- idx = rcar_du_plane_hwalloc(plane, plane_state,
- free);
- if (idx < 0) {
- dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
- __func__);
- return idx;
- }
-
- dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
- __func__, plane_state->format->planes, idx);
-
- plane_state->hwindex = idx;
-
- group_free_planes[plane->group->index] &=
- ~rcar_du_plane_hwmask(plane_state);
-
- dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
- __func__, plane->group->index,
- group_free_planes[plane->group->index]);
- }
-
- return 0;
+ return rcar_du_atomic_check_planes(dev, state);
}
struct rcar_du_commit {
*/
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
#include "rcar_du_plane.h"
#include "rcar_du_regs.h"
+/* -----------------------------------------------------------------------------
+ * Atomic hardware plane allocator
+ *
+ * The hardware plane allocator is solely based on the atomic plane states
+ * without keeping any external state to avoid races between .atomic_check()
+ * and .atomic_commit().
+ *
+ * The core idea is to avoid using a free planes bitmask that would need to be
+ * shared between check and commit handlers with a collective knowledge based on
+ * the allocated hardware plane(s) for each KMS plane. The allocator then loops
+ * over all plane states to compute the free planes bitmask, allocates hardware
+ * planes based on that bitmask, and stores the result back in the plane states.
+ *
+ * For this to work we need to access the current state of planes not touched by
+ * the atomic update. To ensure that it won't be modified, we need to lock all
+ * planes using drm_atomic_get_plane_state(). This effectively serializes atomic
+ * updates from .atomic_check() up to completion (when swapping the states if
+ * the check step has succeeded) or rollback (when freeing the states if the
+ * check step has failed).
+ *
+ * Allocation is performed in the .atomic_check() handler and applied
+ * automatically when the core swaps the old and new states.
+ */
+
+static bool rcar_du_plane_needs_realloc(struct rcar_du_plane *plane,
+ struct rcar_du_plane_state *new_state)
+{
+ struct rcar_du_plane_state *cur_state;
+
+ cur_state = to_rcar_plane_state(plane->plane.state);
+
+ /* Lowering the number of planes doesn't strictly require reallocation
+ * as the extra hardware plane will be freed when committing, but doing
+ * so could lead to more fragmentation.
+ */
+ if (!cur_state->format ||
+ cur_state->format->planes != new_state->format->planes)
+ return true;
+
+ /* Reallocate hardware planes if the source has changed. */
+ if (cur_state->source != new_state->source)
+ return true;
+
+ return false;
+}
+
+static unsigned int rcar_du_plane_hwmask(struct rcar_du_plane_state *state)
+{
+ unsigned int mask;
+
+ if (state->hwindex == -1)
+ return 0;
+
+ mask = 1 << state->hwindex;
+ if (state->format->planes == 2)
+ mask |= 1 << ((state->hwindex + 1) % 8);
+
+ return mask;
+}
+
+/*
+ * The R8A7790 DU can source frames directly from the VSP1 devices VSPD0 and
+ * VSPD1. VSPD0 feeds DU0/1 plane 0, and VSPD1 feeds either DU2 plane 0 or
+ * DU0/1 plane 1.
+ *
+ * Allocate the correct fixed plane when sourcing frames from VSPD0 or VSPD1,
+ * and allocate planes in reverse index order otherwise to ensure maximum
+ * availability of planes 0 and 1.
+ *
+ * The caller is responsible for ensuring that the requested source is
+ * compatible with the DU revision.
+ */
+static int rcar_du_plane_hwalloc(struct rcar_du_plane *plane,
+ struct rcar_du_plane_state *state,
+ unsigned int free)
+{
+ unsigned int num_planes = state->format->planes;
+ int fixed = -1;
+ int i;
+
+ if (state->source == RCAR_DU_PLANE_VSPD0) {
+ /* VSPD0 feeds plane 0 on DU0/1. */
+ if (plane->group->index != 0)
+ return -EINVAL;
+
+ fixed = 0;
+ } else if (state->source == RCAR_DU_PLANE_VSPD1) {
+ /* VSPD1 feeds plane 1 on DU0/1 or plane 0 on DU2. */
+ fixed = plane->group->index == 0 ? 1 : 0;
+ }
+
+ if (fixed >= 0)
+ return free & (1 << fixed) ? fixed : -EBUSY;
+
+ for (i = RCAR_DU_NUM_HW_PLANES - 1; i >= 0; --i) {
+ if (!(free & (1 << i)))
+ continue;
+
+ if (num_planes == 1 || free & (1 << ((i + 1) % 8)))
+ break;
+ }
+
+ return i < 0 ? -EBUSY : i;
+}
+
+int rcar_du_atomic_check_planes(struct drm_device *dev,
+ struct drm_atomic_state *state)
+{
+ struct rcar_du_device *rcdu = dev->dev_private;
+ unsigned int group_freed_planes[RCAR_DU_MAX_GROUPS] = { 0, };
+ unsigned int group_free_planes[RCAR_DU_MAX_GROUPS] = { 0, };
+ bool needs_realloc = false;
+ unsigned int groups = 0;
+ unsigned int i;
+
+ /* Check if hardware planes need to be reallocated. */
+ for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
+ struct rcar_du_plane_state *plane_state;
+ struct rcar_du_plane *plane;
+ unsigned int index;
+
+ if (!state->planes[i])
+ continue;
+
+ plane = to_rcar_plane(state->planes[i]);
+ plane_state = to_rcar_plane_state(state->plane_states[i]);
+
+ dev_dbg(rcdu->dev, "%s: checking plane (%u,%u)\n", __func__,
+ plane->group->index, plane - plane->group->planes);
+
+ /* If the plane is being disabled we don't need to go through
+ * the full reallocation procedure. Just mark the hardware
+ * plane(s) as freed.
+ */
+ if (!plane_state->format) {
+ dev_dbg(rcdu->dev, "%s: plane is being disabled\n",
+ __func__);
+ index = plane - plane->group->planes;
+ group_freed_planes[plane->group->index] |= 1 << index;
+ plane_state->hwindex = -1;
+ continue;
+ }
+
+ /* If the plane needs to be reallocated mark it as such, and
+ * mark the hardware plane(s) as free.
+ */
+ if (rcar_du_plane_needs_realloc(plane, plane_state)) {
+ dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
+ __func__);
+ groups |= 1 << plane->group->index;
+ needs_realloc = true;
+
+ index = plane - plane->group->planes;
+ group_freed_planes[plane->group->index] |= 1 << index;
+ plane_state->hwindex = -1;
+ }
+ }
+
+ if (!needs_realloc)
+ return 0;
+
+ /* Grab all plane states for the groups that need reallocation to ensure
+ * locking and avoid racy updates. This serializes the update operation,
+ * but there's not much we can do about it as that's the hardware
+ * design.
+ *
+ * Compute the used planes mask for each group at the same time to avoid
+ * looping over the planes separately later.
+ */
+ while (groups) {
+ unsigned int index = ffs(groups) - 1;
+ struct rcar_du_group *group = &rcdu->groups[index];
+ unsigned int used_planes = 0;
+
+ dev_dbg(rcdu->dev, "%s: finding free planes for group %u\n",
+ __func__, index);
+
+ for (i = 0; i < group->num_planes; ++i) {
+ struct rcar_du_plane *plane = &group->planes[i];
+ struct rcar_du_plane_state *plane_state;
+ struct drm_plane_state *s;
+
+ s = drm_atomic_get_plane_state(state, &plane->plane);
+ if (IS_ERR(s))
+ return PTR_ERR(s);
+
+ /* If the plane has been freed in the above loop its
+ * hardware planes must not be added to the used planes
+ * bitmask. However, the current state doesn't reflect
+ * the free state yet, as we've modified the new state
+ * above. Use the local freed planes list to check for
+ * that condition instead.
+ */
+ if (group_freed_planes[index] & (1 << i)) {
+ dev_dbg(rcdu->dev,
+ "%s: plane (%u,%u) has been freed, skipping\n",
+ __func__, plane->group->index,
+ plane - plane->group->planes);
+ continue;
+ }
+
+ plane_state = to_rcar_plane_state(plane->plane.state);
+ used_planes |= rcar_du_plane_hwmask(plane_state);
+
+ dev_dbg(rcdu->dev,
+ "%s: plane (%u,%u) uses %u hwplanes (index %d)\n",
+ __func__, plane->group->index,
+ plane - plane->group->planes,
+ plane_state->format ?
+ plane_state->format->planes : 0,
+ plane_state->hwindex);
+ }
+
+ group_free_planes[index] = 0xff & ~used_planes;
+ groups &= ~(1 << index);
+
+ dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
+ __func__, index, group_free_planes[index]);
+ }
+
+ /* Reallocate hardware planes for each plane that needs it. */
+ for (i = 0; i < dev->mode_config.num_total_plane; ++i) {
+ struct rcar_du_plane_state *plane_state;
+ struct rcar_du_plane *plane;
+ unsigned int crtc_planes;
+ unsigned int free;
+ int idx;
+
+ if (!state->planes[i])
+ continue;
+
+ plane = to_rcar_plane(state->planes[i]);
+ plane_state = to_rcar_plane_state(state->plane_states[i]);
+
+ dev_dbg(rcdu->dev, "%s: allocating plane (%u,%u)\n", __func__,
+ plane->group->index, plane - plane->group->planes);
+
+ /* Skip planes that are being disabled or don't need to be
+ * reallocated.
+ */
+ if (!plane_state->format ||
+ !rcar_du_plane_needs_realloc(plane, plane_state))
+ continue;
+
+ /* Try to allocate the plane from the free planes currently
+ * associated with the target CRTC to avoid restarting the CRTC
+ * group and thus minimize flicker. If it fails fall back to
+ * allocating from all free planes.
+ */
+ crtc_planes = to_rcar_crtc(plane_state->state.crtc)->index % 2
+ ? plane->group->dptsr_planes
+ : ~plane->group->dptsr_planes;
+ free = group_free_planes[plane->group->index];
+
+ idx = rcar_du_plane_hwalloc(plane, plane_state,
+ free & crtc_planes);
+ if (idx < 0)
+ idx = rcar_du_plane_hwalloc(plane, plane_state,
+ free);
+ if (idx < 0) {
+ dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
+ __func__);
+ return idx;
+ }
+
+ dev_dbg(rcdu->dev, "%s: allocated %u hwplanes (index %u)\n",
+ __func__, plane_state->format->planes, idx);
+
+ plane_state->hwindex = idx;
+
+ group_free_planes[plane->group->index] &=
+ ~rcar_du_plane_hwmask(plane_state);
+
+ dev_dbg(rcdu->dev, "%s: group %u free planes mask 0x%02x\n",
+ __func__, plane->group->index,
+ group_free_planes[plane->group->index]);
+ }
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Plane Setup
+ */
+
#define RCAR_DU_COLORKEY_NONE (0 << 24)
#define RCAR_DU_COLORKEY_SOURCE (1 << 24)
#define RCAR_DU_COLORKEY_MASK (1 << 24)