Virtual wide planes give high amount of flexibility, but it is not always enough: In parallel multirect case only the half of the usual width is supported for tiled formats. Thus the whole width of two tiled multirect rectangles can not be greater than max_linewidth, which is not enough for some platforms/compositors. Another example is as simple as wide YUV plane. YUV planes can not use multirect, so currently they are limited to max_linewidth too. Now that the planes are fully virtualized, add support for allocating two SSPP blocks to drive a single DRM plane. This fixes both mentioned cases and allows all planes to go up to 2*max_linewidth (at the cost of making some of the planes unavailable to the user). Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@xxxxxxxxxx> --- drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c | 2 +- drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 236 ++++++++++++++++++++-- drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h | 13 +- 3 files changed, 227 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c index cdece21b81c9..7422bee8d21f 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_crtc.c @@ -1354,7 +1354,7 @@ static int dpu_crtc_atomic_check(struct drm_crtc *crtc, * is a shortcut. Perform actual check here, after * allocating SSPPs. */ - rc = dpu_plane_atomic_check(plane, crtc_state->state); + rc = dpu_plane_virtual_atomic_check_late(plane, crtc_state->state); if (rc) return rc; } diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index 787e81740eb9..3bd3951d9ffa 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -844,16 +844,27 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane, drm_atomic_get_plane_state(state, plane); struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state); const struct dpu_format *format; - struct drm_crtc_state *crtc_state; + struct drm_crtc_state *crtc_state = NULL; + int ret; + + if (plane_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); /* - * Main part of checks, including drm_atomic_helper_check_plane_state() - * is called from dpu_crtc_atomic_check(). Do minimal processing here. + * Main part of checks is performed in + * dpu_plane_virtual_atomic_check_late(), called from + * dpu_crtc_atomic_check(). Do minimal processing here. */ + ret = drm_atomic_helper_check_plane_noscale(plane_state, crtc_state, + true, true); + if (ret) { + DPU_DEBUG_PLANE(to_dpu_plane(plane), + "Check plane state failed (%d)\n", ret); + return ret; + } - if (!plane_state->fb) { - plane_state->visible = false; + if (!plane_state->visible) { /* resources are freed by dpu_crtc_atomic_check(), but clean them here */ pstate->pipe.sspp = NULL; pstate->r_pipe.sspp = NULL; @@ -861,18 +872,46 @@ static int dpu_plane_virtual_atomic_check(struct drm_plane *plane, return 0; } + pstate->stage = DPU_STAGE_0 + pstate->base.normalized_zpos; + if (pstate->stage >= pdpu->catalog->caps->max_mixer_blendstages) { + DPU_ERROR("> %d plane stages assigned\n", + pdpu->catalog->caps->max_mixer_blendstages - DPU_STAGE_0); + return -EINVAL; + } + + /* Ensure fb size is supported */ + if (plane_state->fb->width > MAX_IMG_WIDTH || + plane_state->fb->height > MAX_IMG_HEIGHT) { + DPU_DEBUG_PLANE(pdpu, "invalid framebuffer %dx%d\n", + plane_state->fb->width, + plane_state->fb->height); + return -E2BIG; + } + format = to_dpu_format(msm_framebuffer_format(plane_state->fb)); - crtc_state = drm_atomic_get_new_crtc_state(state, plane_state->crtc); - /* force resource reallocation if the format of FB has changed */ - if (pstate->saved_fmt ! = format) { + /* force resource reallocation if the format of FB or src/dst have changed */ + if (pstate->saved_fmt != format || + pstate->saved_src_w != plane_state->src_w || + pstate->saved_src_h != plane_state->src_h || + pstate->saved_src_w != plane_state->src_w || + pstate->saved_crtc_h != plane_state->crtc_h) { crtc_state->planes_changed = true; pstate->saved_fmt = format; + pstate->saved_src_w = plane_state->src_w; + pstate->saved_src_h = plane_state->src_h; + pstate->saved_crtc_w = plane_state->crtc_w; + pstate->saved_crtc_h = plane_state->crtc_h; } return 0; } +/* + * Allocate backing SSPP blocks for the plane. This does not perform any + * additional checks on the plane, this is done in + * dpu_plane_virtual_atomic_check_late(). + */ int dpu_plane_virtual_assign_resources(struct drm_plane *plane, struct drm_crtc *crtc, struct dpu_global_state *global_state, @@ -881,25 +920,185 @@ int dpu_plane_virtual_assign_resources(struct drm_plane *plane, struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); struct dpu_plane_state *pstate; struct drm_plane_state *plane_state; + struct dpu_sw_pipe *pipe; + struct dpu_sw_pipe *r_pipe; + struct dpu_sw_pipe_cfg *pipe_cfg; + struct dpu_sw_pipe_cfg *r_pipe_cfg; struct dpu_hw_sspp *hw_sspp; - bool yuv, scale; + const struct dpu_format *fmt; + bool yuv, scale, tiled; + uint32_t max_linewidth; plane_state = drm_atomic_get_plane_state(state, plane); if (IS_ERR(plane_state)) return PTR_ERR(plane_state); - yuv = plane_state->fb ? - DPU_FORMAT_IS_YUV(to_dpu_format(msm_framebuffer_format(plane_state->fb))) : - false; - scale = (plane_state->src_w >> 16 != plane_state->crtc_w) || - (plane_state->src_h >> 16 != plane_state->crtc_h); + pstate = to_dpu_plane_state(plane_state); + + pipe = &pstate->pipe; + r_pipe = &pstate->r_pipe; + pipe_cfg = &pstate->pipe_cfg; + r_pipe_cfg = &pstate->r_pipe_cfg; + + fmt = to_dpu_format(msm_framebuffer_format(plane_state->fb)); + yuv = DPU_FORMAT_IS_YUV(fmt); + tiled = DPU_FORMAT_IS_UBWC(fmt); + + pipe_cfg->src_rect = plane_state->src; + + /* state->src is 16.16, src_rect is not */ + pipe_cfg->src_rect.x1 >>= 16; + pipe_cfg->src_rect.x2 >>= 16; + pipe_cfg->src_rect.y1 >>= 16; + pipe_cfg->src_rect.y2 >>= 16; + + pipe_cfg->dst_rect = plane_state->dst; + + scale = (drm_rect_width(&pipe_cfg->src_rect) != drm_rect_width(&pipe_cfg->dst_rect)) || + (drm_rect_height(&pipe_cfg->src_rect) != drm_rect_height(&pipe_cfg->dst_rect)); + + max_linewidth = dpu_kms->catalog->caps->max_linewidth; + + pipe->multirect_index = DPU_SSPP_RECT_SOLO; + pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE; + r_pipe->multirect_index = DPU_SSPP_RECT_SOLO; + r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE; + r_pipe->sspp = NULL; hw_sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, yuv, scale); if (!hw_sspp) return -ENODEV; - pstate = to_dpu_plane_state(plane_state); - pstate->pipe.sspp = hw_sspp; + pipe->sspp = hw_sspp; + + if (drm_rect_width(&pipe_cfg->src_rect) <= max_linewidth) + return 0; + + *r_pipe_cfg = *pipe_cfg; + + pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1; + pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1; + + if (yuv && pipe_cfg->src_rect.x2 & 0x1) { + pipe_cfg->src_rect.x2 -= 1; + pipe_cfg->dst_rect.x2 -= 1; + } + + r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2; + r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2; + + if (drm_rect_width(&pipe_cfg->src_rect) > max_linewidth || + drm_rect_width(&r_pipe_cfg->src_rect) > max_linewidth) { + DPU_DEBUG_PLANE(to_dpu_plane(plane), + "invalid src " DRM_RECT_FMT " / " DRM_RECT_FMT " line:%u\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), + DRM_RECT_ARG(&r_pipe_cfg->src_rect), + max_linewidth); + return -E2BIG; + } + + /* + * Check if we can use parallel multirect for the wide plane. + * + * For tiled formats there is no point in trying multirect. + * In parallel multirect case only the half of the usual width + * is supported for tiled formats. If we are here, we know that + * full width is more than max_linewidth, thus each rect is + * wider than allowed. + */ + if (!yuv && !scale && !tiled && + test_bit(DPU_SSPP_SMART_DMA_V2, &pipe->sspp->cap->features)) { + pipe->multirect_index = DPU_SSPP_RECT_0; + pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; + + r_pipe->sspp = pipe->sspp; + r_pipe->multirect_index = DPU_SSPP_RECT_1; + r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; + } else { + /* multirect is not possible, use two SSPP blocks */ + hw_sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, yuv, scale); + if (!hw_sspp) + return -ENODEV; + + r_pipe->sspp = hw_sspp; + } + + return 0; +} + +int dpu_plane_virtual_atomic_check_late(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane); + int ret = 0, min_scale, max_scale, hscale, vscale; + struct dpu_plane *pdpu = to_dpu_plane(plane); + struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state); + struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + struct dpu_sw_pipe *pipe = &pstate->pipe; + struct dpu_sw_pipe *r_pipe = &pstate->r_pipe; + const struct drm_crtc_state *crtc_state = NULL; + const struct dpu_format *fmt; + struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg; + struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg; + struct drm_rect fb_rect = { 0 }; + uint32_t max_linewidth; + unsigned int rotation; + uint32_t supported_rotations; + const struct dpu_sspp_cfg *pipe_hw_caps; + const struct dpu_sspp_sub_blks *sblk; + + if (plane_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, + plane_state->crtc); + + pipe_hw_caps = pstate->pipe.sspp->cap; + sblk = pstate->pipe.sspp->cap->sblk; + + min_scale = FRAC_16_16(1, sblk->maxupscale); + max_scale = sblk->maxdwnscale << 16; + hscale = drm_rect_calc_hscale(&plane_state->src, &plane_state->dst, min_scale, max_scale); + vscale = drm_rect_calc_vscale(&plane_state->src, &plane_state->dst, min_scale, max_scale); + if (hscale < 0 || vscale < 0) { + drm_dbg_kms(plane->dev, "Invalid scaling of plane\n"); + drm_rect_debug_print("src: ", &plane_state->src, true); + drm_rect_debug_print("dst: ", &plane_state->dst, false); + return -ERANGE; + } + + if (!plane_state->visible) + return 0; + + fmt = to_dpu_format(msm_framebuffer_format(plane_state->fb)); + + max_linewidth = pdpu->catalog->caps->max_linewidth; + + ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg, fmt); + if (ret) + return ret; + + if (r_pipe->sspp) { + ret = dpu_plane_atomic_check_pipe(pdpu, r_pipe, r_pipe_cfg, fmt); + if (ret) + return ret; + } + + supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0; + + if (pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION)) + supported_rotations |= DRM_MODE_ROTATE_90; + + rotation = drm_rotation_simplify(plane_state->rotation, + supported_rotations); + + if ((pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION)) && + (rotation & DRM_MODE_ROTATE_90)) { + ret = dpu_plane_check_inline_rotation(pdpu, sblk, pipe_cfg->src_rect, fmt); + if (ret) + return ret; + } + + pstate->rotation = rotation; + pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state); return 0; } @@ -930,11 +1129,6 @@ int dpu_plane_atomic_check(struct drm_plane *plane, crtc_state = drm_atomic_get_new_crtc_state(state, new_plane_state->crtc); - if (pdpu->pipe != SSPP_NONE) { - pipe->sspp = dpu_rm_get_sspp(&dpu_kms->rm, pdpu->pipe); - r_pipe->sspp = NULL; - } - pipe_hw_caps = pstate->pipe.sspp->cap; sblk = pstate->pipe.sspp->cap->sblk; diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h index cb1e31ef0d3f..07e0796cc100 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.h @@ -32,6 +32,10 @@ * @needs_dirtyfb: whether attached CRTC needs pixel data explicitly flushed * @rotation: simplified drm rotation hint * @saved_fmt: format used by the plane's FB, saved for for virtual plane support + * @saved_src_w: cached value of plane's src_w, saved for for virtual plane support + * @saved_src_h: cached value of plane's src_h, saved for for virtual plane support + * @saved_crtc_w: cached value of plane's crtc_w, saved for for virtual plane support + * @saved_crtc_h: cached value of plane's crtc_h, saved for for virtual plane support */ struct dpu_plane_state { struct drm_plane_state base; @@ -51,6 +55,10 @@ struct dpu_plane_state { unsigned int rotation; const struct dpu_format *saved_fmt; + uint32_t saved_src_w; + uint32_t saved_src_h; + uint32_t saved_crtc_w; + uint32_t saved_crtc_h; }; #define to_dpu_plane_state(x) \ @@ -106,11 +114,12 @@ void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable); static inline void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable) {} #endif -int dpu_plane_atomic_check(struct drm_plane *plane, struct drm_atomic_state *state); - int dpu_plane_virtual_assign_resources(struct drm_plane *plane, struct drm_crtc *crtc, struct dpu_global_state *global_state, struct drm_atomic_state *state); +int dpu_plane_virtual_atomic_check_late(struct drm_plane *plane, + struct drm_atomic_state *state); + #endif /* _DPU_PLANE_H_ */ -- 2.30.2