// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2015 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com>
*/
/* * flush updates, to make sure hw is updated to new scanout fb, * so that we can safely queue unref to current fb (ie. next * vblank we know hw is done w/ previous scanout_fb).
*/ static u32 crtc_flush_all(struct drm_crtc *crtc)
{ struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_hw_mixer *mixer, *r_mixer; struct drm_plane *plane;
uint32_t flush_mask = 0;
/* this should not happen: */ if (WARN_ON(!mdp5_cstate->ctl)) return 0;
drm_atomic_crtc_for_each_plane(plane, crtc) { if (!plane->state->visible) continue;
flush_mask |= mdp5_plane_get_flush(plane);
}
if (ctl && !crtc->state->enable) { /* set STAGE_UNUSED for all layers */
mdp5_ctl_blend(ctl, pipeline, NULL, NULL, 0, 0); /* XXX: What to do here? */ /* mdp5_crtc->ctl = NULL; */
}
}
staticinline u32 mdp5_lm_use_fg_alpha_mask(enum mdp_mixer_stage_id stage)
{ switch (stage) { case STAGE0: return MDP5_LM_BLEND_COLOR_OUT_STAGE0_FG_ALPHA; case STAGE1: return MDP5_LM_BLEND_COLOR_OUT_STAGE1_FG_ALPHA; case STAGE2: return MDP5_LM_BLEND_COLOR_OUT_STAGE2_FG_ALPHA; case STAGE3: return MDP5_LM_BLEND_COLOR_OUT_STAGE3_FG_ALPHA; case STAGE4: return MDP5_LM_BLEND_COLOR_OUT_STAGE4_FG_ALPHA; case STAGE5: return MDP5_LM_BLEND_COLOR_OUT_STAGE5_FG_ALPHA; case STAGE6: return MDP5_LM_BLEND_COLOR_OUT_STAGE6_FG_ALPHA; default: return 0;
}
}
/* * left/right pipe offsets for the stage array used in blend_setup()
*/ #define PIPE_LEFT 0 #define PIPE_RIGHT 1
/* * blend_setup() - blend all the planes of a CRTC * * If no base layer is available, border will be enabled as the base layer. * Otherwise all layers will be blended based on their stage calculated * in mdp5_crtc_atomic_check.
*/ staticvoid blend_setup(struct drm_crtc *crtc)
{ struct mdp5_crtc *mdp5_crtc = to_mdp5_crtc(crtc); struct mdp5_crtc_state *mdp5_cstate = to_mdp5_crtc_state(crtc->state); struct mdp5_pipeline *pipeline = &mdp5_cstate->pipeline; struct mdp5_kms *mdp5_kms = get_kms(crtc); struct drm_plane *plane; struct mdp5_plane_state *pstate, *pstates[STAGE_MAX + 1] = {NULL}; conststruct msm_format *format; struct mdp5_hw_mixer *mixer = pipeline->mixer;
uint32_t lm = mixer->lm; struct mdp5_hw_mixer *r_mixer = pipeline->r_mixer;
uint32_t r_lm = r_mixer ? r_mixer->lm : 0; struct mdp5_ctl *ctl = mdp5_cstate->ctl;
uint32_t blend_op, fg_alpha, bg_alpha, ctl_blend_flags = 0; unsignedlong flags; enum mdp5_pipe stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { { SSPP_NONE } }; enum mdp5_pipe r_stage[STAGE_MAX + 1][MAX_PIPE_STAGE] = { { SSPP_NONE } }; int i, plane_cnt = 0; bool bg_alpha_enabled = false;
u32 mixer_op_mode = 0;
u32 val; #define blender(stage) ((stage) - STAGE0)
spin_lock_irqsave(&mdp5_crtc->lm_lock, flags);
/* ctl could be released already when we are shutting down: */ /* XXX: Can this happen now? */ if (!ctl) goto out;
/* Collect all plane information */
drm_atomic_crtc_for_each_plane(plane, crtc) { enum mdp5_pipe right_pipe;
if (!plane->state->visible) continue;
pstate = to_mdp5_plane_state(plane->state);
pstates[pstate->stage] = pstate;
stage[pstate->stage][PIPE_LEFT] = mdp5_plane_pipe(plane); /* * if we have a right mixer, stage the same pipe as we * have on the left mixer
*/ if (r_mixer)
r_stage[pstate->stage][PIPE_LEFT] =
mdp5_plane_pipe(plane); /* * if we have a right pipe (i.e, the plane comprises of 2 * hwpipes, then stage the right pipe on the right side of both * the layer mixers
*/
right_pipe = mdp5_plane_right_pipe(plane); if (right_pipe) {
stage[pstate->stage][PIPE_RIGHT] = right_pipe;
r_stage[pstate->stage][PIPE_RIGHT] = right_pipe;
}
plane_cnt++;
}
if (!pstates[STAGE_BASE]) {
ctl_blend_flags |= MDP5_CTL_BLEND_OP_FLAG_BORDER_OUT;
DBG("Border Color is enabled");
} elseif (plane_cnt) {
format = msm_framebuffer_format(pstates[STAGE_BASE]->base.fb);
if (format->alpha_enable)
bg_alpha_enabled = true;
}
/* The reset for blending */ for (i = STAGE0; i <= STAGE_MAX; i++) { if (!pstates[i]) continue;
val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm));
mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm),
val | mixer_op_mode); if (r_mixer) {
val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm));
mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm),
val | mixer_op_mode);
}
/* Assign mixer to LEFT side in source split mode */
val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm));
val &= ~MDP5_LM_BLEND_COLOR_OUT_SPLIT_LEFT_RIGHT;
mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(lm), val);
/* Assign mixer to RIGHT side in source split mode */
val = mdp5_read(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm));
val |= MDP5_LM_BLEND_COLOR_OUT_SPLIT_LEFT_RIGHT;
mdp5_write(mdp5_kms, REG_MDP5_LM_BLEND_COLOR_OUT(r_lm), val);
}
/* * the line counter is 1 at the start of the VSYNC pulse and VTOTAL at * the end of VFP. Translate the porch values relative to the line * counter positions.
*/
if (mdp5_crtc->lm_cursor_enabled) { /* * Restore LM cursor state, as it might have been lost * with suspend:
*/ if (mdp5_crtc->cursor.iova) { unsignedlong flags;
/* * if any plane on this crtc uses 2 hwpipes, then we need * the crtc to have a right hwmixer.
*/ if (pstates[cnt].state->r_hwpipe)
need_right_mixer = true;
cnt++;
if (plane->type == DRM_PLANE_TYPE_CURSOR)
cursor_plane = true;
}
/* bail out early if there aren't any planes */ if (!cnt) return 0;
hw_cfg = mdp5_cfg_get_hw_config(mdp5_kms->cfg);
/* * we need a right hwmixer if the mode's width is greater than a single * LM's max width
*/ if (mode->hdisplay > hw_cfg->lm.max_width)
need_right_mixer = true;
ret = mdp5_crtc_setup_pipeline(crtc, crtc_state, need_right_mixer); if (ret) {
DRM_DEV_ERROR(dev->dev, "couldn't assign mixers %d\n", ret); return ret;
}
/* assign a stage based on sorted zpos property */
sort(pstates, cnt, sizeof(pstates[0]), pstate_cmp, NULL);
/* trigger a warning if cursor isn't the highest zorder */
WARN_ON(cursor_plane &&
(pstates[cnt - 1].plane->type != DRM_PLANE_TYPE_CURSOR));
/* verify that there are not too many planes attached to crtc * and that we don't have conflicting mixer stages:
*/ if ((cnt + start - 1) >= hw_cfg->lm.nb_stages) {
DRM_DEV_ERROR(dev->dev, "too many planes! cnt=%d, start stage=%d\n",
cnt, start); return -EINVAL;
}
for (i = 0; i < cnt; i++) { if (cursor_plane && (i == (cnt - 1)))
pstates[i].state->stage = hw_cfg->lm.nb_stages; else
pstates[i].state->stage = start + i;
DBG("%s: assign pipe %s on stage=%d", crtc->name,
pstates[i].plane->name,
pstates[i].state->stage);
}
/* * If no CTL has been allocated in mdp5_crtc_atomic_check(), * it means we are trying to flush a CRTC whose state is disabled: * nothing else needs to be done.
*/ /* XXX: Can this happen now ? */ if (unlikely(!mdp5_cstate->ctl)) return;
blend_setup(crtc);
/* PP_DONE irq is only used by command mode for now. * It is better to request pending before FLUSH and START trigger * to make sure no pp_done irq missed. * This is safe because no pp_done will happen before SW trigger * in command mode.
*/ if (mdp5_cstate->cmd_mode)
request_pp_done_pending(crtc);
mdp5_crtc->flushed_mask = crtc_flush_all(crtc);
/* XXX are we leaking out state here? */
mdp5_crtc->vblank.irqmask = mdp5_cstate->vblank_irqmask;
mdp5_crtc->err.irqmask = mdp5_cstate->err_irqmask;
mdp5_crtc->pp_done.irqmask = mdp5_cstate->pp_done_irqmask;
/* * Cursor Region Of Interest (ROI) is a plane read from cursor * buffer to render. The ROI region is determined by the visibility of * the cursor point. In the default Cursor image the cursor point will * be at the top left of the cursor image. * * Without rotation: * If the cursor point reaches the right (xres - x < cursor.width) or * bottom (yres - y < cursor.height) boundary of the screen, then ROI * width and ROI height need to be evaluated to crop the cursor image * accordingly. * (xres-x) will be new cursor width when x > (xres - cursor.width) * (yres-y) will be new cursor height when y > (yres - cursor.height) * * With rotation: * We get negative x and/or y coordinates. * (cursor.width - abs(x)) will be new cursor width when x < 0 * (cursor.height - abs(y)) will be new cursor width when y < 0
*/ if (mdp5_crtc->cursor.x >= 0)
*roi_w = min(mdp5_crtc->cursor.width, xres -
mdp5_crtc->cursor.x); else
*roi_w = mdp5_crtc->cursor.width - abs(mdp5_crtc->cursor.x); if (mdp5_crtc->cursor.y >= 0)
*roi_h = min(mdp5_crtc->cursor.height, yres -
mdp5_crtc->cursor.y); else
*roi_h = mdp5_crtc->cursor.height - abs(mdp5_crtc->cursor.y);
}
x = mdp5_crtc->cursor.x;
y = mdp5_crtc->cursor.y;
width = mdp5_crtc->cursor.width;
height = mdp5_crtc->cursor.height;
stride = width * info->cpp[0];
get_roi(crtc, &roi_w, &roi_h);
/* If cusror buffer overlaps due to rotation on the * upper or left screen border the pixel offset inside * the cursor buffer of the ROI is the positive overlap * distance.
*/ if (mdp5_crtc->cursor.x < 0) {
src_x = abs(mdp5_crtc->cursor.x);
x = 0;
} else {
src_x = 0;
} if (mdp5_crtc->cursor.y < 0) {
src_y = abs(mdp5_crtc->cursor.y);
y = 0;
} else {
src_y = 0;
}
DBG("%s: x=%d, y=%d roi_w=%d roi_h=%d src_x=%d src_y=%d",
crtc->name, x, y, roi_w, roi_h, src_x, src_y);
if (!mdp5_crtc->lm_cursor_enabled) {
dev_warn(dev->dev, "cursor_move is deprecated with cursor planes\n"); return -EINVAL;
}
/* don't support LM cursors when we have source split enabled */ if (mdp5_cstate->pipeline.r_mixer) return -EINVAL;
/* In case the CRTC is disabled, just drop the cursor update */ if (unlikely(!crtc->state->enable)) return 0;
/* accept negative x/y coordinates up to maximum cursor overlap */
mdp5_crtc->cursor.x = x = max(x, -(int)mdp5_crtc->cursor.width);
mdp5_crtc->cursor.y = y = max(y, -(int)mdp5_crtc->cursor.height);
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.