/* ----------------------------------------------------------------------------- * 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.
*/
staticbool rcar_du_plane_needs_realloc( conststruct rcar_du_plane_state *old_state, conststruct rcar_du_plane_state *new_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 (!old_state->format ||
old_state->format->planes != new_state->format->planes) returntrue;
/* Reallocate hardware planes if the source has changed. */ if (old_state->source != new_state->source) returntrue;
/* * 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.
*/ staticint rcar_du_plane_hwalloc(struct rcar_du_plane *plane, struct rcar_du_plane_state *state, unsignedint free)
{ unsignedint 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;
/* * 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 (!new_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;
new_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(old_plane_state, new_plane_state)) {
dev_dbg(rcdu->dev, "%s: plane needs reallocation\n",
__func__);
groups |= 1 << plane->group->index;
needs_realloc = true;
/* * 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) { unsignedint index = ffs(groups) - 1; struct rcar_du_group *group = &rcdu->groups[index]; unsignedint 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 *new_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,%tu) has been freed, skipping\n",
__func__, plane->group->index,
plane - plane->group->planes); continue;
}
/* * Skip planes that are being disabled or don't need to be * reallocated.
*/ if (!new_plane_state->format ||
!rcar_du_plane_needs_realloc(old_plane_state, new_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(new_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, new_plane_state,
free & crtc_planes); if (idx < 0)
idx = rcar_du_plane_hwalloc(plane, new_plane_state,
free); if (idx < 0) {
dev_dbg(rcdu->dev, "%s: no available hardware plane\n",
__func__); return idx;
}
/* * Memory pitch (expressed in pixels). Must be doubled for interlaced * operation with 32bpp formats.
*/
rcar_du_plane_write(rgrp, index, PnMWR,
(interlaced && state->format->bpp == 32) ?
pitch * 2 : pitch);
/* * The Y position is expressed in raster line units and must be doubled * for 32bpp formats, according to the R8A7790 datasheet. No mention of * doubling the Y position is found in the R8A7779 datasheet, but the * rule seems to apply there as well. * * Despite not being documented, doubling seem not to be needed when * operating in interlaced mode. * * Similarly, for the second plane, NV12 and NV21 formats seem to * require a halved Y position value, in both progressive and interlaced * modes.
*/
rcar_du_plane_write(rgrp, index, PnSPXR, src_x);
rcar_du_plane_write(rgrp, index, PnSPYR, src_y *
(!interlaced && state->format->bpp == 32 ? 2 : 1));
/* * The PnALPHAR register controls alpha-blending in 16bpp formats * (ARGB1555 and XRGB1555). * * For ARGB, set the alpha value to 0, and enable alpha-blending when * the A bit is 0. This maps A=0 to alpha=0 and A=1 to alpha=255. * * For XRGB, set the alpha value to the plane-wide alpha value and * enable alpha-blending regardless of the X bit value.
*/ if (state->format->fourcc != DRM_FORMAT_XRGB1555)
rcar_du_plane_write(rgrp, index, PnALPHAR, PnALPHAR_ABIT_0); else
rcar_du_plane_write(rgrp, index, PnALPHAR,
PnALPHAR_ABIT_X | state->state.alpha >> 8);
pnmr = PnMR_BM_MD | state->format->pnmr;
/* * Disable color keying when requested. YUV formats have the * PnMR_SPIM_TP_OFF bit set in their pnmr field, disabling color keying * automatically.
*/ if ((state->colorkey & RCAR_DU_COLORKEY_MASK) == RCAR_DU_COLORKEY_NONE)
pnmr |= PnMR_SPIM_TP_OFF;
/* For packed YUV formats we need to select the U/V order. */ if (state->format->fourcc == DRM_FORMAT_YUYV)
pnmr |= PnMR_YCDF_YUYV;
if (rcdu->info->features & RCAR_DU_FEATURE_NO_BLENDING) { /* No blending. ALP and EOR are not supported. */
pnmr &= ~(PnMR_SPIM_ALP | PnMR_SPIM_EOR);
}
/* * On Gen3, some DU channels have two planes, each being wired to a * separate VSPD instance. The DU can then blend two planes. While * this feature isn't used by the driver, issues related to alpha * blending (such as incorrect colors or planes being invisible) may * still occur if the PnALPHAR register has a stale value. Set the * register to 0 to avoid this.
*/
if (!state->crtc) { /* * The visible field is not reset by the DRM core but only * updated by drm_atomic_helper_check_plane_state(), set it * manually.
*/
state->visible = false;
*format = NULL; return 0;
}
crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc); if (IS_ERR(crtc_state)) return PTR_ERR(crtc_state);
ret = drm_atomic_helper_check_plane_state(state, crtc_state,
DRM_PLANE_NO_SCALING,
DRM_PLANE_NO_SCALING, true, true); if (ret < 0) return ret;
if (!state->visible) {
*format = NULL; return 0;
}
*format = rcar_du_format_info(state->fb->format->format); if (*format == NULL) {
dev_dbg(dev->dev, "%s: unsupported format %p4cc\n", __func__,
&state->fb->format->format); return -EINVAL;
}
/* * Check whether the source has changed from memory to live source or * from live source to memory. The source has been configured by the * VSPS bit in the PnDDCR4 register. Although the datasheet states that * the bit is updated during vertical blanking, it seems that updates * only occur when the DU group is held in reset through the DSYSR.DRES * bit. We thus need to restart the group if the source changes.
*/
old_rstate = to_rcar_plane_state(old_state);
new_rstate = to_rcar_plane_state(new_state);
for (i = 0; i < rgrp->num_planes; ++i) { enum drm_plane_type type = i < rgrp->num_crtcs
? DRM_PLANE_TYPE_PRIMARY
: DRM_PLANE_TYPE_OVERLAY; struct rcar_du_plane *plane = &rgrp->planes[i];
plane->group = rgrp;
ret = drm_universal_plane_init(&rcdu->ddev, &plane->plane,
crtcs, &rcar_du_plane_funcs,
formats, ARRAY_SIZE(formats),
NULL, type, NULL); if (ret < 0) return ret;
if (type == DRM_PLANE_TYPE_PRIMARY)
drm_plane_helper_add(&plane->plane,
&rcar_du_primary_plane_helper_funcs); else
drm_plane_helper_add(&plane->plane,
&rcar_du_plane_helper_funcs);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.