diff options
Diffstat (limited to 'drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c')
| -rw-r--r-- | drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c | 2330 |
1 files changed, 1371 insertions, 959 deletions
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c index fd75870eb17f..d07a6ab6e7ee 100644 --- a/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c +++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_plane.c @@ -1,19 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014-2018 The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ @@ -21,19 +10,25 @@ #include <linux/debugfs.h> #include <linux/dma-buf.h> +#include <drm/drm_atomic.h> #include <drm/drm_atomic_uapi.h> +#include <drm/drm_blend.h> +#include <drm/drm_damage_helper.h> +#include <drm/drm_framebuffer.h> +#include <drm/drm_gem_atomic_helper.h> + +#include <linux/soc/qcom/ubwc.h> #include "msm_drv.h" #include "dpu_kms.h" -#include "dpu_formats.h" #include "dpu_hw_sspp.h" -#include "dpu_hw_catalog_format.h" +#include "dpu_hw_util.h" #include "dpu_trace.h" #include "dpu_crtc.h" #include "dpu_vbif.h" #include "dpu_plane.h" -#define DPU_DEBUG_PLANE(pl, fmt, ...) DPU_DEBUG("plane%d " fmt,\ +#define DPU_DEBUG_PLANE(pl, fmt, ...) DRM_DEBUG_ATOMIC("plane%d " fmt,\ (pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__) #define DPU_ERROR_PLANE(pl, fmt, ...) DPU_ERROR("plane%d " fmt,\ @@ -49,76 +44,54 @@ #define SHARP_SMOOTH_THR_DEFAULT 8 #define SHARP_NOISE_THR_DEFAULT 2 -#define DPU_NAME_SIZE 12 - #define DPU_PLANE_COLOR_FILL_FLAG BIT(31) #define DPU_ZPOS_MAX 255 -/* multirect rect index */ -enum { - R0, - R1, - R_MAX -}; - +/* + * Default Preload Values + */ #define DPU_QSEED3_DEFAULT_PRELOAD_H 0x4 #define DPU_QSEED3_DEFAULT_PRELOAD_V 0x3 +#define DPU_QSEED4_DEFAULT_PRELOAD_V 0x2 +#define DPU_QSEED4_DEFAULT_PRELOAD_H 0x4 #define DEFAULT_REFRESH_RATE 60 -/** - * enum dpu_plane_qos - Different qos configurations for each pipe - * - * @DPU_PLANE_QOS_VBLANK_CTRL: Setup VBLANK qos for the pipe. - * @DPU_PLANE_QOS_VBLANK_AMORTIZE: Enables Amortization within pipe. - * this configuration is mutually exclusive from VBLANK_CTRL. - * @DPU_PLANE_QOS_PANIC_CTRL: Setup panic for the pipe. - */ -enum dpu_plane_qos { - DPU_PLANE_QOS_VBLANK_CTRL = BIT(0), - DPU_PLANE_QOS_VBLANK_AMORTIZE = BIT(1), - DPU_PLANE_QOS_PANIC_CTRL = BIT(2), +static const uint32_t qcom_compressed_supported_formats[] = { + DRM_FORMAT_ABGR8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_BGR565, + + DRM_FORMAT_NV12, + DRM_FORMAT_P010, }; /* * struct dpu_plane - local dpu plane structure - * @aspace: address space pointer + * @vm: address space pointer * @csc_ptr: Points to dpu_csc_cfg structure to use for current - * @mplane_list: List of multirect planes of the same pipe * @catalog: Points to dpu catalog structure * @revalidate: force revalidation of all the plane properties */ struct dpu_plane { struct drm_plane base; - struct mutex lock; - enum dpu_sspp pipe; - uint32_t features; /* capabilities from catalog */ - uint32_t nformats; - uint32_t formats[64]; - struct dpu_hw_pipe *pipe_hw; - struct dpu_hw_pipe_cfg pipe_cfg; - struct dpu_hw_pipe_qos_cfg pipe_qos_cfg; uint32_t color_fill; bool is_error; bool is_rt_pipe; - bool is_virtual; - struct list_head mplane_list; - struct dpu_mdss_cfg *catalog; - - struct dpu_csc_cfg *csc_ptr; - - const struct dpu_sspp_sub_blks *pipe_sblk; - char pipe_name[DPU_NAME_SIZE]; + const struct dpu_mdss_cfg *catalog; +}; - /* debugfs related stuff */ - struct dentry *debugfs_root; - struct dpu_debugfs_regset32 debugfs_src; - struct dpu_debugfs_regset32 debugfs_scaler; - struct dpu_debugfs_regset32 debugfs_csc; - bool debugfs_default_scale; +static const uint64_t supported_format_modifiers[] = { + DRM_FORMAT_MOD_QCOM_COMPRESSED, + DRM_FORMAT_MOD_LINEAR, + DRM_FORMAT_MOD_INVALID }; #define to_dpu_plane(x) container_of(x, struct dpu_plane, base) @@ -131,42 +104,119 @@ static struct dpu_kms *_dpu_plane_get_kms(struct drm_plane *plane) } /** + * _dpu_plane_calc_bw - calculate bandwidth required for a plane + * @catalog: Points to dpu catalog structure + * @fmt: Pointer to source buffer format + * @mode: Pointer to drm display mode + * @pipe_cfg: Pointer to pipe configuration + * Result: Updates calculated bandwidth in the plane state. + * BW Equation: src_w * src_h * bpp * fps * (v_total / v_dest) + * Prefill BW Equation: line src bytes * line_time + */ +static u64 _dpu_plane_calc_bw(const struct dpu_mdss_cfg *catalog, + const struct msm_format *fmt, + const struct drm_display_mode *mode, + struct dpu_sw_pipe_cfg *pipe_cfg) +{ + int src_width, src_height, dst_height, fps; + u64 plane_pixel_rate, plane_bit_rate; + u64 plane_prefill_bw; + u64 plane_bw; + u32 hw_latency_lines; + u64 scale_factor; + int vbp, vpw, vfp; + + src_width = drm_rect_width(&pipe_cfg->src_rect); + src_height = drm_rect_height(&pipe_cfg->src_rect); + dst_height = drm_rect_height(&pipe_cfg->dst_rect); + fps = drm_mode_vrefresh(mode); + vbp = mode->vtotal - mode->vsync_end; + vpw = mode->vsync_end - mode->vsync_start; + vfp = mode->vsync_start - mode->vdisplay; + hw_latency_lines = catalog->perf->min_prefill_lines; + scale_factor = src_height > dst_height ? + mult_frac(src_height, 1, dst_height) : 1; + + plane_pixel_rate = src_width * mode->vtotal * fps; + plane_bit_rate = plane_pixel_rate * fmt->bpp; + + plane_bw = plane_bit_rate * scale_factor; + + plane_prefill_bw = plane_bw * hw_latency_lines; + + if ((vbp+vpw) > hw_latency_lines) + do_div(plane_prefill_bw, (vbp+vpw)); + else if ((vbp+vpw+vfp) < hw_latency_lines) + do_div(plane_prefill_bw, (vbp+vpw+vfp)); + else + do_div(plane_prefill_bw, hw_latency_lines); + + + return max(plane_bw, plane_prefill_bw); +} + +/** + * _dpu_plane_calc_clk - calculate clock required for a plane + * @mode: Pointer to drm display mode + * @pipe_cfg: Pointer to pipe configuration + * Result: Updates calculated clock in the plane state. + * Clock equation: dst_w * v_total * fps * (src_h / dst_h) + */ +static u64 _dpu_plane_calc_clk(const struct drm_display_mode *mode, + struct dpu_sw_pipe_cfg *pipe_cfg) +{ + int dst_width, src_height, dst_height, fps; + u64 plane_clk; + + src_height = drm_rect_height(&pipe_cfg->src_rect); + dst_width = drm_rect_width(&pipe_cfg->dst_rect); + dst_height = drm_rect_height(&pipe_cfg->dst_rect); + fps = drm_mode_vrefresh(mode); + + plane_clk = + dst_width * mode->vtotal * fps; + + if (src_height > dst_height) { + plane_clk *= src_height; + do_div(plane_clk, dst_height); + } + + return plane_clk; +} + +/** * _dpu_plane_calc_fill_level - calculate fill level of the given source format * @plane: Pointer to drm plane + * @pipe: Pointer to software pipe + * @lut_usage: LUT usecase * @fmt: Pointer to source buffer format - * @src_wdith: width of source buffer + * @src_width: width of source buffer * Return: fill level corresponding to the source buffer/format or 0 if error */ static int _dpu_plane_calc_fill_level(struct drm_plane *plane, - const struct dpu_format *fmt, u32 src_width) + struct dpu_sw_pipe *pipe, + enum dpu_qos_lut_usage lut_usage, + const struct msm_format *fmt, u32 src_width) { - struct dpu_plane *pdpu, *tmp; - struct dpu_plane_state *pstate; + struct dpu_plane *pdpu; u32 fixed_buff_size; u32 total_fl; - if (!fmt || !plane->state || !src_width || !fmt->bpp) { + if (!fmt || !pipe || !src_width || !fmt->bpp) { DPU_ERROR("invalid arguments\n"); return 0; } + if (lut_usage == DPU_QOS_LUT_USAGE_NRT) + return 0; + pdpu = to_dpu_plane(plane); - pstate = to_dpu_plane_state(plane->state); - fixed_buff_size = pdpu->pipe_sblk->common->pixel_ram_size; + fixed_buff_size = pdpu->catalog->caps->pixel_ram_size; - list_for_each_entry(tmp, &pdpu->mplane_list, mplane_list) { - if (!tmp->base.state->visible) - continue; - DPU_DEBUG("plane%d/%d src_width:%d/%d\n", - pdpu->base.base.id, tmp->base.base.id, - src_width, - drm_rect_width(&tmp->pipe_cfg.src_rect)); - src_width = max_t(u32, src_width, - drm_rect_width(&tmp->pipe_cfg.src_rect)); - } + /* FIXME: in multirect case account for the src_width of all the planes */ - if (fmt->fetch_planes == DPU_PLANE_PSEUDO_PLANAR) { - if (fmt->chroma_sample == DPU_CHROMA_420) { + if (fmt->fetch_type == MDP_PLANE_PSEUDO_PLANAR) { + if (fmt->chroma_sample == CHROMA_420) { /* NV12 */ total_fl = (fixed_buff_size / 2) / ((src_width + 32) * fmt->bpp); @@ -176,7 +226,7 @@ static int _dpu_plane_calc_fill_level(struct drm_plane *plane, ((src_width + 32) * fmt->bpp); } } else { - if (pstate->multirect_mode == DPU_SSPP_MULTIRECT_PARALLEL) { + if (pipe->multirect_mode == DPU_SSPP_MULTIRECT_PARALLEL) { total_fl = (fixed_buff_size / 2) * 2 / ((src_width + 32) * fmt->bpp); } else { @@ -185,286 +235,211 @@ static int _dpu_plane_calc_fill_level(struct drm_plane *plane, } } - DPU_DEBUG("plane%u: pnum:%d fmt: %4.4s w:%u fl:%u\n", - plane->base.id, pdpu->pipe - SSPP_VIG0, - (char *)&fmt->base.pixel_format, + DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %p4cc w:%u fl:%u\n", + pipe->sspp->idx - SSPP_VIG0, + &fmt->pixel_format, src_width, total_fl); return total_fl; } /** - * _dpu_plane_get_qos_lut - get LUT mapping based on fill level - * @tbl: Pointer to LUT table - * @total_fl: fill level - * Return: LUT setting corresponding to the fill level - */ -static u64 _dpu_plane_get_qos_lut(const struct dpu_qos_lut_tbl *tbl, - u32 total_fl) -{ - int i; - - if (!tbl || !tbl->nentry || !tbl->entries) - return 0; - - for (i = 0; i < tbl->nentry; i++) - if (total_fl <= tbl->entries[i].fl) - return tbl->entries[i].lut; - - /* if last fl is zero, use as default */ - if (!tbl->entries[i-1].fl) - return tbl->entries[i-1].lut; - - return 0; -} - -/** * _dpu_plane_set_qos_lut - set QoS LUT of the given plane * @plane: Pointer to drm plane - * @fb: Pointer to framebuffer associated with the given plane + * @pipe: Pointer to software pipe + * @fmt: Pointer to source buffer format + * @pipe_cfg: Pointer to pipe configuration */ static void _dpu_plane_set_qos_lut(struct drm_plane *plane, - struct drm_framebuffer *fb) + struct dpu_sw_pipe *pipe, + const struct msm_format *fmt, struct dpu_sw_pipe_cfg *pipe_cfg) { struct dpu_plane *pdpu = to_dpu_plane(plane); - const struct dpu_format *fmt = NULL; - u64 qos_lut; - u32 total_fl = 0, lut_usage; + struct dpu_hw_qos_cfg cfg; + u32 total_fl, lut_usage; if (!pdpu->is_rt_pipe) { lut_usage = DPU_QOS_LUT_USAGE_NRT; } else { - fmt = dpu_get_dpu_format_ext( - fb->format->format, - fb->modifier); - total_fl = _dpu_plane_calc_fill_level(plane, fmt, - drm_rect_width(&pdpu->pipe_cfg.src_rect)); - - if (fmt && DPU_FORMAT_IS_LINEAR(fmt)) + if (fmt && MSM_FORMAT_IS_LINEAR(fmt)) lut_usage = DPU_QOS_LUT_USAGE_LINEAR; else lut_usage = DPU_QOS_LUT_USAGE_MACROTILE; } - qos_lut = _dpu_plane_get_qos_lut( - &pdpu->catalog->perf.qos_lut_tbl[lut_usage], total_fl); + total_fl = _dpu_plane_calc_fill_level(plane, pipe, lut_usage, fmt, + drm_rect_width(&pipe_cfg->src_rect)); - pdpu->pipe_qos_cfg.creq_lut = qos_lut; + cfg.creq_lut = _dpu_hw_get_qos_lut(&pdpu->catalog->perf->qos_lut_tbl[lut_usage], total_fl); + cfg.danger_lut = pdpu->catalog->perf->danger_lut_tbl[lut_usage]; + cfg.safe_lut = pdpu->catalog->perf->safe_lut_tbl[lut_usage]; - trace_dpu_perf_set_qos_luts(pdpu->pipe - SSPP_VIG0, - (fmt) ? fmt->base.pixel_format : 0, - pdpu->is_rt_pipe, total_fl, qos_lut, lut_usage); + if (pipe->sspp->idx != SSPP_CURSOR0 && + pipe->sspp->idx != SSPP_CURSOR1 && + pdpu->is_rt_pipe) + cfg.danger_safe_en = true; - DPU_DEBUG("plane%u: pnum:%d fmt: %4.4s rt:%d fl:%u lut:0x%llx\n", - plane->base.id, - pdpu->pipe - SSPP_VIG0, - fmt ? (char *)&fmt->base.pixel_format : NULL, - pdpu->is_rt_pipe, total_fl, qos_lut); - - pdpu->pipe_hw->ops.setup_creq_lut(pdpu->pipe_hw, &pdpu->pipe_qos_cfg); -} - -/** - * _dpu_plane_set_panic_lut - set danger/safe LUT of the given plane - * @plane: Pointer to drm plane - * @fb: Pointer to framebuffer associated with the given plane - */ -static void _dpu_plane_set_danger_lut(struct drm_plane *plane, - struct drm_framebuffer *fb) -{ - struct dpu_plane *pdpu = to_dpu_plane(plane); - const struct dpu_format *fmt = NULL; - u32 danger_lut, safe_lut; + DPU_DEBUG_PLANE(pdpu, "pnum:%d ds:%d is_rt:%d\n", + pdpu->pipe - SSPP_VIG0, + cfg.danger_safe_en, + pdpu->is_rt_pipe); - if (!pdpu->is_rt_pipe) { - danger_lut = pdpu->catalog->perf.danger_lut_tbl - [DPU_QOS_LUT_USAGE_NRT]; - safe_lut = pdpu->catalog->perf.safe_lut_tbl - [DPU_QOS_LUT_USAGE_NRT]; - } else { - fmt = dpu_get_dpu_format_ext( - fb->format->format, - fb->modifier); - - if (fmt && DPU_FORMAT_IS_LINEAR(fmt)) { - danger_lut = pdpu->catalog->perf.danger_lut_tbl - [DPU_QOS_LUT_USAGE_LINEAR]; - safe_lut = pdpu->catalog->perf.safe_lut_tbl - [DPU_QOS_LUT_USAGE_LINEAR]; - } else { - danger_lut = pdpu->catalog->perf.danger_lut_tbl - [DPU_QOS_LUT_USAGE_MACROTILE]; - safe_lut = pdpu->catalog->perf.safe_lut_tbl - [DPU_QOS_LUT_USAGE_MACROTILE]; - } - } + trace_dpu_perf_set_qos_luts(pipe->sspp->idx - SSPP_VIG0, + (fmt) ? fmt->pixel_format : 0, + pdpu->is_rt_pipe, total_fl, cfg.creq_lut, lut_usage); - pdpu->pipe_qos_cfg.danger_lut = danger_lut; - pdpu->pipe_qos_cfg.safe_lut = safe_lut; + DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %p4cc rt:%d fl:%u lut:0x%llx\n", + pdpu->pipe - SSPP_VIG0, + fmt ? &fmt->pixel_format : NULL, + pdpu->is_rt_pipe, total_fl, cfg.creq_lut); trace_dpu_perf_set_danger_luts(pdpu->pipe - SSPP_VIG0, - (fmt) ? fmt->base.pixel_format : 0, + (fmt) ? fmt->pixel_format : 0, (fmt) ? fmt->fetch_mode : 0, - pdpu->pipe_qos_cfg.danger_lut, - pdpu->pipe_qos_cfg.safe_lut); + cfg.danger_lut, + cfg.safe_lut); - DPU_DEBUG("plane%u: pnum:%d fmt: %4.4s mode:%d luts[0x%x, 0x%x]\n", - plane->base.id, - pdpu->pipe - SSPP_VIG0, - fmt ? (char *)&fmt->base.pixel_format : NULL, - fmt ? fmt->fetch_mode : -1, - pdpu->pipe_qos_cfg.danger_lut, - pdpu->pipe_qos_cfg.safe_lut); + DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %p4cc mode:%d luts[0x%x, 0x%x]\n", + pdpu->pipe - SSPP_VIG0, + fmt ? &fmt->pixel_format : NULL, + fmt ? fmt->fetch_mode : -1, + cfg.danger_lut, + cfg.safe_lut); - pdpu->pipe_hw->ops.setup_danger_safe_lut(pdpu->pipe_hw, - &pdpu->pipe_qos_cfg); + pipe->sspp->ops.setup_qos_lut(pipe->sspp, &cfg); } /** * _dpu_plane_set_qos_ctrl - set QoS control of the given plane * @plane: Pointer to drm plane + * @pipe: Pointer to software pipe * @enable: true to enable QoS control - * @flags: QoS control mode (enum dpu_plane_qos) */ static void _dpu_plane_set_qos_ctrl(struct drm_plane *plane, - bool enable, u32 flags) + struct dpu_sw_pipe *pipe, + bool enable) { struct dpu_plane *pdpu = to_dpu_plane(plane); - if (flags & DPU_PLANE_QOS_VBLANK_CTRL) { - pdpu->pipe_qos_cfg.creq_vblank = pdpu->pipe_sblk->creq_vblank; - pdpu->pipe_qos_cfg.danger_vblank = - pdpu->pipe_sblk->danger_vblank; - pdpu->pipe_qos_cfg.vblank_en = enable; - } - - if (flags & DPU_PLANE_QOS_VBLANK_AMORTIZE) { - /* this feature overrules previous VBLANK_CTRL */ - pdpu->pipe_qos_cfg.vblank_en = false; - pdpu->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */ - } - - if (flags & DPU_PLANE_QOS_PANIC_CTRL) - pdpu->pipe_qos_cfg.danger_safe_en = enable; - - if (!pdpu->is_rt_pipe) { - pdpu->pipe_qos_cfg.vblank_en = false; - pdpu->pipe_qos_cfg.danger_safe_en = false; - } + if (!pdpu->is_rt_pipe) + enable = false; - DPU_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n", - plane->base.id, + DPU_DEBUG_PLANE(pdpu, "pnum:%d ds:%d is_rt:%d\n", pdpu->pipe - SSPP_VIG0, - pdpu->pipe_qos_cfg.danger_safe_en, - pdpu->pipe_qos_cfg.vblank_en, - pdpu->pipe_qos_cfg.creq_vblank, - pdpu->pipe_qos_cfg.danger_vblank, + enable, pdpu->is_rt_pipe); - pdpu->pipe_hw->ops.setup_qos_ctrl(pdpu->pipe_hw, - &pdpu->pipe_qos_cfg); + pipe->sspp->ops.setup_qos_ctrl(pipe->sspp, + enable); } -static void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable) +static bool _dpu_plane_sspp_clk_force_ctrl(struct dpu_hw_sspp *sspp, + struct dpu_hw_mdp *mdp, + bool enable, bool *forced_on) { - struct dpu_plane *pdpu = to_dpu_plane(plane); - struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + if (sspp->ops.setup_clk_force_ctrl) { + *forced_on = sspp->ops.setup_clk_force_ctrl(sspp, enable); + return true; + } - if (!pdpu->is_rt_pipe) - return; + if (mdp->ops.setup_clk_force_ctrl) { + *forced_on = mdp->ops.setup_clk_force_ctrl(mdp, sspp->cap->clk_ctrl, enable); + return true; + } - pm_runtime_get_sync(&dpu_kms->pdev->dev); - _dpu_plane_set_qos_ctrl(plane, enable, DPU_PLANE_QOS_PANIC_CTRL); - pm_runtime_put_sync(&dpu_kms->pdev->dev); + return false; } /** * _dpu_plane_set_ot_limit - set OT limit for the given plane * @plane: Pointer to drm plane - * @crtc: Pointer to drm crtc + * @pipe: Pointer to software pipe + * @pipe_cfg: Pointer to pipe configuration + * @frame_rate: CRTC's frame rate */ static void _dpu_plane_set_ot_limit(struct drm_plane *plane, - struct drm_crtc *crtc) + struct dpu_sw_pipe *pipe, + struct dpu_sw_pipe_cfg *pipe_cfg, + int frame_rate) { struct dpu_plane *pdpu = to_dpu_plane(plane); struct dpu_vbif_set_ot_params ot_params; struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + bool forced_on = false; memset(&ot_params, 0, sizeof(ot_params)); - ot_params.xin_id = pdpu->pipe_hw->cap->xin_id; - ot_params.num = pdpu->pipe_hw->idx - SSPP_NONE; - ot_params.width = drm_rect_width(&pdpu->pipe_cfg.src_rect); - ot_params.height = drm_rect_height(&pdpu->pipe_cfg.src_rect); + ot_params.xin_id = pipe->sspp->cap->xin_id; + ot_params.num = pipe->sspp->idx - SSPP_NONE; + ot_params.width = drm_rect_width(&pipe_cfg->src_rect); + ot_params.height = drm_rect_height(&pipe_cfg->src_rect); ot_params.is_wfd = !pdpu->is_rt_pipe; - ot_params.frame_rate = crtc->mode.vrefresh; + ot_params.frame_rate = frame_rate; ot_params.vbif_idx = VBIF_RT; - ot_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl; ot_params.rd = true; + if (!_dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp, + true, &forced_on)) + return; + dpu_vbif_set_ot_limit(dpu_kms, &ot_params); + + if (forced_on) + _dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp, + false, &forced_on); } /** - * _dpu_plane_set_vbif_qos - set vbif QoS for the given plane + * _dpu_plane_set_qos_remap - set vbif QoS for the given plane * @plane: Pointer to drm plane + * @pipe: Pointer to software pipe */ -static void _dpu_plane_set_qos_remap(struct drm_plane *plane) +static void _dpu_plane_set_qos_remap(struct drm_plane *plane, + struct dpu_sw_pipe *pipe) { struct dpu_plane *pdpu = to_dpu_plane(plane); struct dpu_vbif_set_qos_params qos_params; struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + bool forced_on = false; memset(&qos_params, 0, sizeof(qos_params)); qos_params.vbif_idx = VBIF_RT; - qos_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl; - qos_params.xin_id = pdpu->pipe_hw->cap->xin_id; - qos_params.num = pdpu->pipe_hw->idx - SSPP_VIG0; + qos_params.xin_id = pipe->sspp->cap->xin_id; + qos_params.num = pipe->sspp->idx - SSPP_VIG0; qos_params.is_rt = pdpu->is_rt_pipe; - DPU_DEBUG("plane%d pipe:%d vbif:%d xin:%d rt:%d, clk_ctrl:%d\n", - plane->base.id, qos_params.num, + DPU_DEBUG_PLANE(pdpu, "pipe:%d vbif:%d xin:%d rt:%d\n", + qos_params.num, qos_params.vbif_idx, - qos_params.xin_id, qos_params.is_rt, - qos_params.clk_ctrl); + qos_params.xin_id, qos_params.is_rt); - dpu_vbif_set_qos_remap(dpu_kms, &qos_params); -} + if (!_dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp, + true, &forced_on)) + return; -static void _dpu_plane_set_scanout(struct drm_plane *plane, - struct dpu_plane_state *pstate, - struct dpu_hw_pipe_cfg *pipe_cfg, - struct drm_framebuffer *fb) -{ - struct dpu_plane *pdpu = to_dpu_plane(plane); - struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base); - struct msm_gem_address_space *aspace = kms->base.aspace; - int ret; + dpu_vbif_set_qos_remap(dpu_kms, &qos_params); - ret = dpu_format_populate_layout(aspace, fb, &pipe_cfg->layout); - if (ret == -EAGAIN) - DPU_DEBUG_PLANE(pdpu, "not updating same src addrs\n"); - else if (ret) - DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret); - else if (pdpu->pipe_hw->ops.setup_sourceaddress) { - trace_dpu_plane_set_scanout(pdpu->pipe_hw->idx, - &pipe_cfg->layout, - pstate->multirect_index); - pdpu->pipe_hw->ops.setup_sourceaddress(pdpu->pipe_hw, pipe_cfg, - pstate->multirect_index); - } + if (forced_on) + _dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp, + false, &forced_on); } -static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu, - struct dpu_plane_state *pstate, +static void _dpu_plane_setup_scaler3(struct dpu_hw_sspp *pipe_hw, uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h, struct dpu_hw_scaler3_cfg *scale_cfg, - const struct dpu_format *fmt, - uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v) + const struct msm_format *fmt, + uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v, + unsigned int rotation) { uint32_t i; + bool inline_rotation = rotation & DRM_MODE_ROTATE_90; - memset(scale_cfg, 0, sizeof(*scale_cfg)); - memset(&pstate->pixel_ext, 0, sizeof(struct dpu_hw_pixel_ext)); + /* + * For inline rotation cases, scaler config is post-rotation, + * so swap the dimensions here. However, pixel extension will + * need pre-rotation settings. + */ + if (inline_rotation) + swap(src_w, src_h); scale_cfg->phase_step_x[DPU_SSPP_COMP_0] = mult_frac((1 << PHASE_STEP_SHIFT), src_w, dst_w); @@ -494,14 +469,16 @@ static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu, scale_cfg->src_width[i] /= chroma_subsmpl_h; scale_cfg->src_height[i] /= chroma_subsmpl_v; } - scale_cfg->preload_x[i] = DPU_QSEED3_DEFAULT_PRELOAD_H; - scale_cfg->preload_y[i] = DPU_QSEED3_DEFAULT_PRELOAD_V; - pstate->pixel_ext.num_ext_pxls_top[i] = - scale_cfg->src_height[i]; - pstate->pixel_ext.num_ext_pxls_left[i] = - scale_cfg->src_width[i]; + + if (pipe_hw->cap->sblk->scaler_blk.version >= 0x3000) { + scale_cfg->preload_x[i] = DPU_QSEED4_DEFAULT_PRELOAD_H; + scale_cfg->preload_y[i] = DPU_QSEED4_DEFAULT_PRELOAD_V; + } else { + scale_cfg->preload_x[i] = DPU_QSEED3_DEFAULT_PRELOAD_H; + scale_cfg->preload_y[i] = DPU_QSEED3_DEFAULT_PRELOAD_V; + } } - if (!(DPU_FORMAT_IS_YUV(fmt)) && (src_h == dst_h) + if (!(MSM_FORMAT_IS_YUV(fmt)) && (src_h == dst_h) && (src_w == dst_w)) return; @@ -515,73 +492,120 @@ static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu, scale_cfg->enable = 1; } -static void _dpu_plane_setup_csc(struct dpu_plane *pdpu) +static void _dpu_plane_setup_pixel_ext(struct dpu_hw_scaler3_cfg *scale_cfg, + struct dpu_hw_pixel_ext *pixel_ext, + uint32_t src_w, uint32_t src_h, + uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v) { - static const struct dpu_csc_cfg dpu_csc_YUV2RGB_601L = { - { - /* S15.16 format */ - 0x00012A00, 0x00000000, 0x00019880, - 0x00012A00, 0xFFFF9B80, 0xFFFF3000, - 0x00012A00, 0x00020480, 0x00000000, - }, - /* signed bias */ - { 0xfff0, 0xff80, 0xff80,}, - { 0x0, 0x0, 0x0,}, - /* unsigned clamp */ - { 0x10, 0xeb, 0x10, 0xf0, 0x10, 0xf0,}, - { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,}, - }; - static const struct dpu_csc_cfg dpu_csc10_YUV2RGB_601L = { - { - /* S15.16 format */ - 0x00012A00, 0x00000000, 0x00019880, - 0x00012A00, 0xFFFF9B80, 0xFFFF3000, - 0x00012A00, 0x00020480, 0x00000000, - }, - /* signed bias */ - { 0xffc0, 0xfe00, 0xfe00,}, - { 0x0, 0x0, 0x0,}, - /* unsigned clamp */ - { 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,}, - { 0x00, 0x3ff, 0x00, 0x3ff, 0x00, 0x3ff,}, - }; - - if (!pdpu) { - DPU_ERROR("invalid plane\n"); - return; + int i; + + for (i = 0; i < DPU_MAX_PLANES; i++) { + uint32_t w = src_w, h = src_h; + + if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) { + w /= chroma_subsmpl_h; + h /= chroma_subsmpl_v; + } + + pixel_ext->num_ext_pxls_top[i] = h; + pixel_ext->num_ext_pxls_left[i] = w; } +} - if (BIT(DPU_SSPP_CSC_10BIT) & pdpu->features) - pdpu->csc_ptr = (struct dpu_csc_cfg *)&dpu_csc10_YUV2RGB_601L; +static const struct dpu_csc_cfg *_dpu_plane_get_csc(struct dpu_sw_pipe *pipe, + const struct msm_format *fmt) +{ + const struct dpu_csc_cfg *csc_ptr; + + if (!MSM_FORMAT_IS_YUV(fmt)) + return NULL; + + if (BIT(DPU_SSPP_CSC_10BIT) & pipe->sspp->cap->features) + csc_ptr = &dpu_csc10_YUV2RGB_601L; else - pdpu->csc_ptr = (struct dpu_csc_cfg *)&dpu_csc_YUV2RGB_601L; + csc_ptr = &dpu_csc_YUV2RGB_601L; - DPU_DEBUG_PLANE(pdpu, "using 0x%X 0x%X 0x%X...\n", - pdpu->csc_ptr->csc_mv[0], - pdpu->csc_ptr->csc_mv[1], - pdpu->csc_ptr->csc_mv[2]); + return csc_ptr; } -static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu, - struct dpu_plane_state *pstate, - const struct dpu_format *fmt, bool color_fill) +static void _dpu_plane_setup_scaler(struct dpu_sw_pipe *pipe, + const struct msm_format *fmt, bool color_fill, + struct dpu_sw_pipe_cfg *pipe_cfg) { - uint32_t chroma_subsmpl_h, chroma_subsmpl_v; + struct dpu_hw_sspp *pipe_hw = pipe->sspp; + const struct drm_format_info *info = drm_format_info(fmt->pixel_format); + struct dpu_hw_scaler3_cfg scaler3_cfg; + struct dpu_hw_pixel_ext pixel_ext; + u32 src_width = drm_rect_width(&pipe_cfg->src_rect); + u32 src_height = drm_rect_height(&pipe_cfg->src_rect); + u32 dst_width = drm_rect_width(&pipe_cfg->dst_rect); + u32 dst_height = drm_rect_height(&pipe_cfg->dst_rect); + + memset(&scaler3_cfg, 0, sizeof(scaler3_cfg)); + memset(&pixel_ext, 0, sizeof(pixel_ext)); /* don't chroma subsample if decimating */ - chroma_subsmpl_h = - drm_format_horz_chroma_subsampling(fmt->base.pixel_format); - chroma_subsmpl_v = - drm_format_vert_chroma_subsampling(fmt->base.pixel_format); - /* update scaler. calculate default config for QSEED3 */ - _dpu_plane_setup_scaler3(pdpu, pstate, - drm_rect_width(&pdpu->pipe_cfg.src_rect), - drm_rect_height(&pdpu->pipe_cfg.src_rect), - drm_rect_width(&pdpu->pipe_cfg.dst_rect), - drm_rect_height(&pdpu->pipe_cfg.dst_rect), - &pstate->scaler3_cfg, fmt, - chroma_subsmpl_h, chroma_subsmpl_v); + _dpu_plane_setup_scaler3(pipe_hw, + src_width, + src_height, + dst_width, + dst_height, + &scaler3_cfg, fmt, + info->hsub, info->vsub, + pipe_cfg->rotation); + + /* configure pixel extension based on scalar config */ + _dpu_plane_setup_pixel_ext(&scaler3_cfg, &pixel_ext, + src_width, src_height, info->hsub, info->vsub); + + if (pipe_hw->ops.setup_pe) + pipe_hw->ops.setup_pe(pipe_hw, + &pixel_ext); + + /** + * when programmed in multirect mode, scalar block will be + * bypassed. Still we need to update alpha and bitwidth + * ONLY for RECT0 + */ + if (pipe_hw->ops.setup_scaler && + pipe->multirect_index != DPU_SSPP_RECT_1) + pipe_hw->ops.setup_scaler(pipe_hw, + &scaler3_cfg, + fmt); +} + +static void _dpu_plane_color_fill_pipe(struct dpu_plane_state *pstate, + struct dpu_sw_pipe *pipe, + struct drm_rect *dst_rect, + u32 fill_color, + const struct msm_format *fmt) +{ + struct dpu_sw_pipe_cfg pipe_cfg; + + /* update sspp */ + if (!pipe->sspp->ops.setup_solidfill) + return; + + pipe->sspp->ops.setup_solidfill(pipe, fill_color); + + /* override scaler/decimation if solid fill */ + pipe_cfg.dst_rect = *dst_rect; + + pipe_cfg.src_rect.x1 = 0; + pipe_cfg.src_rect.y1 = 0; + pipe_cfg.src_rect.x2 = + drm_rect_width(&pipe_cfg.dst_rect); + pipe_cfg.src_rect.y2 = + drm_rect_height(&pipe_cfg.dst_rect); + + if (pipe->sspp->ops.setup_format) + pipe->sspp->ops.setup_format(pipe, fmt, DPU_SSPP_SOLID_FILL); + + if (pipe->sspp->ops.setup_rects) + pipe->sspp->ops.setup_rects(pipe, &pipe_cfg); + + _dpu_plane_setup_scaler(pipe, fmt, true, &pipe_cfg); } /** @@ -589,14 +613,16 @@ static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu, * @pdpu: Pointer to DPU plane object * @color: RGB fill color value, [23..16] Blue, [15..8] Green, [7..0] Red * @alpha: 8-bit fill alpha value, 255 selects 100% alpha - * Returns: 0 on success */ -static int _dpu_plane_color_fill(struct dpu_plane *pdpu, +static void _dpu_plane_color_fill(struct dpu_plane *pdpu, uint32_t color, uint32_t alpha) { - const struct dpu_format *fmt; + const struct msm_format *fmt; const struct drm_plane *plane = &pdpu->base; + struct msm_drm_private *priv = plane->dev->dev_private; struct dpu_plane_state *pstate = to_dpu_plane_state(plane->state); + u32 fill_color = (color & 0xFFFFFF) | ((alpha & 0xFF) << 24); + int i; DPU_DEBUG_PLANE(pdpu, "\n"); @@ -604,354 +630,787 @@ static int _dpu_plane_color_fill(struct dpu_plane *pdpu, * select fill format to match user property expectation, * h/w only supports RGB variants */ - fmt = dpu_get_dpu_format(DRM_FORMAT_ABGR8888); + fmt = mdp_get_format(priv->kms, DRM_FORMAT_ABGR8888, 0); + /* should not happen ever */ + if (!fmt) + return; /* update sspp */ - if (fmt && pdpu->pipe_hw->ops.setup_solidfill) { - pdpu->pipe_hw->ops.setup_solidfill(pdpu->pipe_hw, - (color & 0xFFFFFF) | ((alpha & 0xFF) << 24), - pstate->multirect_index); - - /* override scaler/decimation if solid fill */ - pdpu->pipe_cfg.src_rect.x1 = 0; - pdpu->pipe_cfg.src_rect.y1 = 0; - pdpu->pipe_cfg.src_rect.x2 = - drm_rect_width(&pdpu->pipe_cfg.dst_rect); - pdpu->pipe_cfg.src_rect.y2 = - drm_rect_height(&pdpu->pipe_cfg.dst_rect); - _dpu_plane_setup_scaler(pdpu, pstate, fmt, true); - - if (pdpu->pipe_hw->ops.setup_format) - pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw, - fmt, DPU_SSPP_SOLID_FILL, - pstate->multirect_index); - - if (pdpu->pipe_hw->ops.setup_rects) - pdpu->pipe_hw->ops.setup_rects(pdpu->pipe_hw, - &pdpu->pipe_cfg, - pstate->multirect_index); - - if (pdpu->pipe_hw->ops.setup_pe) - pdpu->pipe_hw->ops.setup_pe(pdpu->pipe_hw, - &pstate->pixel_ext); - - if (pdpu->pipe_hw->ops.setup_scaler && - pstate->multirect_index != DPU_SSPP_RECT_1) - pdpu->pipe_hw->ops.setup_scaler(pdpu->pipe_hw, - &pdpu->pipe_cfg, &pstate->pixel_ext, - &pstate->scaler3_cfg); + for (i = 0; i < PIPES_PER_PLANE; i++) { + if (!pstate->pipe[i].sspp) + continue; + _dpu_plane_color_fill_pipe(pstate, &pstate->pipe[i], + &pstate->pipe_cfg[i].dst_rect, + fill_color, fmt); + } +} + +static int dpu_plane_prepare_fb(struct drm_plane *plane, + struct drm_plane_state *new_state) +{ + struct drm_framebuffer *fb = new_state->fb; + struct dpu_plane *pdpu = to_dpu_plane(plane); + struct dpu_plane_state *pstate = to_dpu_plane_state(new_state); + int ret; + + if (!new_state->fb) + return 0; + + DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", fb->base.id); + + /* + * TODO: Need to sort out the msm_framebuffer_prepare() call below so + * we can use msm_atomic_prepare_fb() instead of doing the + * implicit fence and fb prepare by hand here. + */ + drm_gem_plane_helper_prepare_fb(plane, new_state); + + ret = msm_framebuffer_prepare(new_state->fb, pstate->needs_dirtyfb); + if (ret) { + DPU_ERROR("failed to prepare framebuffer\n"); + return ret; } return 0; } -void dpu_plane_clear_multirect(const struct drm_plane_state *drm_state) +static void dpu_plane_cleanup_fb(struct drm_plane *plane, + struct drm_plane_state *old_state) { - struct dpu_plane_state *pstate = to_dpu_plane_state(drm_state); + struct dpu_plane *pdpu = to_dpu_plane(plane); + struct dpu_plane_state *old_pstate; + + if (!old_state || !old_state->fb) + return; + + old_pstate = to_dpu_plane_state(old_state); - pstate->multirect_index = DPU_SSPP_RECT_SOLO; - pstate->multirect_mode = DPU_SSPP_MULTIRECT_NONE; + DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", old_state->fb->base.id); + + msm_framebuffer_cleanup(old_state->fb, old_pstate->needs_dirtyfb); } -int dpu_plane_validate_multirect_v2(struct dpu_multirect_plane_states *plane) +static int dpu_plane_check_inline_rotation(struct dpu_plane *pdpu, + struct dpu_sw_pipe *pipe, + struct drm_rect src, + const struct msm_format *fmt) { - struct dpu_plane_state *pstate[R_MAX]; - const struct drm_plane_state *drm_state[R_MAX]; - struct drm_rect src[R_MAX], dst[R_MAX]; - struct dpu_plane *dpu_plane[R_MAX]; - const struct dpu_format *fmt[R_MAX]; - int i, buffer_lines; - unsigned int max_tile_height = 1; - bool parallel_fetch_qualified = true; - bool has_tiled_rect = false; - - for (i = 0; i < R_MAX; i++) { - const struct msm_format *msm_fmt; - - drm_state[i] = i ? plane->r1 : plane->r0; - msm_fmt = msm_framebuffer_format(drm_state[i]->fb); - fmt[i] = to_dpu_format(msm_fmt); - - if (DPU_FORMAT_IS_UBWC(fmt[i])) { - has_tiled_rect = true; - if (fmt[i]->tile_height > max_tile_height) - max_tile_height = fmt[i]->tile_height; - } + const struct dpu_sspp_sub_blks *sblk = pipe->sspp->cap->sblk; + size_t num_formats; + const u32 *supported_formats; + + if (!test_bit(DPU_SSPP_INLINE_ROTATION, &pipe->sspp->cap->features)) + return -EINVAL; + + if (!sblk->rotation_cfg) { + DPU_ERROR("invalid rotation cfg\n"); + return -EINVAL; } - for (i = 0; i < R_MAX; i++) { - int width_threshold; + if (drm_rect_width(&src) > sblk->rotation_cfg->rot_maxheight) { + DPU_DEBUG_PLANE(pdpu, "invalid height for inline rot:%d max:%d\n", + src.y2, sblk->rotation_cfg->rot_maxheight); + return -EINVAL; + } - pstate[i] = to_dpu_plane_state(drm_state[i]); - dpu_plane[i] = to_dpu_plane(drm_state[i]->plane); + supported_formats = sblk->rotation_cfg->rot_format_list; + num_formats = sblk->rotation_cfg->rot_num_formats; - if (pstate[i] == NULL) { - DPU_ERROR("DPU plane state of plane id %d is NULL\n", - drm_state[i]->plane->base.id); - return -EINVAL; - } + if (!MSM_FORMAT_IS_UBWC(fmt) || + !dpu_find_format(fmt->pixel_format, supported_formats, num_formats)) + return -EINVAL; - src[i].x1 = drm_state[i]->src_x >> 16; - src[i].y1 = drm_state[i]->src_y >> 16; - src[i].x2 = src[i].x1 + (drm_state[i]->src_w >> 16); - src[i].y2 = src[i].y1 + (drm_state[i]->src_h >> 16); + return 0; +} - dst[i] = drm_plane_state_dest(drm_state[i]); +static int dpu_plane_atomic_check_pipe(struct dpu_plane *pdpu, + struct dpu_sw_pipe *pipe, + struct dpu_sw_pipe_cfg *pipe_cfg, + const struct drm_display_mode *mode, + struct drm_plane_state *new_plane_state) +{ + uint32_t min_src_size; + struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base); + int ret; + const struct msm_format *fmt; + uint32_t supported_rotations; + const struct dpu_sspp_cfg *pipe_hw_caps; + const struct dpu_sspp_sub_blks *sblk; - if (drm_rect_calc_hscale(&src[i], &dst[i], 1, 1) != 1 || - drm_rect_calc_vscale(&src[i], &dst[i], 1, 1) != 1) { - DPU_ERROR_PLANE(dpu_plane[i], - "scaling is not supported in multirect mode\n"); - return -EINVAL; - } + pipe_hw_caps = pipe->sspp->cap; + sblk = pipe->sspp->cap->sblk; - if (DPU_FORMAT_IS_YUV(fmt[i])) { - DPU_ERROR_PLANE(dpu_plane[i], - "Unsupported format for multirect mode\n"); - return -EINVAL; - } + /* + * We already have verified scaling against platform limitations. + * Now check if the SSPP supports scaling at all. + */ + if (!(sblk->scaler_blk.len && pipe->sspp->ops.setup_scaler) && + ((drm_rect_width(&new_plane_state->src) >> 16 != + drm_rect_width(&new_plane_state->dst)) || + (drm_rect_height(&new_plane_state->src) >> 16 != + drm_rect_height(&new_plane_state->dst)))) + return -ERANGE; - /** - * SSPP PD_MEM is split half - one for each RECT. - * Tiled formats need 5 lines of buffering while fetching - * whereas linear formats need only 2 lines. - * So we cannot support more than half of the supported SSPP - * width for tiled formats. - */ - width_threshold = dpu_plane[i]->pipe_sblk->common->maxlinewidth; - if (has_tiled_rect) - width_threshold /= 2; + fmt = msm_framebuffer_format(new_plane_state->fb); - if (parallel_fetch_qualified && - drm_rect_width(&src[i]) > width_threshold) - parallel_fetch_qualified = false; + 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; - /* Validate RECT's and set the mode */ + pipe_cfg->rotation = drm_rotation_simplify(new_plane_state->rotation, + supported_rotations); - /* Prefer PARALLEL FETCH Mode over TIME_MX Mode */ - if (parallel_fetch_qualified) { - pstate[R0]->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; - pstate[R1]->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; + min_src_size = MSM_FORMAT_IS_YUV(fmt) ? 2 : 1; - goto done; + if (MSM_FORMAT_IS_YUV(fmt) && + !pipe->sspp->cap->sblk->csc_blk.len) { + DPU_DEBUG_PLANE(pdpu, + "plane doesn't have csc for yuv\n"); + return -EINVAL; } - /* TIME_MX Mode */ - buffer_lines = 2 * max_tile_height; + /* check src bounds */ + if (drm_rect_width(&pipe_cfg->src_rect) < min_src_size || + drm_rect_height(&pipe_cfg->src_rect) < min_src_size) { + DPU_DEBUG_PLANE(pdpu, "invalid source " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect)); + return -E2BIG; + } - if (dst[R1].y1 >= dst[R0].y2 + buffer_lines || - dst[R0].y1 >= dst[R1].y2 + buffer_lines) { - pstate[R0]->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX; - pstate[R1]->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX; - } else { - DPU_ERROR( - "No multirect mode possible for the planes (%d - %d)\n", - drm_state[R0]->plane->base.id, - drm_state[R1]->plane->base.id); + /* valid yuv image */ + if (MSM_FORMAT_IS_YUV(fmt) && + (pipe_cfg->src_rect.x1 & 0x1 || + pipe_cfg->src_rect.y1 & 0x1 || + drm_rect_width(&pipe_cfg->src_rect) & 0x1 || + drm_rect_height(&pipe_cfg->src_rect) & 0x1)) { + DPU_DEBUG_PLANE(pdpu, "invalid yuv source " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect)); return -EINVAL; } -done: - if (dpu_plane[R0]->is_virtual) { - pstate[R0]->multirect_index = DPU_SSPP_RECT_1; - pstate[R1]->multirect_index = DPU_SSPP_RECT_0; - } else { - pstate[R0]->multirect_index = DPU_SSPP_RECT_0; - pstate[R1]->multirect_index = DPU_SSPP_RECT_1; - }; - - DPU_DEBUG_PLANE(dpu_plane[R0], "R0: %d - %d\n", - pstate[R0]->multirect_mode, pstate[R0]->multirect_index); - DPU_DEBUG_PLANE(dpu_plane[R1], "R1: %d - %d\n", - pstate[R1]->multirect_mode, pstate[R1]->multirect_index); + /* min dst support */ + if (drm_rect_width(&pipe_cfg->dst_rect) < 0x1 || + drm_rect_height(&pipe_cfg->dst_rect) < 0x1) { + DPU_DEBUG_PLANE(pdpu, "invalid dest rect " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->dst_rect)); + return -EINVAL; + } + + if (pipe_cfg->rotation & DRM_MODE_ROTATE_90) { + ret = dpu_plane_check_inline_rotation(pdpu, pipe, pipe_cfg->src_rect, fmt); + if (ret) + return ret; + } + + /* max clk check */ + if (_dpu_plane_calc_clk(mode, pipe_cfg) > kms->perf.max_core_clk_rate) { + DPU_DEBUG_PLANE(pdpu, "plane exceeds max mdp core clk limits\n"); + return -E2BIG; + } + return 0; } -/** - * dpu_plane_get_ctl_flush - get control flush for the given plane - * @plane: Pointer to drm plane structure - * @ctl: Pointer to hardware control driver - * @flush_sspp: Pointer to sspp flush control word - */ -void dpu_plane_get_ctl_flush(struct drm_plane *plane, struct dpu_hw_ctl *ctl, - u32 *flush_sspp) -{ - *flush_sspp = ctl->ops.get_bitmask_sspp(ctl, dpu_plane_pipe(plane)); -} +#define MAX_UPSCALE_RATIO 20 +#define MAX_DOWNSCALE_RATIO 4 -static int dpu_plane_prepare_fb(struct drm_plane *plane, - struct drm_plane_state *new_state) +static int dpu_plane_atomic_check_nosspp(struct drm_plane *plane, + struct drm_plane_state *new_plane_state, + const struct drm_crtc_state *crtc_state) { - struct drm_framebuffer *fb = new_state->fb; + int i, ret = 0, min_scale, max_scale; struct dpu_plane *pdpu = to_dpu_plane(plane); - struct dpu_plane_state *pstate = to_dpu_plane_state(new_state); - struct dpu_hw_fmt_layout layout; - struct drm_gem_object *obj; - struct msm_gem_object *msm_obj; - struct dma_fence *fence; struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base); - int ret; - - if (!new_state->fb) + u64 max_mdp_clk_rate = kms->perf.max_core_clk_rate; + struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); + struct dpu_sw_pipe_cfg *pipe_cfg; + struct dpu_sw_pipe_cfg *r_pipe_cfg; + struct dpu_sw_pipe_cfg init_pipe_cfg; + struct drm_rect fb_rect = { 0 }; + const struct drm_display_mode *mode = &crtc_state->adjusted_mode; + uint32_t max_linewidth; + u32 num_lm; + int stage_id, num_stages; + + min_scale = FRAC_16_16(1, MAX_UPSCALE_RATIO); + max_scale = MAX_DOWNSCALE_RATIO << 16; + + ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state, + min_scale, + max_scale, + true, true); + if (ret) { + DPU_DEBUG_PLANE(pdpu, "Check plane state failed (%d)\n", ret); + return ret; + } + if (!new_plane_state->visible) return 0; - DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", fb->base.id); + 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; + } - /* cache aspace */ - pstate->aspace = kms->base.aspace; + num_lm = dpu_crtc_get_num_lm(crtc_state); - /* - * TODO: Need to sort out the msm_framebuffer_prepare() call below so - * we can use msm_atomic_prepare_fb() instead of doing the - * implicit fence and fb prepare by hand here. - */ - obj = msm_framebuffer_bo(new_state->fb, 0); - msm_obj = to_msm_bo(obj); - fence = reservation_object_get_excl_rcu(msm_obj->resv); - if (fence) - drm_atomic_set_fence_for_plane(new_state, fence); - - if (pstate->aspace) { - ret = msm_framebuffer_prepare(new_state->fb, - pstate->aspace); - if (ret) { - DPU_ERROR("failed to prepare framebuffer\n"); - return ret; - } + /* state->src is 16.16, src_rect is not */ + drm_rect_fp_to_int(&init_pipe_cfg.src_rect, &new_plane_state->src); + + fb_rect.x2 = new_plane_state->fb->width; + fb_rect.y2 = new_plane_state->fb->height; + + /* Ensure fb size is supported */ + if (drm_rect_width(&fb_rect) > DPU_MAX_IMG_WIDTH || + drm_rect_height(&fb_rect) > DPU_MAX_IMG_HEIGHT) { + DPU_DEBUG_PLANE(pdpu, "invalid framebuffer " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&fb_rect)); + return -E2BIG; } - /* validate framebuffer layout before commit */ - ret = dpu_format_populate_layout(pstate->aspace, - new_state->fb, &layout); + ret = dpu_format_populate_plane_sizes(new_plane_state->fb, &pstate->layout); if (ret) { - DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret); + DPU_ERROR_PLANE(pdpu, "failed to get format plane sizes, %d\n", ret); return ret; } + for (i = 0; i < pstate->layout.num_planes; i++) + if (pstate->layout.plane_pitch[i] > DPU_SSPP_MAX_PITCH_SIZE) + return -E2BIG; + + max_linewidth = pdpu->catalog->caps->max_linewidth; + + drm_rect_rotate(&init_pipe_cfg.src_rect, + new_plane_state->fb->width, new_plane_state->fb->height, + new_plane_state->rotation); + + /* + * We have 1 mixer pair cfg for 1:1:1 and 2:2:1 topology, 2 mixer pair + * configs for left and right half screen in case of 4:4:2 topology. + * But we may have 2 rect to split wide plane that exceeds limit with 1 + * config for 2:2:1. So need to handle both wide plane splitting, and + * two halves of screen splitting for quad-pipe case. Check dest + * rectangle left/right clipping first, then check wide rectangle + * splitting in every half next. + */ + num_stages = (num_lm + 1) / 2; + /* iterate mixer configs for this plane, to separate left/right with the id */ + for (stage_id = 0; stage_id < num_stages; stage_id++) { + struct drm_rect mixer_rect = { + .x1 = stage_id * mode->hdisplay / num_stages, + .y1 = 0, + .x2 = (stage_id + 1) * mode->hdisplay / num_stages, + .y2 = mode->vdisplay + }; + int cfg_idx = stage_id * PIPES_PER_STAGE; + + pipe_cfg = &pstate->pipe_cfg[cfg_idx]; + r_pipe_cfg = &pstate->pipe_cfg[cfg_idx + 1]; + + drm_rect_fp_to_int(&pipe_cfg->src_rect, &new_plane_state->src); + pipe_cfg->dst_rect = new_plane_state->dst; + + DPU_DEBUG_PLANE(pdpu, "checking src " DRM_RECT_FMT + " vs clip window " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), + DRM_RECT_ARG(&mixer_rect)); + + /* + * If this plane does not fall into mixer rect, check next + * mixer rect. + */ + if (!drm_rect_clip_scaled(&pipe_cfg->src_rect, + &pipe_cfg->dst_rect, + &mixer_rect)) { + memset(pipe_cfg, 0, 2 * sizeof(struct dpu_sw_pipe_cfg)); + + continue; + } + + pipe_cfg->dst_rect.x1 -= mixer_rect.x1; + pipe_cfg->dst_rect.x2 -= mixer_rect.x1; + + DPU_DEBUG_PLANE(pdpu, "Got clip src:" DRM_RECT_FMT " dst: " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), DRM_RECT_ARG(&pipe_cfg->dst_rect)); + + /* Split wide rect into 2 rect */ + if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) || + _dpu_plane_calc_clk(mode, pipe_cfg) > max_mdp_clk_rate) { + + if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) { + DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth); + return -E2BIG; + } + + memcpy(r_pipe_cfg, pipe_cfg, sizeof(struct dpu_sw_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; + r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2; + r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2; + DPU_DEBUG_PLANE(pdpu, "Split wide plane into:" + DRM_RECT_FMT " and " DRM_RECT_FMT "\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), + DRM_RECT_ARG(&r_pipe_cfg->src_rect)); + } else { + memset(r_pipe_cfg, 0, sizeof(struct dpu_sw_pipe_cfg)); + } + + drm_rect_rotate_inv(&pipe_cfg->src_rect, + new_plane_state->fb->width, + new_plane_state->fb->height, + new_plane_state->rotation); + + if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) + drm_rect_rotate_inv(&r_pipe_cfg->src_rect, + new_plane_state->fb->width, + new_plane_state->fb->height, + new_plane_state->rotation); + } + + pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state); + return 0; } -static void dpu_plane_cleanup_fb(struct drm_plane *plane, - struct drm_plane_state *old_state) +static int dpu_plane_is_multirect_capable(struct dpu_hw_sspp *sspp, + struct dpu_sw_pipe_cfg *pipe_cfg, + const struct msm_format *fmt) { + if (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)) + return false; + + if (pipe_cfg->rotation & DRM_MODE_ROTATE_90) + return false; + + if (MSM_FORMAT_IS_YUV(fmt)) + return false; + + if (!sspp) + return true; + + if (!test_bit(DPU_SSPP_SMART_DMA_V1, &sspp->cap->features) && + !test_bit(DPU_SSPP_SMART_DMA_V2, &sspp->cap->features)) + return false; + + return true; +} + +static int dpu_plane_is_parallel_capable(struct dpu_sw_pipe_cfg *pipe_cfg, + const struct msm_format *fmt, + uint32_t max_linewidth) +{ + if (MSM_FORMAT_IS_UBWC(fmt) && + drm_rect_width(&pipe_cfg->src_rect) > max_linewidth / 2) + return false; + + return true; +} + +static int dpu_plane_is_multirect_parallel_capable(struct dpu_hw_sspp *sspp, + struct dpu_sw_pipe_cfg *pipe_cfg, + const struct msm_format *fmt, + uint32_t max_linewidth) +{ + return dpu_plane_is_multirect_capable(sspp, pipe_cfg, fmt) && + dpu_plane_is_parallel_capable(pipe_cfg, fmt, max_linewidth); +} + +static bool dpu_plane_get_single_pipe_in_stage(struct dpu_plane_state *pstate, + struct dpu_sw_pipe **single_pipe, + struct dpu_sw_pipe_cfg **single_pipe_cfg, + int stage_index) +{ + int pipe_idx; + + pipe_idx = stage_index * PIPES_PER_STAGE; + if (drm_rect_width(&pstate->pipe_cfg[pipe_idx].src_rect) != 0 && + drm_rect_width(&pstate->pipe_cfg[pipe_idx + 1].src_rect) == 0) { + *single_pipe = &pstate->pipe[pipe_idx]; + *single_pipe_cfg = &pstate->pipe_cfg[pipe_idx]; + return true; + } + + return false; +} + +static int dpu_plane_atomic_check_sspp(struct drm_plane *plane, + struct drm_atomic_state *state, + const struct drm_crtc_state *crtc_state) +{ + struct drm_plane_state *new_plane_state = + drm_atomic_get_new_plane_state(state, plane); struct dpu_plane *pdpu = to_dpu_plane(plane); - struct dpu_plane_state *old_pstate; + struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); + struct dpu_sw_pipe *pipe; + struct dpu_sw_pipe_cfg *pipe_cfg; + int ret = 0, i; + + for (i = 0; i < PIPES_PER_PLANE; i++) { + pipe = &pstate->pipe[i]; + pipe_cfg = &pstate->pipe_cfg[i]; + if (!drm_rect_width(&pipe_cfg->src_rect)) + continue; + DPU_DEBUG_PLANE(pdpu, "pipe %d is in use, validate it\n", i); + ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg, + &crtc_state->adjusted_mode, + new_plane_state); + if (ret) + return ret; + } - if (!old_state || !old_state->fb) - return; + return 0; +} - old_pstate = to_dpu_plane_state(old_state); +static bool dpu_plane_try_multirect_parallel(struct dpu_sw_pipe *pipe, struct dpu_sw_pipe_cfg *pipe_cfg, + struct dpu_sw_pipe *r_pipe, struct dpu_sw_pipe_cfg *r_pipe_cfg, + struct dpu_hw_sspp *sspp, const struct msm_format *fmt, + uint32_t max_linewidth) +{ + r_pipe->sspp = NULL; - DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", old_state->fb->base.id); + 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; + + if (drm_rect_width(&r_pipe_cfg->src_rect) != 0) { + if (!dpu_plane_is_multirect_parallel_capable(pipe->sspp, pipe_cfg, fmt, max_linewidth) || + !dpu_plane_is_multirect_parallel_capable(pipe->sspp, r_pipe_cfg, fmt, max_linewidth)) + return false; + + r_pipe->sspp = pipe->sspp; + + pipe->multirect_index = DPU_SSPP_RECT_0; + pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; + + r_pipe->multirect_index = DPU_SSPP_RECT_1; + r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; + } - msm_framebuffer_cleanup(old_state->fb, old_pstate->aspace); + return true; } -static bool dpu_plane_validate_src(struct drm_rect *src, - struct drm_rect *fb_rect, - uint32_t min_src_size) +static int dpu_plane_try_multirect_shared(struct dpu_plane_state *pstate, + struct dpu_plane_state *prev_adjacent_pstate, + const struct msm_format *fmt, + uint32_t max_linewidth, int stage_index) { - /* Ensure fb size is supported */ - if (drm_rect_width(fb_rect) > MAX_IMG_WIDTH || - drm_rect_height(fb_rect) > MAX_IMG_HEIGHT) + struct dpu_sw_pipe *pipe, *prev_pipe; + struct dpu_sw_pipe_cfg *pipe_cfg, *prev_pipe_cfg; + const struct msm_format *prev_fmt = msm_framebuffer_format(prev_adjacent_pstate->base.fb); + u16 max_tile_height = 1; + + if (!dpu_plane_get_single_pipe_in_stage(pstate, &pipe, + &pipe_cfg, stage_index)) return false; - /* Ensure src rect is above the minimum size */ - if (drm_rect_width(src) < min_src_size || - drm_rect_height(src) < min_src_size) + if (!dpu_plane_get_single_pipe_in_stage(prev_adjacent_pstate, + &prev_pipe, &prev_pipe_cfg, + stage_index) || + prev_pipe->multirect_mode != DPU_SSPP_MULTIRECT_NONE) return false; - /* Ensure src is fully encapsulated in fb */ - return drm_rect_intersect(fb_rect, src) && - drm_rect_equals(fb_rect, src); + /* Do not validate SSPP of current plane when it is not ready */ + if (!dpu_plane_is_multirect_capable(pipe->sspp, pipe_cfg, fmt) || + !dpu_plane_is_multirect_capable(prev_pipe->sspp, prev_pipe_cfg, prev_fmt)) + return false; + + if (MSM_FORMAT_IS_UBWC(fmt)) + max_tile_height = max(max_tile_height, fmt->tile_height); + + if (MSM_FORMAT_IS_UBWC(prev_fmt)) + max_tile_height = max(max_tile_height, prev_fmt->tile_height); + + if (dpu_plane_is_parallel_capable(pipe_cfg, fmt, max_linewidth) && + dpu_plane_is_parallel_capable(prev_pipe_cfg, prev_fmt, max_linewidth) && + (pipe_cfg->dst_rect.x1 >= prev_pipe_cfg->dst_rect.x2 || + prev_pipe_cfg->dst_rect.x1 >= pipe_cfg->dst_rect.x2)) { + pipe->sspp = prev_pipe->sspp; + + pipe->multirect_index = DPU_SSPP_RECT_1; + pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; + + prev_pipe->multirect_index = DPU_SSPP_RECT_0; + prev_pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL; + + return true; + } + + if (pipe_cfg->dst_rect.y1 >= prev_pipe_cfg->dst_rect.y2 + 2 * max_tile_height || + prev_pipe_cfg->dst_rect.y1 >= pipe_cfg->dst_rect.y2 + 2 * max_tile_height) { + pipe->sspp = prev_pipe->sspp; + + pipe->multirect_index = DPU_SSPP_RECT_1; + pipe->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX; + + prev_pipe->multirect_index = DPU_SSPP_RECT_0; + prev_pipe->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX; + + return true; + } + + return false; } static int dpu_plane_atomic_check(struct drm_plane *plane, - struct drm_plane_state *state) + struct drm_atomic_state *state) { - int ret = 0, min_scale; + struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, + plane); + int ret = 0; struct dpu_plane *pdpu = to_dpu_plane(plane); + struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state); + struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + struct dpu_sw_pipe *pipe = &pstate->pipe[0]; + struct dpu_sw_pipe *r_pipe = &pstate->pipe[1]; + struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg[0]; + struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->pipe_cfg[1]; const struct drm_crtc_state *crtc_state = NULL; - const struct dpu_format *fmt; - struct drm_rect src, dst, fb_rect = { 0 }; - uint32_t min_src_size, max_linewidth; - - if (state->crtc) - crtc_state = drm_atomic_get_new_crtc_state(state->state, - state->crtc); - - min_scale = FRAC_16_16(1, pdpu->pipe_sblk->maxdwnscale); - ret = drm_atomic_helper_check_plane_state(state, crtc_state, min_scale, - pdpu->pipe_sblk->maxupscale << 16, - true, true); - if (ret) { - DPU_ERROR_PLANE(pdpu, "Check plane state failed (%d)\n", ret); + uint32_t max_linewidth = dpu_kms->catalog->caps->max_linewidth; + + if (new_plane_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, + new_plane_state->crtc); + + pipe->sspp = dpu_rm_get_sspp(&dpu_kms->rm, pdpu->pipe); + + if (!pipe->sspp) + return -EINVAL; + + ret = dpu_plane_atomic_check_nosspp(plane, new_plane_state, crtc_state); + if (ret) return ret; + + if (!new_plane_state->visible) + return 0; + + if (!dpu_plane_try_multirect_parallel(pipe, pipe_cfg, r_pipe, r_pipe_cfg, + pipe->sspp, + msm_framebuffer_format(new_plane_state->fb), + max_linewidth)) { + DPU_DEBUG_PLANE(pdpu, "invalid " DRM_RECT_FMT " /" DRM_RECT_FMT + " max_line:%u, can't use split source\n", + DRM_RECT_ARG(&pipe_cfg->src_rect), + DRM_RECT_ARG(&r_pipe_cfg->src_rect), + max_linewidth); + return -E2BIG; } - if (!state->visible) + + return dpu_plane_atomic_check_sspp(plane, state, crtc_state); +} + +static int dpu_plane_virtual_atomic_check(struct drm_plane *plane, + struct drm_atomic_state *state) +{ + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); + struct drm_plane_state *old_plane_state = + drm_atomic_get_old_plane_state(state, plane); + struct dpu_plane_state *pstate = to_dpu_plane_state(plane_state); + struct drm_crtc_state *crtc_state = NULL; + int ret, i; + + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + + if (plane_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, + plane_state->crtc); + + ret = dpu_plane_atomic_check_nosspp(plane, plane_state, crtc_state); + if (ret) + return ret; + + if (!plane_state->visible) { + /* + * resources are freed by dpu_crtc_assign_plane_resources(), + * but clean them here. + */ + for (i = 0; i < PIPES_PER_PLANE; i++) + pstate->pipe[i].sspp = NULL; + return 0; + } - src.x1 = state->src_x >> 16; - src.y1 = state->src_y >> 16; - src.x2 = src.x1 + (state->src_w >> 16); - src.y2 = src.y1 + (state->src_h >> 16); + /* + * Force resource reallocation if the format of FB or src/dst have + * changed. We might need to allocate different SSPP or SSPPs for this + * plane than the one used previously. + */ + if (!old_plane_state || !old_plane_state->fb || + old_plane_state->src_w != plane_state->src_w || + old_plane_state->src_h != plane_state->src_h || + old_plane_state->crtc_w != plane_state->crtc_w || + old_plane_state->crtc_h != plane_state->crtc_h || + msm_framebuffer_format(old_plane_state->fb) != + msm_framebuffer_format(plane_state->fb)) + crtc_state->planes_changed = true; - dst = drm_plane_state_dest(state); + return 0; +} - fb_rect.x2 = state->fb->width; - fb_rect.y2 = state->fb->height; +static int dpu_plane_assign_resource_in_stage(struct dpu_sw_pipe *pipe, + struct dpu_sw_pipe_cfg *pipe_cfg, + struct drm_plane_state *plane_state, + struct dpu_global_state *global_state, + struct drm_crtc *crtc, + struct dpu_rm_sspp_requirements *reqs) +{ + struct drm_plane *plane = plane_state->plane; + struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + struct dpu_sw_pipe *r_pipe = pipe + 1; + struct dpu_sw_pipe_cfg *r_pipe_cfg = pipe_cfg + 1; - max_linewidth = pdpu->pipe_sblk->common->maxlinewidth; + if (drm_rect_width(&pipe_cfg->src_rect) == 0) + return 0; - fmt = to_dpu_format(msm_framebuffer_format(state->fb)); + pipe->sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, reqs); + if (!pipe->sspp) + return -ENODEV; + pipe->multirect_index = DPU_SSPP_RECT_SOLO; + pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE; - min_src_size = DPU_FORMAT_IS_YUV(fmt) ? 2 : 1; + if (drm_rect_width(&r_pipe_cfg->src_rect) == 0) + return 0; - if (DPU_FORMAT_IS_YUV(fmt) && - (!(pdpu->features & DPU_SSPP_SCALER) || - !(pdpu->features & (BIT(DPU_SSPP_CSC) - | BIT(DPU_SSPP_CSC_10BIT))))) { - DPU_ERROR_PLANE(pdpu, - "plane doesn't have scaler/csc for yuv\n"); - return -EINVAL; + if (dpu_plane_try_multirect_parallel(pipe, pipe_cfg, r_pipe, r_pipe_cfg, + pipe->sspp, + msm_framebuffer_format(plane_state->fb), + dpu_kms->catalog->caps->max_linewidth)) + return 0; - /* check src bounds */ - } else if (!dpu_plane_validate_src(&src, &fb_rect, min_src_size)) { - DPU_ERROR_PLANE(pdpu, "invalid source " DRM_RECT_FMT "\n", - DRM_RECT_ARG(&src)); - return -E2BIG; + r_pipe->sspp = dpu_rm_reserve_sspp(&dpu_kms->rm, global_state, crtc, reqs); + if (!r_pipe->sspp) + return -ENODEV; + r_pipe->multirect_index = DPU_SSPP_RECT_SOLO; + r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE; - /* valid yuv image */ - } else if (DPU_FORMAT_IS_YUV(fmt) && - (src.x1 & 0x1 || src.y1 & 0x1 || - drm_rect_width(&src) & 0x1 || - drm_rect_height(&src) & 0x1)) { - DPU_ERROR_PLANE(pdpu, "invalid yuv source " DRM_RECT_FMT "\n", - DRM_RECT_ARG(&src)); - return -EINVAL; + return 0; +} - /* min dst support */ - } else if (drm_rect_width(&dst) < 0x1 || drm_rect_height(&dst) < 0x1) { - DPU_ERROR_PLANE(pdpu, "invalid dest rect " DRM_RECT_FMT "\n", - DRM_RECT_ARG(&dst)); +static int dpu_plane_virtual_assign_resources(struct drm_crtc *crtc, + struct dpu_global_state *global_state, + struct drm_atomic_state *state, + struct drm_plane_state *plane_state, + struct drm_plane_state **prev_adjacent_plane_state) +{ + const struct drm_crtc_state *crtc_state = NULL; + struct drm_plane *plane = plane_state->plane; + struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + struct dpu_rm_sspp_requirements reqs; + struct dpu_plane_state *pstate, *prev_adjacent_pstate[STAGES_PER_PLANE]; + struct dpu_sw_pipe *pipe; + struct dpu_sw_pipe_cfg *pipe_cfg; + const struct msm_format *fmt; + int i, ret; + + if (plane_state->crtc) + crtc_state = drm_atomic_get_new_crtc_state(state, + plane_state->crtc); + + pstate = to_dpu_plane_state(plane_state); + for (i = 0; i < STAGES_PER_PLANE; i++) + prev_adjacent_pstate[i] = prev_adjacent_plane_state[i] ? + to_dpu_plane_state(prev_adjacent_plane_state[i]) : NULL; + + for (i = 0; i < PIPES_PER_PLANE; i++) + pstate->pipe[i].sspp = NULL; + + if (!plane_state->fb) return -EINVAL; - /* check decimated source width */ - } else if (drm_rect_width(&src) > max_linewidth) { - DPU_ERROR_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n", - DRM_RECT_ARG(&src), max_linewidth); - return -E2BIG; + fmt = msm_framebuffer_format(plane_state->fb); + reqs.yuv = MSM_FORMAT_IS_YUV(fmt); + reqs.scale = (plane_state->src_w >> 16 != plane_state->crtc_w) || + (plane_state->src_h >> 16 != plane_state->crtc_h); + + reqs.rot90 = drm_rotation_90_or_270(plane_state->rotation); + + for (i = 0; i < STAGES_PER_PLANE; i++) { + if (prev_adjacent_pstate[i] && + dpu_plane_try_multirect_shared(pstate, prev_adjacent_pstate[i], fmt, + dpu_kms->catalog->caps->max_linewidth, + i)) + continue; + + if (dpu_plane_get_single_pipe_in_stage(pstate, &pipe, &pipe_cfg, i)) + prev_adjacent_plane_state[i] = plane_state; + + pipe = &pstate->pipe[i * PIPES_PER_STAGE]; + pipe_cfg = &pstate->pipe_cfg[i * PIPES_PER_STAGE]; + ret = dpu_plane_assign_resource_in_stage(pipe, pipe_cfg, + plane_state, + global_state, + crtc, &reqs); + if (ret) + return ret; + } + + return dpu_plane_atomic_check_sspp(plane, state, crtc_state); +} + +int dpu_assign_plane_resources(struct dpu_global_state *global_state, + struct drm_atomic_state *state, + struct drm_crtc *crtc, + struct drm_plane_state **states, + unsigned int num_planes) +{ + unsigned int i; + struct drm_plane_state *prev_adjacent_plane_state[STAGES_PER_PLANE] = { NULL }; + + for (i = 0; i < num_planes; i++) { + struct drm_plane_state *plane_state = states[i]; + + if (!plane_state || + !plane_state->visible) + continue; + + int ret = dpu_plane_virtual_assign_resources(crtc, global_state, + state, plane_state, + prev_adjacent_plane_state); + if (ret) + return ret; } return 0; } +static void dpu_plane_flush_csc(struct dpu_plane *pdpu, struct dpu_sw_pipe *pipe) +{ + const struct msm_format *format = + msm_framebuffer_format(pdpu->base.state->fb); + const struct dpu_csc_cfg *csc_ptr; + + if (!pipe->sspp || !pipe->sspp->ops.setup_csc) + return; + + csc_ptr = _dpu_plane_get_csc(pipe, format); + if (!csc_ptr) + return; + + DPU_DEBUG_PLANE(pdpu, "using 0x%X 0x%X 0x%X...\n", + csc_ptr->csc_mv[0], + csc_ptr->csc_mv[1], + csc_ptr->csc_mv[2]); + + pipe->sspp->ops.setup_csc(pipe->sspp, csc_ptr); + +} + +/** + * dpu_plane_flush - final plane operations before commit flush + * @plane: Pointer to drm plane structure + */ void dpu_plane_flush(struct drm_plane *plane) { struct dpu_plane *pdpu; struct dpu_plane_state *pstate; + int i; if (!plane || !plane->state) { DPU_ERROR("invalid plane\n"); @@ -971,8 +1430,10 @@ void dpu_plane_flush(struct drm_plane *plane) else if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG) /* force 100% alpha */ _dpu_plane_color_fill(pdpu, pdpu->color_fill, 0xFF); - else if (pdpu->pipe_hw && pdpu->csc_ptr && pdpu->pipe_hw->ops.setup_csc) - pdpu->pipe_hw->ops.setup_csc(pdpu->pipe_hw, pdpu->csc_ptr); + else { + for (i = 0; i < PIPES_PER_PLANE; i++) + dpu_plane_flush_csc(pdpu, &pstate->pipe[i]); + } /* flag h/w flush complete */ if (plane->state) @@ -982,6 +1443,7 @@ void dpu_plane_flush(struct drm_plane *plane) /** * dpu_plane_set_error: enable/disable error condition * @plane: pointer to drm_plane structure + * @error: error value to set */ void dpu_plane_set_error(struct drm_plane *plane, bool error) { @@ -994,187 +1456,175 @@ void dpu_plane_set_error(struct drm_plane *plane, bool error) pdpu->is_error = error; } -static void dpu_plane_sspp_atomic_update(struct drm_plane *plane) +static void dpu_plane_sspp_update_pipe(struct drm_plane *plane, + struct dpu_sw_pipe *pipe, + struct dpu_sw_pipe_cfg *pipe_cfg, + const struct msm_format *fmt, + int frame_rate, + struct dpu_hw_fmt_layout *layout) { uint32_t src_flags; struct dpu_plane *pdpu = to_dpu_plane(plane); struct drm_plane_state *state = plane->state; struct dpu_plane_state *pstate = to_dpu_plane_state(state); - struct drm_crtc *crtc = state->crtc; - struct drm_framebuffer *fb = state->fb; - const struct dpu_format *fmt = - to_dpu_format(msm_framebuffer_format(fb)); - - memset(&(pdpu->pipe_cfg), 0, sizeof(struct dpu_hw_pipe_cfg)); - _dpu_plane_set_scanout(plane, pstate, &pdpu->pipe_cfg, fb); - - pstate->pending = true; - - pdpu->is_rt_pipe = (dpu_crtc_get_client_type(crtc) != NRT_CLIENT); - _dpu_plane_set_qos_ctrl(plane, false, DPU_PLANE_QOS_PANIC_CTRL); - - DPU_DEBUG_PLANE(pdpu, "FB[%u] " DRM_RECT_FP_FMT "->crtc%u " DRM_RECT_FMT - ", %4.4s ubwc %d\n", fb->base.id, DRM_RECT_FP_ARG(&state->src), - crtc->base.id, DRM_RECT_ARG(&state->dst), - (char *)&fmt->base.pixel_format, DPU_FORMAT_IS_UBWC(fmt)); - - pdpu->pipe_cfg.src_rect = state->src; - - /* state->src is 16.16, src_rect is not */ - pdpu->pipe_cfg.src_rect.x1 >>= 16; - pdpu->pipe_cfg.src_rect.x2 >>= 16; - pdpu->pipe_cfg.src_rect.y1 >>= 16; - pdpu->pipe_cfg.src_rect.y2 >>= 16; - - pdpu->pipe_cfg.dst_rect = state->dst; - - _dpu_plane_setup_scaler(pdpu, pstate, fmt, false); + if (layout && pipe->sspp->ops.setup_sourceaddress) { + trace_dpu_plane_set_scanout(pipe, layout); + pipe->sspp->ops.setup_sourceaddress(pipe, layout); + } /* override for color fill */ if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG) { + _dpu_plane_set_qos_ctrl(plane, pipe, false); + /* skip remaining processing on color fill */ return; } - if (pdpu->pipe_hw->ops.setup_rects) { - pdpu->pipe_hw->ops.setup_rects(pdpu->pipe_hw, - &pdpu->pipe_cfg, - pstate->multirect_index); + if (pipe->sspp->ops.setup_rects) { + pipe->sspp->ops.setup_rects(pipe, + pipe_cfg); } - if (pdpu->pipe_hw->ops.setup_pe && - (pstate->multirect_index != DPU_SSPP_RECT_1)) - pdpu->pipe_hw->ops.setup_pe(pdpu->pipe_hw, - &pstate->pixel_ext); + _dpu_plane_setup_scaler(pipe, fmt, false, pipe_cfg); + + if (pipe->sspp->ops.setup_multirect) + pipe->sspp->ops.setup_multirect( + pipe); + + if (pipe->sspp->ops.setup_format) { + unsigned int rotation = pipe_cfg->rotation; - /** - * when programmed in multirect mode, scalar block will be - * bypassed. Still we need to update alpha and bitwidth - * ONLY for RECT0 - */ - if (pdpu->pipe_hw->ops.setup_scaler && - pstate->multirect_index != DPU_SSPP_RECT_1) - pdpu->pipe_hw->ops.setup_scaler(pdpu->pipe_hw, - &pdpu->pipe_cfg, &pstate->pixel_ext, - &pstate->scaler3_cfg); - - if (pdpu->pipe_hw->ops.setup_multirect) - pdpu->pipe_hw->ops.setup_multirect( - pdpu->pipe_hw, - pstate->multirect_index, - pstate->multirect_mode); - - if (pdpu->pipe_hw->ops.setup_format) { src_flags = 0x0; - /* update format */ - pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw, fmt, src_flags, - pstate->multirect_index); + if (rotation & DRM_MODE_REFLECT_X) + src_flags |= DPU_SSPP_FLIP_LR; - if (pdpu->pipe_hw->ops.setup_cdp) { - struct dpu_hw_pipe_cdp_cfg *cdp_cfg = &pstate->cdp_cfg; + if (rotation & DRM_MODE_REFLECT_Y) + src_flags |= DPU_SSPP_FLIP_UD; - memset(cdp_cfg, 0, sizeof(struct dpu_hw_pipe_cdp_cfg)); + if (rotation & DRM_MODE_ROTATE_90) + src_flags |= DPU_SSPP_ROT_90; - cdp_cfg->enable = pdpu->catalog->perf.cdp_cfg - [DPU_PERF_CDP_USAGE_RT].rd_enable; - cdp_cfg->ubwc_meta_enable = - DPU_FORMAT_IS_UBWC(fmt); - cdp_cfg->tile_amortize_enable = - DPU_FORMAT_IS_UBWC(fmt) || - DPU_FORMAT_IS_TILE(fmt); - cdp_cfg->preload_ahead = DPU_SSPP_CDP_PRELOAD_AHEAD_64; + /* update format */ + pipe->sspp->ops.setup_format(pipe, fmt, src_flags); - pdpu->pipe_hw->ops.setup_cdp(pdpu->pipe_hw, cdp_cfg); - } + if (pipe->sspp->ops.setup_cdp) { + const struct dpu_perf_cfg *perf = pdpu->catalog->perf; - /* update csc */ - if (DPU_FORMAT_IS_YUV(fmt)) - _dpu_plane_setup_csc(pdpu); - else - pdpu->csc_ptr = 0; + pipe->sspp->ops.setup_cdp(pipe, fmt, + perf->cdp_cfg[DPU_PERF_CDP_USAGE_RT].rd_enable); + } } - _dpu_plane_set_qos_lut(plane, fb); - _dpu_plane_set_danger_lut(plane, fb); + _dpu_plane_set_qos_lut(plane, pipe, fmt, pipe_cfg); - if (plane->type != DRM_PLANE_TYPE_CURSOR) { - _dpu_plane_set_qos_ctrl(plane, true, DPU_PLANE_QOS_PANIC_CTRL); - _dpu_plane_set_ot_limit(plane, crtc); - } + if (pipe->sspp->idx != SSPP_CURSOR0 && + pipe->sspp->idx != SSPP_CURSOR1) + _dpu_plane_set_ot_limit(plane, pipe, pipe_cfg, frame_rate); - _dpu_plane_set_qos_remap(plane); + if (pstate->needs_qos_remap) + _dpu_plane_set_qos_remap(plane, pipe); } -static void _dpu_plane_atomic_disable(struct drm_plane *plane) +static void dpu_plane_sspp_atomic_update(struct drm_plane *plane, + struct drm_plane_state *new_state) { struct dpu_plane *pdpu = to_dpu_plane(plane); struct drm_plane_state *state = plane->state; struct dpu_plane_state *pstate = to_dpu_plane_state(state); - - trace_dpu_plane_disable(DRMID(plane), is_dpu_plane_virtual(plane), - pstate->multirect_mode); + struct drm_crtc *crtc = state->crtc; + struct drm_framebuffer *fb = state->fb; + bool is_rt_pipe; + const struct msm_format *fmt = + msm_framebuffer_format(fb); + int i; pstate->pending = true; - if (is_dpu_plane_virtual(plane) && - pdpu->pipe_hw && pdpu->pipe_hw->ops.setup_multirect) - pdpu->pipe_hw->ops.setup_multirect(pdpu->pipe_hw, - DPU_SSPP_RECT_SOLO, DPU_SSPP_MULTIRECT_NONE); -} + is_rt_pipe = (dpu_crtc_get_client_type(crtc) != NRT_CLIENT); + pstate->needs_qos_remap |= (is_rt_pipe != pdpu->is_rt_pipe); + pdpu->is_rt_pipe = is_rt_pipe; -static void dpu_plane_atomic_update(struct drm_plane *plane, - struct drm_plane_state *old_state) -{ - struct dpu_plane *pdpu = to_dpu_plane(plane); - struct drm_plane_state *state = plane->state; + dpu_format_populate_addrs(new_state->fb, &pstate->layout); - pdpu->is_error = false; + DPU_DEBUG_PLANE(pdpu, "FB[%u] " DRM_RECT_FP_FMT "->crtc%u " DRM_RECT_FMT + ", %p4cc ubwc %d\n", fb->base.id, DRM_RECT_FP_ARG(&state->src), + crtc->base.id, DRM_RECT_ARG(&state->dst), + &fmt->pixel_format, MSM_FORMAT_IS_UBWC(fmt)); - DPU_DEBUG_PLANE(pdpu, "\n"); + for (i = 0; i < PIPES_PER_PLANE; i++) { + if (!drm_rect_width(&pstate->pipe_cfg[i].src_rect)) + continue; + dpu_plane_sspp_update_pipe(plane, &pstate->pipe[i], + &pstate->pipe_cfg[i], fmt, + drm_mode_vrefresh(&crtc->mode), + &pstate->layout); + } - if (!state->visible) { - _dpu_plane_atomic_disable(plane); - } else { - dpu_plane_sspp_atomic_update(plane); + if (pstate->needs_qos_remap) + pstate->needs_qos_remap = false; + + pstate->plane_fetch_bw = 0; + pstate->plane_clk = 0; + for (i = 0; i < PIPES_PER_PLANE; i++) { + if (!drm_rect_width(&pstate->pipe_cfg[i].src_rect)) + continue; + pstate->plane_fetch_bw += _dpu_plane_calc_bw(pdpu->catalog, fmt, + &crtc->mode, &pstate->pipe_cfg[i]); + + pstate->plane_clk = max(pstate->plane_clk, + _dpu_plane_calc_clk(&crtc->mode, + &pstate->pipe_cfg[i])); } } -void dpu_plane_restore(struct drm_plane *plane) +static void _dpu_plane_atomic_disable(struct drm_plane *plane) { - struct dpu_plane *pdpu; + struct drm_plane_state *state = plane->state; + struct dpu_plane_state *pstate = to_dpu_plane_state(state); + struct dpu_sw_pipe *pipe; + int i; - if (!plane || !plane->state) { - DPU_ERROR("invalid plane\n"); - return; - } + for (i = 0; i < PIPES_PER_PLANE; i += 1) { + pipe = &pstate->pipe[i]; + if (!pipe->sspp) + continue; - pdpu = to_dpu_plane(plane); + trace_dpu_plane_disable(DRMID(plane), false, + pstate->pipe[i].multirect_mode); - DPU_DEBUG_PLANE(pdpu, "\n"); + if (i % PIPES_PER_STAGE == 0) + continue; - /* last plane state is same as current state */ - dpu_plane_atomic_update(plane, plane->state); + /* + * clear multirect for the right pipe so that the SSPP + * can be further reused in the solo mode + */ + pipe->multirect_index = DPU_SSPP_RECT_SOLO; + pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE; + if (pipe->sspp->ops.setup_multirect) + pipe->sspp->ops.setup_multirect(pipe); + } + + pstate->pending = true; } -static void dpu_plane_destroy(struct drm_plane *plane) +static void dpu_plane_atomic_update(struct drm_plane *plane, + struct drm_atomic_state *state) { - struct dpu_plane *pdpu = plane ? to_dpu_plane(plane) : NULL; - - DPU_DEBUG_PLANE(pdpu, "\n"); - - if (pdpu) { - _dpu_plane_set_qos_ctrl(plane, false, DPU_PLANE_QOS_PANIC_CTRL); - - mutex_destroy(&pdpu->lock); + struct dpu_plane *pdpu = to_dpu_plane(plane); + struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, + plane); - /* this will destroy the states as well */ - drm_plane_cleanup(plane); + pdpu->is_error = false; - dpu_hw_sspp_destroy(pdpu->pipe_hw); + DPU_DEBUG_PLANE(pdpu, "\n"); - kfree(pdpu); + if (!new_state->visible) { + _dpu_plane_atomic_disable(plane); + } else { + dpu_plane_sspp_atomic_update(plane, new_state); } } @@ -1217,6 +1667,61 @@ dpu_plane_duplicate_state(struct drm_plane *plane) return &pstate->base; } +static const char * const multirect_mode_name[] = { + [DPU_SSPP_MULTIRECT_NONE] = "none", + [DPU_SSPP_MULTIRECT_PARALLEL] = "parallel", + [DPU_SSPP_MULTIRECT_TIME_MX] = "time_mx", +}; + +static const char * const multirect_index_name[] = { + [DPU_SSPP_RECT_SOLO] = "solo", + [DPU_SSPP_RECT_0] = "rect_0", + [DPU_SSPP_RECT_1] = "rect_1", +}; + +static const char *dpu_get_multirect_mode(enum dpu_sspp_multirect_mode mode) +{ + if (WARN_ON(mode >= ARRAY_SIZE(multirect_mode_name))) + return "unknown"; + + return multirect_mode_name[mode]; +} + +static const char *dpu_get_multirect_index(enum dpu_sspp_multirect_index index) +{ + if (WARN_ON(index >= ARRAY_SIZE(multirect_index_name))) + return "unknown"; + + return multirect_index_name[index]; +} + +static void dpu_plane_atomic_print_state(struct drm_printer *p, + const struct drm_plane_state *state) +{ + const struct dpu_plane_state *pstate = to_dpu_plane_state(state); + const struct dpu_sw_pipe *pipe; + const struct dpu_sw_pipe_cfg *pipe_cfg; + int i; + + drm_printf(p, "\tstage=%d\n", pstate->stage); + + for (i = 0; i < PIPES_PER_PLANE; i++) { + pipe = &pstate->pipe[i]; + if (!pipe->sspp) + continue; + pipe_cfg = &pstate->pipe_cfg[i]; + drm_printf(p, "\tsspp[%d]=%s\n", i, pipe->sspp->cap->name); + drm_printf(p, "\tmultirect_mode[%d]=%s\n", i, + dpu_get_multirect_mode(pipe->multirect_mode)); + drm_printf(p, "\tmultirect_index[%d]=%s\n", i, + dpu_get_multirect_index(pipe->multirect_index)); + drm_printf(p, "\tsrc[%d]=" DRM_RECT_FMT "\n", i, + DRM_RECT_ARG(&pipe_cfg->src_rect)); + drm_printf(p, "\tdst[%d]=" DRM_RECT_FMT "\n", i, + DRM_RECT_ARG(&pipe_cfg->dst_rect)); + } +} + static void dpu_plane_reset(struct drm_plane *plane) { struct dpu_plane *pdpu; @@ -1233,7 +1738,7 @@ static void dpu_plane_reset(struct drm_plane *plane) /* remove previous state, if present */ if (plane->state) { dpu_plane_destroy_state(plane, plane->state); - plane->state = 0; + plane->state = NULL; } pstate = kzalloc(sizeof(*pstate), GFP_KERNEL); @@ -1242,183 +1747,55 @@ static void dpu_plane_reset(struct drm_plane *plane) return; } - pstate->base.plane = plane; - - plane->state = &pstate->base; + __drm_atomic_helper_plane_reset(plane, &pstate->base); } #ifdef CONFIG_DEBUG_FS -static ssize_t _dpu_plane_danger_read(struct file *file, - char __user *buff, size_t count, loff_t *ppos) -{ - struct dpu_kms *kms = file->private_data; - int len; - char buf[40]; - - len = scnprintf(buf, sizeof(buf), "%d\n", !kms->has_danger_ctrl); - - return simple_read_from_buffer(buff, count, ppos, buf, len); -} - -static void _dpu_plane_set_danger_state(struct dpu_kms *kms, bool enable) -{ - struct drm_plane *plane; - - drm_for_each_plane(plane, kms->dev) { - if (plane->fb && plane->state) { - dpu_plane_danger_signal_ctrl(plane, enable); - DPU_DEBUG("plane:%d img:%dx%d ", - plane->base.id, plane->fb->width, - plane->fb->height); - DPU_DEBUG("src[%d,%d,%d,%d] dst[%d,%d,%d,%d]\n", - plane->state->src_x >> 16, - plane->state->src_y >> 16, - plane->state->src_w >> 16, - plane->state->src_h >> 16, - plane->state->crtc_x, plane->state->crtc_y, - plane->state->crtc_w, plane->state->crtc_h); - } else { - DPU_DEBUG("Inactive plane:%d\n", plane->base.id); - } - } -} - -static ssize_t _dpu_plane_danger_write(struct file *file, - const char __user *user_buf, size_t count, loff_t *ppos) -{ - struct dpu_kms *kms = file->private_data; - int disable_panic; - int ret; - - ret = kstrtouint_from_user(user_buf, count, 0, &disable_panic); - if (ret) - return ret; - - if (disable_panic) { - /* Disable panic signal for all active pipes */ - DPU_DEBUG("Disabling danger:\n"); - _dpu_plane_set_danger_state(kms, false); - kms->has_danger_ctrl = false; - } else { - /* Enable panic signal for all active pipes */ - DPU_DEBUG("Enabling danger:\n"); - kms->has_danger_ctrl = true; - _dpu_plane_set_danger_state(kms, true); - } - - return count; -} - -static const struct file_operations dpu_plane_danger_enable = { - .open = simple_open, - .read = _dpu_plane_danger_read, - .write = _dpu_plane_danger_write, -}; - -static int _dpu_plane_init_debugfs(struct drm_plane *plane) +void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable) { struct dpu_plane *pdpu = to_dpu_plane(plane); - struct dpu_kms *kms = _dpu_plane_get_kms(plane); - const struct dpu_sspp_cfg *cfg = pdpu->pipe_hw->cap; - const struct dpu_sspp_sub_blks *sblk = cfg->sblk; - - /* create overall sub-directory for the pipe */ - pdpu->debugfs_root = - debugfs_create_dir(pdpu->pipe_name, - plane->dev->primary->debugfs_root); - - if (!pdpu->debugfs_root) - return -ENOMEM; - - /* don't error check these */ - debugfs_create_x32("features", 0600, - pdpu->debugfs_root, &pdpu->features); - - /* add register dump support */ - dpu_debugfs_setup_regset32(&pdpu->debugfs_src, - sblk->src_blk.base + cfg->base, - sblk->src_blk.len, - kms); - dpu_debugfs_create_regset32("src_blk", 0400, - pdpu->debugfs_root, &pdpu->debugfs_src); - - if (cfg->features & BIT(DPU_SSPP_SCALER_QSEED3) || - cfg->features & BIT(DPU_SSPP_SCALER_QSEED2)) { - dpu_debugfs_setup_regset32(&pdpu->debugfs_scaler, - sblk->scaler_blk.base + cfg->base, - sblk->scaler_blk.len, - kms); - dpu_debugfs_create_regset32("scaler_blk", 0400, - pdpu->debugfs_root, - &pdpu->debugfs_scaler); - debugfs_create_bool("default_scaling", - 0600, - pdpu->debugfs_root, - &pdpu->debugfs_default_scale); - } - - if (cfg->features & BIT(DPU_SSPP_CSC) || - cfg->features & BIT(DPU_SSPP_CSC_10BIT)) { - dpu_debugfs_setup_regset32(&pdpu->debugfs_csc, - sblk->csc_blk.base + cfg->base, - sblk->csc_blk.len, - kms); - dpu_debugfs_create_regset32("csc_blk", 0400, - pdpu->debugfs_root, &pdpu->debugfs_csc); - } + struct dpu_plane_state *pstate = to_dpu_plane_state(plane->state); + struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + int i; - debugfs_create_u32("xin_id", - 0400, - pdpu->debugfs_root, - (u32 *) &cfg->xin_id); - debugfs_create_u32("clk_ctrl", - 0400, - pdpu->debugfs_root, - (u32 *) &cfg->clk_ctrl); - debugfs_create_x32("creq_vblank", - 0600, - pdpu->debugfs_root, - (u32 *) &sblk->creq_vblank); - debugfs_create_x32("danger_vblank", - 0600, - pdpu->debugfs_root, - (u32 *) &sblk->danger_vblank); - - debugfs_create_file("disable_danger", - 0600, - pdpu->debugfs_root, - kms, &dpu_plane_danger_enable); + if (!pdpu->is_rt_pipe) + return; - return 0; -} -#else -static int _dpu_plane_init_debugfs(struct drm_plane *plane) -{ - return 0; + pm_runtime_get_sync(&dpu_kms->pdev->dev); + for (i = 0; i < PIPES_PER_PLANE; i++) { + if (!pstate->pipe[i].sspp) + continue; + _dpu_plane_set_qos_ctrl(plane, &pstate->pipe[i], enable); + } + pm_runtime_put_sync(&dpu_kms->pdev->dev); } #endif -static int dpu_plane_late_register(struct drm_plane *plane) +static bool dpu_plane_format_mod_supported(struct drm_plane *plane, + uint32_t format, uint64_t modifier) { - return _dpu_plane_init_debugfs(plane); -} + struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane); + bool has_no_ubwc = (dpu_kms->mdss->ubwc_enc_version == 0) && + (dpu_kms->mdss->ubwc_dec_version == 0); -static void dpu_plane_early_unregister(struct drm_plane *plane) -{ - struct dpu_plane *pdpu = to_dpu_plane(plane); + if (modifier == DRM_FORMAT_MOD_LINEAR) + return true; - debugfs_remove_recursive(pdpu->debugfs_root); + if (modifier == DRM_FORMAT_MOD_QCOM_COMPRESSED && !has_no_ubwc) + return dpu_find_format(format, qcom_compressed_supported_formats, + ARRAY_SIZE(qcom_compressed_supported_formats)); + + return false; } static const struct drm_plane_funcs dpu_plane_funcs = { .update_plane = drm_atomic_helper_update_plane, .disable_plane = drm_atomic_helper_disable_plane, - .destroy = dpu_plane_destroy, .reset = dpu_plane_reset, .atomic_duplicate_state = dpu_plane_duplicate_state, .atomic_destroy_state = dpu_plane_destroy_state, - .late_register = dpu_plane_late_register, - .early_unregister = dpu_plane_early_unregister, + .atomic_print_state = dpu_plane_atomic_print_state, + .format_mod_supported = dpu_plane_format_mod_supported, }; static const struct drm_plane_helper_funcs dpu_plane_helper_funcs = { @@ -1428,119 +1805,154 @@ static const struct drm_plane_helper_funcs dpu_plane_helper_funcs = { .atomic_update = dpu_plane_atomic_update, }; -enum dpu_sspp dpu_plane_pipe(struct drm_plane *plane) -{ - return plane ? to_dpu_plane(plane)->pipe : SSPP_NONE; -} - -bool is_dpu_plane_virtual(struct drm_plane *plane) -{ - return plane ? to_dpu_plane(plane)->is_virtual : false; -} +static const struct drm_plane_helper_funcs dpu_plane_virtual_helper_funcs = { + .prepare_fb = dpu_plane_prepare_fb, + .cleanup_fb = dpu_plane_cleanup_fb, + .atomic_check = dpu_plane_virtual_atomic_check, + .atomic_update = dpu_plane_atomic_update, +}; /* initialize plane */ -struct drm_plane *dpu_plane_init(struct drm_device *dev, - uint32_t pipe, enum drm_plane_type type, - unsigned long possible_crtcs, u32 master_plane_id) +static struct drm_plane *dpu_plane_init_common(struct drm_device *dev, + enum drm_plane_type type, + unsigned long possible_crtcs, + bool inline_rotation, + const uint32_t *format_list, + uint32_t num_formats, + enum dpu_sspp pipe) { - struct drm_plane *plane = NULL, *master_plane = NULL; - const struct dpu_format_extended *format_list; + struct drm_plane *plane = NULL; struct dpu_plane *pdpu; struct msm_drm_private *priv = dev->dev_private; struct dpu_kms *kms = to_dpu_kms(priv->kms); - int zpos_max = DPU_ZPOS_MAX; - int ret = -EINVAL; - - /* create and zero local structure */ - pdpu = kzalloc(sizeof(*pdpu), GFP_KERNEL); - if (!pdpu) { - DPU_ERROR("[%u]failed to allocate local plane struct\n", pipe); - ret = -ENOMEM; - return ERR_PTR(ret); - } + uint32_t supported_rotations; + int ret; + + pdpu = drmm_universal_plane_alloc(dev, struct dpu_plane, base, + 0xff, &dpu_plane_funcs, + format_list, num_formats, + supported_format_modifiers, type, NULL); + if (IS_ERR(pdpu)) + return ERR_CAST(pdpu); /* cache local stuff for later */ plane = &pdpu->base; pdpu->pipe = pipe; - pdpu->is_virtual = (master_plane_id != 0); - INIT_LIST_HEAD(&pdpu->mplane_list); - master_plane = drm_plane_find(dev, NULL, master_plane_id); - if (master_plane) { - struct dpu_plane *mpdpu = to_dpu_plane(master_plane); - list_add_tail(&pdpu->mplane_list, &mpdpu->mplane_list); - } + pdpu->catalog = kms->catalog; - /* initialize underlying h/w driver */ - pdpu->pipe_hw = dpu_hw_sspp_init(pipe, kms->mmio, kms->catalog, - master_plane_id != 0); - if (IS_ERR(pdpu->pipe_hw)) { - DPU_ERROR("[%u]SSPP init failed\n", pipe); - ret = PTR_ERR(pdpu->pipe_hw); - goto clean_plane; - } else if (!pdpu->pipe_hw->cap || !pdpu->pipe_hw->cap->sblk) { - DPU_ERROR("[%u]SSPP init returned invalid cfg\n", pipe); - goto clean_sspp; - } + ret = drm_plane_create_zpos_property(plane, 0, 0, DPU_ZPOS_MAX); + if (ret) + DPU_ERROR("failed to install zpos property, rc = %d\n", ret); - /* cache features mask for later */ - pdpu->features = pdpu->pipe_hw->cap->features; - pdpu->pipe_sblk = pdpu->pipe_hw->cap->sblk; - if (!pdpu->pipe_sblk) { - DPU_ERROR("[%u]invalid sblk\n", pipe); - goto clean_sspp; - } + drm_plane_create_alpha_property(plane); + drm_plane_create_blend_mode_property(plane, + BIT(DRM_MODE_BLEND_PIXEL_NONE) | + BIT(DRM_MODE_BLEND_PREMULTI) | + BIT(DRM_MODE_BLEND_COVERAGE)); - if (!master_plane_id) - format_list = pdpu->pipe_sblk->format_list; - else - format_list = pdpu->pipe_sblk->virt_format_list; + supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180; - pdpu->nformats = dpu_populate_formats(format_list, - pdpu->formats, - 0, - ARRAY_SIZE(pdpu->formats)); + if (inline_rotation) + supported_rotations |= DRM_MODE_ROTATE_MASK; - if (!pdpu->nformats) { - DPU_ERROR("[%u]no valid formats for plane\n", pipe); - goto clean_sspp; - } + drm_plane_create_rotation_property(plane, + DRM_MODE_ROTATE_0, supported_rotations); - ret = drm_universal_plane_init(dev, plane, 0xff, &dpu_plane_funcs, - pdpu->formats, pdpu->nformats, - NULL, type, NULL); - if (ret) - goto clean_sspp; + drm_plane_enable_fb_damage_clips(plane); - pdpu->catalog = kms->catalog; + DPU_DEBUG("%s created for pipe:%u id:%u\n", plane->name, + pipe, plane->base.id); + return plane; +} - if (kms->catalog->mixer_count && - kms->catalog->mixer[0].sblk->maxblendstages) { - zpos_max = kms->catalog->mixer[0].sblk->maxblendstages - 1; - if (zpos_max > DPU_STAGE_MAX - DPU_STAGE_0 - 1) - zpos_max = DPU_STAGE_MAX - DPU_STAGE_0 - 1; +/** + * dpu_plane_init - create new dpu plane for the given pipe + * @dev: Pointer to DRM device + * @pipe: dpu hardware pipe identifier + * @type: Plane type - PRIMARY/OVERLAY/CURSOR + * @possible_crtcs: bitmask of crtc that can be attached to the given pipe + * + * Initialize the plane. + */ +struct drm_plane *dpu_plane_init(struct drm_device *dev, + uint32_t pipe, enum drm_plane_type type, + unsigned long possible_crtcs) +{ + struct drm_plane *plane = NULL; + struct msm_drm_private *priv = dev->dev_private; + struct dpu_kms *kms = to_dpu_kms(priv->kms); + struct dpu_hw_sspp *pipe_hw; + + /* initialize underlying h/w driver */ + pipe_hw = dpu_rm_get_sspp(&kms->rm, pipe); + if (!pipe_hw || !pipe_hw->cap || !pipe_hw->cap->sblk) { + DPU_ERROR("[%u]SSPP is invalid\n", pipe); + return ERR_PTR(-EINVAL); } - ret = drm_plane_create_zpos_property(plane, 0, 0, zpos_max); - if (ret) - DPU_ERROR("failed to install zpos property, rc = %d\n", ret); - /* success! finalize initialization */ - drm_plane_helper_add(plane, &dpu_plane_helper_funcs); + plane = dpu_plane_init_common(dev, type, possible_crtcs, + pipe_hw->cap->features & BIT(DPU_SSPP_INLINE_ROTATION), + pipe_hw->cap->sblk->format_list, + pipe_hw->cap->sblk->num_formats, + pipe); + if (IS_ERR(plane)) + return plane; - /* save user friendly pipe name for later */ - snprintf(pdpu->pipe_name, DPU_NAME_SIZE, "plane%u", plane->base.id); + drm_plane_helper_add(plane, &dpu_plane_helper_funcs); - mutex_init(&pdpu->lock); + DPU_DEBUG("%s created for pipe:%u id:%u\n", plane->name, + pipe, plane->base.id); - DPU_DEBUG("%s created for pipe:%u id:%u virtual:%u\n", pdpu->pipe_name, - pipe, plane->base.id, master_plane_id); return plane; +} + +/** + * dpu_plane_init_virtual - create new virtualized DPU plane + * @dev: Pointer to DRM device + * @type: Plane type - PRIMARY/OVERLAY/CURSOR + * @possible_crtcs: bitmask of crtc that can be attached to the given pipe + * + * Initialize the virtual plane with no backing SSPP / pipe. + */ +struct drm_plane *dpu_plane_init_virtual(struct drm_device *dev, + enum drm_plane_type type, + unsigned long possible_crtcs) +{ + struct drm_plane *plane = NULL; + struct msm_drm_private *priv = dev->dev_private; + struct dpu_kms *kms = to_dpu_kms(priv->kms); + bool has_inline_rotation = false; + const u32 *format_list = NULL; + u32 num_formats = 0; + int i; -clean_sspp: - if (pdpu && pdpu->pipe_hw) - dpu_hw_sspp_destroy(pdpu->pipe_hw); -clean_plane: - kfree(pdpu); - return ERR_PTR(ret); + /* Determine the largest configuration that we can implement */ + for (i = 0; i < kms->catalog->sspp_count; i++) { + const struct dpu_sspp_cfg *cfg = &kms->catalog->sspp[i]; + + if (test_bit(DPU_SSPP_INLINE_ROTATION, &cfg->features)) + has_inline_rotation = true; + + if (!format_list || + cfg->sblk->csc_blk.len) { + format_list = cfg->sblk->format_list; + num_formats = cfg->sblk->num_formats; + } + } + + plane = dpu_plane_init_common(dev, type, possible_crtcs, + has_inline_rotation, + format_list, + num_formats, + SSPP_NONE); + if (IS_ERR(plane)) + return plane; + + drm_plane_helper_add(plane, &dpu_plane_virtual_helper_funcs); + + DPU_DEBUG("%s created virtual id:%u\n", plane->name, plane->base.id); + + return plane; } |
