1456 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2014-2018 The Linux Foundation. All rights reserved.4 * Copyright (C) 2013 Red Hat5 * Author: Rob Clark <robdclark@gmail.com>6 */7 8#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__9 10#include <linux/debugfs.h>11#include <linux/dma-buf.h>12 13#include <drm/drm_atomic.h>14#include <drm/drm_atomic_uapi.h>15#include <drm/drm_blend.h>16#include <drm/drm_damage_helper.h>17#include <drm/drm_framebuffer.h>18#include <drm/drm_gem_atomic_helper.h>19 20#include "msm_drv.h"21#include "msm_mdss.h"22#include "dpu_kms.h"23#include "dpu_formats.h"24#include "dpu_hw_sspp.h"25#include "dpu_hw_util.h"26#include "dpu_trace.h"27#include "dpu_crtc.h"28#include "dpu_vbif.h"29#include "dpu_plane.h"30 31#define DPU_DEBUG_PLANE(pl, fmt, ...) DRM_DEBUG_ATOMIC("plane%d " fmt,\32 (pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)33 34#define DPU_ERROR_PLANE(pl, fmt, ...) DPU_ERROR("plane%d " fmt,\35 (pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)36 37#define DECIMATED_DIMENSION(dim, deci) (((dim) + ((1 << (deci)) - 1)) >> (deci))38#define PHASE_STEP_SHIFT 2139#define PHASE_STEP_UNIT_SCALE ((int) (1 << PHASE_STEP_SHIFT))40#define PHASE_RESIDUAL 1541 42#define SHARP_STRENGTH_DEFAULT 3243#define SHARP_EDGE_THR_DEFAULT 11244#define SHARP_SMOOTH_THR_DEFAULT 845#define SHARP_NOISE_THR_DEFAULT 246 47#define DPU_PLANE_COLOR_FILL_FLAG BIT(31)48#define DPU_ZPOS_MAX 25549 50/*51 * Default Preload Values52 */53#define DPU_QSEED3_DEFAULT_PRELOAD_H 0x454#define DPU_QSEED3_DEFAULT_PRELOAD_V 0x355#define DPU_QSEED4_DEFAULT_PRELOAD_V 0x256#define DPU_QSEED4_DEFAULT_PRELOAD_H 0x457 58#define DEFAULT_REFRESH_RATE 6059 60static const uint32_t qcom_compressed_supported_formats[] = {61 DRM_FORMAT_ABGR8888,62 DRM_FORMAT_ARGB8888,63 DRM_FORMAT_XBGR8888,64 DRM_FORMAT_XRGB8888,65 DRM_FORMAT_ARGB2101010,66 DRM_FORMAT_XRGB2101010,67 DRM_FORMAT_BGR565,68 69 DRM_FORMAT_NV12,70 DRM_FORMAT_P010,71};72 73/*74 * struct dpu_plane - local dpu plane structure75 * @aspace: address space pointer76 * @csc_ptr: Points to dpu_csc_cfg structure to use for current77 * @catalog: Points to dpu catalog structure78 * @revalidate: force revalidation of all the plane properties79 */80struct dpu_plane {81 struct drm_plane base;82 83 enum dpu_sspp pipe;84 85 uint32_t color_fill;86 bool is_error;87 bool is_rt_pipe;88 const struct dpu_mdss_cfg *catalog;89};90 91static const uint64_t supported_format_modifiers[] = {92 DRM_FORMAT_MOD_QCOM_COMPRESSED,93 DRM_FORMAT_MOD_LINEAR,94 DRM_FORMAT_MOD_INVALID95};96 97#define to_dpu_plane(x) container_of(x, struct dpu_plane, base)98 99static struct dpu_kms *_dpu_plane_get_kms(struct drm_plane *plane)100{101 struct msm_drm_private *priv = plane->dev->dev_private;102 103 return to_dpu_kms(priv->kms);104}105 106/**107 * _dpu_plane_calc_bw - calculate bandwidth required for a plane108 * @catalog: Points to dpu catalog structure109 * @fmt: Pointer to source buffer format110 * @mode: Pointer to drm display mode111 * @pipe_cfg: Pointer to pipe configuration112 * Result: Updates calculated bandwidth in the plane state.113 * BW Equation: src_w * src_h * bpp * fps * (v_total / v_dest)114 * Prefill BW Equation: line src bytes * line_time115 */116static u64 _dpu_plane_calc_bw(const struct dpu_mdss_cfg *catalog,117 const struct msm_format *fmt,118 const struct drm_display_mode *mode,119 struct dpu_sw_pipe_cfg *pipe_cfg)120{121 int src_width, src_height, dst_height, fps;122 u64 plane_pixel_rate, plane_bit_rate;123 u64 plane_prefill_bw;124 u64 plane_bw;125 u32 hw_latency_lines;126 u64 scale_factor;127 int vbp, vpw, vfp;128 129 src_width = drm_rect_width(&pipe_cfg->src_rect);130 src_height = drm_rect_height(&pipe_cfg->src_rect);131 dst_height = drm_rect_height(&pipe_cfg->dst_rect);132 fps = drm_mode_vrefresh(mode);133 vbp = mode->vtotal - mode->vsync_end;134 vpw = mode->vsync_end - mode->vsync_start;135 vfp = mode->vsync_start - mode->vdisplay;136 hw_latency_lines = catalog->perf->min_prefill_lines;137 scale_factor = src_height > dst_height ?138 mult_frac(src_height, 1, dst_height) : 1;139 140 plane_pixel_rate = src_width * mode->vtotal * fps;141 plane_bit_rate = plane_pixel_rate * fmt->bpp;142 143 plane_bw = plane_bit_rate * scale_factor;144 145 plane_prefill_bw = plane_bw * hw_latency_lines;146 147 if ((vbp+vpw) > hw_latency_lines)148 do_div(plane_prefill_bw, (vbp+vpw));149 else if ((vbp+vpw+vfp) < hw_latency_lines)150 do_div(plane_prefill_bw, (vbp+vpw+vfp));151 else152 do_div(plane_prefill_bw, hw_latency_lines);153 154 155 return max(plane_bw, plane_prefill_bw);156}157 158/**159 * _dpu_plane_calc_clk - calculate clock required for a plane160 * @mode: Pointer to drm display mode161 * @pipe_cfg: Pointer to pipe configuration162 * Result: Updates calculated clock in the plane state.163 * Clock equation: dst_w * v_total * fps * (src_h / dst_h)164 */165static u64 _dpu_plane_calc_clk(const struct drm_display_mode *mode,166 struct dpu_sw_pipe_cfg *pipe_cfg)167{168 int dst_width, src_height, dst_height, fps;169 u64 plane_clk;170 171 src_height = drm_rect_height(&pipe_cfg->src_rect);172 dst_width = drm_rect_width(&pipe_cfg->dst_rect);173 dst_height = drm_rect_height(&pipe_cfg->dst_rect);174 fps = drm_mode_vrefresh(mode);175 176 plane_clk =177 dst_width * mode->vtotal * fps;178 179 if (src_height > dst_height) {180 plane_clk *= src_height;181 do_div(plane_clk, dst_height);182 }183 184 return plane_clk;185}186 187/**188 * _dpu_plane_calc_fill_level - calculate fill level of the given source format189 * @plane: Pointer to drm plane190 * @pipe: Pointer to software pipe191 * @lut_usage: LUT usecase192 * @fmt: Pointer to source buffer format193 * @src_width: width of source buffer194 * Return: fill level corresponding to the source buffer/format or 0 if error195 */196static int _dpu_plane_calc_fill_level(struct drm_plane *plane,197 struct dpu_sw_pipe *pipe,198 enum dpu_qos_lut_usage lut_usage,199 const struct msm_format *fmt, u32 src_width)200{201 struct dpu_plane *pdpu;202 u32 fixed_buff_size;203 u32 total_fl;204 205 if (!fmt || !pipe || !src_width || !fmt->bpp) {206 DPU_ERROR("invalid arguments\n");207 return 0;208 }209 210 if (lut_usage == DPU_QOS_LUT_USAGE_NRT)211 return 0;212 213 pdpu = to_dpu_plane(plane);214 fixed_buff_size = pdpu->catalog->caps->pixel_ram_size;215 216 /* FIXME: in multirect case account for the src_width of all the planes */217 218 if (fmt->fetch_type == MDP_PLANE_PSEUDO_PLANAR) {219 if (fmt->chroma_sample == CHROMA_420) {220 /* NV12 */221 total_fl = (fixed_buff_size / 2) /222 ((src_width + 32) * fmt->bpp);223 } else {224 /* non NV12 */225 total_fl = (fixed_buff_size / 2) * 2 /226 ((src_width + 32) * fmt->bpp);227 }228 } else {229 if (pipe->multirect_mode == DPU_SSPP_MULTIRECT_PARALLEL) {230 total_fl = (fixed_buff_size / 2) * 2 /231 ((src_width + 32) * fmt->bpp);232 } else {233 total_fl = (fixed_buff_size) * 2 /234 ((src_width + 32) * fmt->bpp);235 }236 }237 238 DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %p4cc w:%u fl:%u\n",239 pipe->sspp->idx - SSPP_VIG0,240 &fmt->pixel_format,241 src_width, total_fl);242 243 return total_fl;244}245 246/**247 * _dpu_plane_set_qos_lut - set QoS LUT of the given plane248 * @plane: Pointer to drm plane249 * @pipe: Pointer to software pipe250 * @fmt: Pointer to source buffer format251 * @pipe_cfg: Pointer to pipe configuration252 */253static void _dpu_plane_set_qos_lut(struct drm_plane *plane,254 struct dpu_sw_pipe *pipe,255 const struct msm_format *fmt, struct dpu_sw_pipe_cfg *pipe_cfg)256{257 struct dpu_plane *pdpu = to_dpu_plane(plane);258 struct dpu_hw_qos_cfg cfg;259 u32 total_fl, lut_usage;260 261 if (!pdpu->is_rt_pipe) {262 lut_usage = DPU_QOS_LUT_USAGE_NRT;263 } else {264 if (fmt && MSM_FORMAT_IS_LINEAR(fmt))265 lut_usage = DPU_QOS_LUT_USAGE_LINEAR;266 else267 lut_usage = DPU_QOS_LUT_USAGE_MACROTILE;268 }269 270 total_fl = _dpu_plane_calc_fill_level(plane, pipe, lut_usage, fmt,271 drm_rect_width(&pipe_cfg->src_rect));272 273 cfg.creq_lut = _dpu_hw_get_qos_lut(&pdpu->catalog->perf->qos_lut_tbl[lut_usage], total_fl);274 cfg.danger_lut = pdpu->catalog->perf->danger_lut_tbl[lut_usage];275 cfg.safe_lut = pdpu->catalog->perf->safe_lut_tbl[lut_usage];276 277 if (pipe->sspp->idx != SSPP_CURSOR0 &&278 pipe->sspp->idx != SSPP_CURSOR1 &&279 pdpu->is_rt_pipe)280 cfg.danger_safe_en = true;281 282 DPU_DEBUG_PLANE(pdpu, "pnum:%d ds:%d is_rt:%d\n",283 pdpu->pipe - SSPP_VIG0,284 cfg.danger_safe_en,285 pdpu->is_rt_pipe);286 287 trace_dpu_perf_set_qos_luts(pipe->sspp->idx - SSPP_VIG0,288 (fmt) ? fmt->pixel_format : 0,289 pdpu->is_rt_pipe, total_fl, cfg.creq_lut, lut_usage);290 291 DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %p4cc rt:%d fl:%u lut:0x%llx\n",292 pdpu->pipe - SSPP_VIG0,293 fmt ? &fmt->pixel_format : NULL,294 pdpu->is_rt_pipe, total_fl, cfg.creq_lut);295 296 trace_dpu_perf_set_danger_luts(pdpu->pipe - SSPP_VIG0,297 (fmt) ? fmt->pixel_format : 0,298 (fmt) ? fmt->fetch_mode : 0,299 cfg.danger_lut,300 cfg.safe_lut);301 302 DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %p4cc mode:%d luts[0x%x, 0x%x]\n",303 pdpu->pipe - SSPP_VIG0,304 fmt ? &fmt->pixel_format : NULL,305 fmt ? fmt->fetch_mode : -1,306 cfg.danger_lut,307 cfg.safe_lut);308 309 pipe->sspp->ops.setup_qos_lut(pipe->sspp, &cfg);310}311 312/**313 * _dpu_plane_set_qos_ctrl - set QoS control of the given plane314 * @plane: Pointer to drm plane315 * @pipe: Pointer to software pipe316 * @enable: true to enable QoS control317 */318static void _dpu_plane_set_qos_ctrl(struct drm_plane *plane,319 struct dpu_sw_pipe *pipe,320 bool enable)321{322 struct dpu_plane *pdpu = to_dpu_plane(plane);323 324 if (!pdpu->is_rt_pipe)325 enable = false;326 327 DPU_DEBUG_PLANE(pdpu, "pnum:%d ds:%d is_rt:%d\n",328 pdpu->pipe - SSPP_VIG0,329 enable,330 pdpu->is_rt_pipe);331 332 pipe->sspp->ops.setup_qos_ctrl(pipe->sspp,333 enable);334}335 336static bool _dpu_plane_sspp_clk_force_ctrl(struct dpu_hw_sspp *sspp,337 struct dpu_hw_mdp *mdp,338 bool enable, bool *forced_on)339{340 if (sspp->ops.setup_clk_force_ctrl) {341 *forced_on = sspp->ops.setup_clk_force_ctrl(sspp, enable);342 return true;343 }344 345 if (mdp->ops.setup_clk_force_ctrl) {346 *forced_on = mdp->ops.setup_clk_force_ctrl(mdp, sspp->cap->clk_ctrl, enable);347 return true;348 }349 350 return false;351}352 353/**354 * _dpu_plane_set_ot_limit - set OT limit for the given plane355 * @plane: Pointer to drm plane356 * @pipe: Pointer to software pipe357 * @pipe_cfg: Pointer to pipe configuration358 * @frame_rate: CRTC's frame rate359 */360static void _dpu_plane_set_ot_limit(struct drm_plane *plane,361 struct dpu_sw_pipe *pipe,362 struct dpu_sw_pipe_cfg *pipe_cfg,363 int frame_rate)364{365 struct dpu_plane *pdpu = to_dpu_plane(plane);366 struct dpu_vbif_set_ot_params ot_params;367 struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);368 bool forced_on = false;369 370 memset(&ot_params, 0, sizeof(ot_params));371 ot_params.xin_id = pipe->sspp->cap->xin_id;372 ot_params.num = pipe->sspp->idx - SSPP_NONE;373 ot_params.width = drm_rect_width(&pipe_cfg->src_rect);374 ot_params.height = drm_rect_height(&pipe_cfg->src_rect);375 ot_params.is_wfd = !pdpu->is_rt_pipe;376 ot_params.frame_rate = frame_rate;377 ot_params.vbif_idx = VBIF_RT;378 ot_params.rd = true;379 380 if (!_dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp,381 true, &forced_on))382 return;383 384 dpu_vbif_set_ot_limit(dpu_kms, &ot_params);385 386 if (forced_on)387 _dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp,388 false, &forced_on);389}390 391/**392 * _dpu_plane_set_qos_remap - set vbif QoS for the given plane393 * @plane: Pointer to drm plane394 * @pipe: Pointer to software pipe395 */396static void _dpu_plane_set_qos_remap(struct drm_plane *plane,397 struct dpu_sw_pipe *pipe)398{399 struct dpu_plane *pdpu = to_dpu_plane(plane);400 struct dpu_vbif_set_qos_params qos_params;401 struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);402 bool forced_on = false;403 404 memset(&qos_params, 0, sizeof(qos_params));405 qos_params.vbif_idx = VBIF_RT;406 qos_params.xin_id = pipe->sspp->cap->xin_id;407 qos_params.num = pipe->sspp->idx - SSPP_VIG0;408 qos_params.is_rt = pdpu->is_rt_pipe;409 410 DPU_DEBUG_PLANE(pdpu, "pipe:%d vbif:%d xin:%d rt:%d\n",411 qos_params.num,412 qos_params.vbif_idx,413 qos_params.xin_id, qos_params.is_rt);414 415 if (!_dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp,416 true, &forced_on))417 return;418 419 dpu_vbif_set_qos_remap(dpu_kms, &qos_params);420 421 if (forced_on)422 _dpu_plane_sspp_clk_force_ctrl(pipe->sspp, dpu_kms->hw_mdp,423 false, &forced_on);424}425 426static void _dpu_plane_setup_scaler3(struct dpu_hw_sspp *pipe_hw,427 uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h,428 struct dpu_hw_scaler3_cfg *scale_cfg,429 const struct msm_format *fmt,430 uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v,431 unsigned int rotation)432{433 uint32_t i;434 bool inline_rotation = rotation & DRM_MODE_ROTATE_90;435 436 /*437 * For inline rotation cases, scaler config is post-rotation,438 * so swap the dimensions here. However, pixel extension will439 * need pre-rotation settings.440 */441 if (inline_rotation)442 swap(src_w, src_h);443 444 scale_cfg->phase_step_x[DPU_SSPP_COMP_0] =445 mult_frac((1 << PHASE_STEP_SHIFT), src_w, dst_w);446 scale_cfg->phase_step_y[DPU_SSPP_COMP_0] =447 mult_frac((1 << PHASE_STEP_SHIFT), src_h, dst_h);448 449 450 scale_cfg->phase_step_y[DPU_SSPP_COMP_1_2] =451 scale_cfg->phase_step_y[DPU_SSPP_COMP_0] / chroma_subsmpl_v;452 scale_cfg->phase_step_x[DPU_SSPP_COMP_1_2] =453 scale_cfg->phase_step_x[DPU_SSPP_COMP_0] / chroma_subsmpl_h;454 455 scale_cfg->phase_step_x[DPU_SSPP_COMP_2] =456 scale_cfg->phase_step_x[DPU_SSPP_COMP_1_2];457 scale_cfg->phase_step_y[DPU_SSPP_COMP_2] =458 scale_cfg->phase_step_y[DPU_SSPP_COMP_1_2];459 460 scale_cfg->phase_step_x[DPU_SSPP_COMP_3] =461 scale_cfg->phase_step_x[DPU_SSPP_COMP_0];462 scale_cfg->phase_step_y[DPU_SSPP_COMP_3] =463 scale_cfg->phase_step_y[DPU_SSPP_COMP_0];464 465 for (i = 0; i < DPU_MAX_PLANES; i++) {466 scale_cfg->src_width[i] = src_w;467 scale_cfg->src_height[i] = src_h;468 if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) {469 scale_cfg->src_width[i] /= chroma_subsmpl_h;470 scale_cfg->src_height[i] /= chroma_subsmpl_v;471 }472 473 if (pipe_hw->cap->sblk->scaler_blk.version >= 0x3000) {474 scale_cfg->preload_x[i] = DPU_QSEED4_DEFAULT_PRELOAD_H;475 scale_cfg->preload_y[i] = DPU_QSEED4_DEFAULT_PRELOAD_V;476 } else {477 scale_cfg->preload_x[i] = DPU_QSEED3_DEFAULT_PRELOAD_H;478 scale_cfg->preload_y[i] = DPU_QSEED3_DEFAULT_PRELOAD_V;479 }480 }481 if (!(MSM_FORMAT_IS_YUV(fmt)) && (src_h == dst_h)482 && (src_w == dst_w))483 return;484 485 scale_cfg->dst_width = dst_w;486 scale_cfg->dst_height = dst_h;487 scale_cfg->y_rgb_filter_cfg = DPU_SCALE_BIL;488 scale_cfg->uv_filter_cfg = DPU_SCALE_BIL;489 scale_cfg->alpha_filter_cfg = DPU_SCALE_ALPHA_BIL;490 scale_cfg->lut_flag = 0;491 scale_cfg->blend_cfg = 1;492 scale_cfg->enable = 1;493}494 495static void _dpu_plane_setup_pixel_ext(struct dpu_hw_scaler3_cfg *scale_cfg,496 struct dpu_hw_pixel_ext *pixel_ext,497 uint32_t src_w, uint32_t src_h,498 uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v)499{500 int i;501 502 for (i = 0; i < DPU_MAX_PLANES; i++) {503 if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) {504 src_w /= chroma_subsmpl_h;505 src_h /= chroma_subsmpl_v;506 }507 508 pixel_ext->num_ext_pxls_top[i] = src_h;509 pixel_ext->num_ext_pxls_left[i] = src_w;510 }511}512 513static const struct dpu_csc_cfg *_dpu_plane_get_csc(struct dpu_sw_pipe *pipe,514 const struct msm_format *fmt)515{516 const struct dpu_csc_cfg *csc_ptr;517 518 if (!MSM_FORMAT_IS_YUV(fmt))519 return NULL;520 521 if (BIT(DPU_SSPP_CSC_10BIT) & pipe->sspp->cap->features)522 csc_ptr = &dpu_csc10_YUV2RGB_601L;523 else524 csc_ptr = &dpu_csc_YUV2RGB_601L;525 526 return csc_ptr;527}528 529static void _dpu_plane_setup_scaler(struct dpu_sw_pipe *pipe,530 const struct msm_format *fmt, bool color_fill,531 struct dpu_sw_pipe_cfg *pipe_cfg,532 unsigned int rotation)533{534 struct dpu_hw_sspp *pipe_hw = pipe->sspp;535 const struct drm_format_info *info = drm_format_info(fmt->pixel_format);536 struct dpu_hw_scaler3_cfg scaler3_cfg;537 struct dpu_hw_pixel_ext pixel_ext;538 u32 src_width = drm_rect_width(&pipe_cfg->src_rect);539 u32 src_height = drm_rect_height(&pipe_cfg->src_rect);540 u32 dst_width = drm_rect_width(&pipe_cfg->dst_rect);541 u32 dst_height = drm_rect_height(&pipe_cfg->dst_rect);542 543 memset(&scaler3_cfg, 0, sizeof(scaler3_cfg));544 memset(&pixel_ext, 0, sizeof(pixel_ext));545 546 /* don't chroma subsample if decimating */547 /* update scaler. calculate default config for QSEED3 */548 _dpu_plane_setup_scaler3(pipe_hw,549 src_width,550 src_height,551 dst_width,552 dst_height,553 &scaler3_cfg, fmt,554 info->hsub, info->vsub,555 rotation);556 557 /* configure pixel extension based on scalar config */558 _dpu_plane_setup_pixel_ext(&scaler3_cfg, &pixel_ext,559 src_width, src_height, info->hsub, info->vsub);560 561 if (pipe_hw->ops.setup_pe)562 pipe_hw->ops.setup_pe(pipe_hw,563 &pixel_ext);564 565 /**566 * when programmed in multirect mode, scalar block will be567 * bypassed. Still we need to update alpha and bitwidth568 * ONLY for RECT0569 */570 if (pipe_hw->ops.setup_scaler &&571 pipe->multirect_index != DPU_SSPP_RECT_1)572 pipe_hw->ops.setup_scaler(pipe_hw,573 &scaler3_cfg,574 fmt);575}576 577static void _dpu_plane_color_fill_pipe(struct dpu_plane_state *pstate,578 struct dpu_sw_pipe *pipe,579 struct drm_rect *dst_rect,580 u32 fill_color,581 const struct msm_format *fmt)582{583 struct dpu_sw_pipe_cfg pipe_cfg;584 585 /* update sspp */586 if (!pipe->sspp->ops.setup_solidfill)587 return;588 589 pipe->sspp->ops.setup_solidfill(pipe, fill_color);590 591 /* override scaler/decimation if solid fill */592 pipe_cfg.dst_rect = *dst_rect;593 594 pipe_cfg.src_rect.x1 = 0;595 pipe_cfg.src_rect.y1 = 0;596 pipe_cfg.src_rect.x2 =597 drm_rect_width(&pipe_cfg.dst_rect);598 pipe_cfg.src_rect.y2 =599 drm_rect_height(&pipe_cfg.dst_rect);600 601 if (pipe->sspp->ops.setup_format)602 pipe->sspp->ops.setup_format(pipe, fmt, DPU_SSPP_SOLID_FILL);603 604 if (pipe->sspp->ops.setup_rects)605 pipe->sspp->ops.setup_rects(pipe, &pipe_cfg);606 607 _dpu_plane_setup_scaler(pipe, fmt, true, &pipe_cfg, pstate->rotation);608}609 610/**611 * _dpu_plane_color_fill - enables color fill on plane612 * @pdpu: Pointer to DPU plane object613 * @color: RGB fill color value, [23..16] Blue, [15..8] Green, [7..0] Red614 * @alpha: 8-bit fill alpha value, 255 selects 100% alpha615 */616static void _dpu_plane_color_fill(struct dpu_plane *pdpu,617 uint32_t color, uint32_t alpha)618{619 const struct msm_format *fmt;620 const struct drm_plane *plane = &pdpu->base;621 struct msm_drm_private *priv = plane->dev->dev_private;622 struct dpu_plane_state *pstate = to_dpu_plane_state(plane->state);623 u32 fill_color = (color & 0xFFFFFF) | ((alpha & 0xFF) << 24);624 625 DPU_DEBUG_PLANE(pdpu, "\n");626 627 /*628 * select fill format to match user property expectation,629 * h/w only supports RGB variants630 */631 fmt = mdp_get_format(priv->kms, DRM_FORMAT_ABGR8888, 0);632 /* should not happen ever */633 if (!fmt)634 return;635 636 /* update sspp */637 _dpu_plane_color_fill_pipe(pstate, &pstate->pipe, &pstate->pipe_cfg.dst_rect,638 fill_color, fmt);639 640 if (pstate->r_pipe.sspp)641 _dpu_plane_color_fill_pipe(pstate, &pstate->r_pipe, &pstate->r_pipe_cfg.dst_rect,642 fill_color, fmt);643}644 645static int dpu_plane_prepare_fb(struct drm_plane *plane,646 struct drm_plane_state *new_state)647{648 struct drm_framebuffer *fb = new_state->fb;649 struct dpu_plane *pdpu = to_dpu_plane(plane);650 struct dpu_plane_state *pstate = to_dpu_plane_state(new_state);651 struct dpu_hw_fmt_layout layout;652 struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);653 int ret;654 655 if (!new_state->fb)656 return 0;657 658 DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", fb->base.id);659 660 /* cache aspace */661 pstate->aspace = kms->base.aspace;662 663 /*664 * TODO: Need to sort out the msm_framebuffer_prepare() call below so665 * we can use msm_atomic_prepare_fb() instead of doing the666 * implicit fence and fb prepare by hand here.667 */668 drm_gem_plane_helper_prepare_fb(plane, new_state);669 670 if (pstate->aspace) {671 ret = msm_framebuffer_prepare(new_state->fb,672 pstate->aspace, pstate->needs_dirtyfb);673 if (ret) {674 DPU_ERROR("failed to prepare framebuffer\n");675 return ret;676 }677 }678 679 /* validate framebuffer layout before commit */680 ret = dpu_format_populate_layout(pstate->aspace,681 new_state->fb, &layout);682 if (ret) {683 DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);684 if (pstate->aspace)685 msm_framebuffer_cleanup(new_state->fb, pstate->aspace,686 pstate->needs_dirtyfb);687 return ret;688 }689 690 return 0;691}692 693static void dpu_plane_cleanup_fb(struct drm_plane *plane,694 struct drm_plane_state *old_state)695{696 struct dpu_plane *pdpu = to_dpu_plane(plane);697 struct dpu_plane_state *old_pstate;698 699 if (!old_state || !old_state->fb)700 return;701 702 old_pstate = to_dpu_plane_state(old_state);703 704 DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", old_state->fb->base.id);705 706 msm_framebuffer_cleanup(old_state->fb, old_pstate->aspace,707 old_pstate->needs_dirtyfb);708}709 710static int dpu_plane_check_inline_rotation(struct dpu_plane *pdpu,711 const struct dpu_sspp_sub_blks *sblk,712 struct drm_rect src, const struct msm_format *fmt)713{714 size_t num_formats;715 const u32 *supported_formats;716 717 if (!sblk->rotation_cfg) {718 DPU_ERROR("invalid rotation cfg\n");719 return -EINVAL;720 }721 722 if (drm_rect_width(&src) > sblk->rotation_cfg->rot_maxheight) {723 DPU_DEBUG_PLANE(pdpu, "invalid height for inline rot:%d max:%d\n",724 src.y2, sblk->rotation_cfg->rot_maxheight);725 return -EINVAL;726 }727 728 supported_formats = sblk->rotation_cfg->rot_format_list;729 num_formats = sblk->rotation_cfg->rot_num_formats;730 731 if (!MSM_FORMAT_IS_UBWC(fmt) ||732 !dpu_find_format(fmt->pixel_format, supported_formats, num_formats))733 return -EINVAL;734 735 return 0;736}737 738static int dpu_plane_atomic_check_pipe(struct dpu_plane *pdpu,739 struct dpu_sw_pipe *pipe,740 struct dpu_sw_pipe_cfg *pipe_cfg,741 const struct msm_format *fmt,742 const struct drm_display_mode *mode)743{744 uint32_t min_src_size;745 struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);746 747 min_src_size = MSM_FORMAT_IS_YUV(fmt) ? 2 : 1;748 749 if (MSM_FORMAT_IS_YUV(fmt) &&750 !pipe->sspp->cap->sblk->csc_blk.len) {751 DPU_DEBUG_PLANE(pdpu,752 "plane doesn't have csc for yuv\n");753 return -EINVAL;754 }755 756 /* check src bounds */757 if (drm_rect_width(&pipe_cfg->src_rect) < min_src_size ||758 drm_rect_height(&pipe_cfg->src_rect) < min_src_size) {759 DPU_DEBUG_PLANE(pdpu, "invalid source " DRM_RECT_FMT "\n",760 DRM_RECT_ARG(&pipe_cfg->src_rect));761 return -E2BIG;762 }763 764 /* valid yuv image */765 if (MSM_FORMAT_IS_YUV(fmt) &&766 (pipe_cfg->src_rect.x1 & 0x1 ||767 pipe_cfg->src_rect.y1 & 0x1 ||768 drm_rect_width(&pipe_cfg->src_rect) & 0x1 ||769 drm_rect_height(&pipe_cfg->src_rect) & 0x1)) {770 DPU_DEBUG_PLANE(pdpu, "invalid yuv source " DRM_RECT_FMT "\n",771 DRM_RECT_ARG(&pipe_cfg->src_rect));772 return -EINVAL;773 }774 775 /* min dst support */776 if (drm_rect_width(&pipe_cfg->dst_rect) < 0x1 ||777 drm_rect_height(&pipe_cfg->dst_rect) < 0x1) {778 DPU_DEBUG_PLANE(pdpu, "invalid dest rect " DRM_RECT_FMT "\n",779 DRM_RECT_ARG(&pipe_cfg->dst_rect));780 return -EINVAL;781 }782 783 /* max clk check */784 if (_dpu_plane_calc_clk(mode, pipe_cfg) > kms->perf.max_core_clk_rate) {785 DPU_DEBUG_PLANE(pdpu, "plane exceeds max mdp core clk limits\n");786 return -E2BIG;787 }788 789 return 0;790}791 792static int dpu_plane_atomic_check(struct drm_plane *plane,793 struct drm_atomic_state *state)794{795 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,796 plane);797 int ret = 0, min_scale;798 struct dpu_plane *pdpu = to_dpu_plane(plane);799 struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);800 u64 max_mdp_clk_rate = kms->perf.max_core_clk_rate;801 struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);802 struct dpu_sw_pipe *pipe = &pstate->pipe;803 struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;804 const struct drm_crtc_state *crtc_state = NULL;805 const struct msm_format *fmt;806 struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;807 struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;808 struct drm_rect fb_rect = { 0 };809 uint32_t max_linewidth;810 unsigned int rotation;811 uint32_t supported_rotations;812 const struct dpu_sspp_cfg *pipe_hw_caps = pstate->pipe.sspp->cap;813 const struct dpu_sspp_sub_blks *sblk = pstate->pipe.sspp->cap->sblk;814 815 if (new_plane_state->crtc)816 crtc_state = drm_atomic_get_new_crtc_state(state,817 new_plane_state->crtc);818 819 min_scale = FRAC_16_16(1, sblk->maxupscale);820 ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,821 min_scale,822 sblk->maxdwnscale << 16,823 true, true);824 if (ret) {825 DPU_DEBUG_PLANE(pdpu, "Check plane state failed (%d)\n", ret);826 return ret;827 }828 if (!new_plane_state->visible)829 return 0;830 831 pipe->multirect_index = DPU_SSPP_RECT_SOLO;832 pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;833 r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;834 r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;835 r_pipe->sspp = NULL;836 837 pstate->stage = DPU_STAGE_0 + pstate->base.normalized_zpos;838 if (pstate->stage >= pdpu->catalog->caps->max_mixer_blendstages) {839 DPU_ERROR("> %d plane stages assigned\n",840 pdpu->catalog->caps->max_mixer_blendstages - DPU_STAGE_0);841 return -EINVAL;842 }843 844 pipe_cfg->src_rect = new_plane_state->src;845 846 /* state->src is 16.16, src_rect is not */847 pipe_cfg->src_rect.x1 >>= 16;848 pipe_cfg->src_rect.x2 >>= 16;849 pipe_cfg->src_rect.y1 >>= 16;850 pipe_cfg->src_rect.y2 >>= 16;851 852 pipe_cfg->dst_rect = new_plane_state->dst;853 854 fb_rect.x2 = new_plane_state->fb->width;855 fb_rect.y2 = new_plane_state->fb->height;856 857 /* Ensure fb size is supported */858 if (drm_rect_width(&fb_rect) > MAX_IMG_WIDTH ||859 drm_rect_height(&fb_rect) > MAX_IMG_HEIGHT) {860 DPU_DEBUG_PLANE(pdpu, "invalid framebuffer " DRM_RECT_FMT "\n",861 DRM_RECT_ARG(&fb_rect));862 return -E2BIG;863 }864 865 fmt = msm_framebuffer_format(new_plane_state->fb);866 867 max_linewidth = pdpu->catalog->caps->max_linewidth;868 869 drm_rect_rotate(&pipe_cfg->src_rect,870 new_plane_state->fb->width, new_plane_state->fb->height,871 new_plane_state->rotation);872 873 if ((drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) ||874 _dpu_plane_calc_clk(&crtc_state->adjusted_mode, pipe_cfg) > max_mdp_clk_rate) {875 /*876 * In parallel multirect case only the half of the usual width877 * is supported for tiled formats. If we are here, we know that878 * full width is more than max_linewidth, thus each rect is879 * wider than allowed.880 */881 if (MSM_FORMAT_IS_UBWC(fmt) &&882 drm_rect_width(&pipe_cfg->src_rect) > max_linewidth) {883 DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u, tiled format\n",884 DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);885 return -E2BIG;886 }887 888 if (drm_rect_width(&pipe_cfg->src_rect) > 2 * max_linewidth) {889 DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",890 DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);891 return -E2BIG;892 }893 894 if (drm_rect_width(&pipe_cfg->src_rect) != drm_rect_width(&pipe_cfg->dst_rect) ||895 drm_rect_height(&pipe_cfg->src_rect) != drm_rect_height(&pipe_cfg->dst_rect) ||896 (!test_bit(DPU_SSPP_SMART_DMA_V1, &pipe->sspp->cap->features) &&897 !test_bit(DPU_SSPP_SMART_DMA_V2, &pipe->sspp->cap->features)) ||898 MSM_FORMAT_IS_YUV(fmt)) {899 DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u, can't use split source\n",900 DRM_RECT_ARG(&pipe_cfg->src_rect), max_linewidth);901 return -E2BIG;902 }903 904 /*905 * Use multirect for wide plane. We do not support dynamic906 * assignment of SSPPs, so we know the configuration.907 */908 pipe->multirect_index = DPU_SSPP_RECT_0;909 pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;910 911 r_pipe->sspp = pipe->sspp;912 r_pipe->multirect_index = DPU_SSPP_RECT_1;913 r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;914 915 *r_pipe_cfg = *pipe_cfg;916 pipe_cfg->src_rect.x2 = (pipe_cfg->src_rect.x1 + pipe_cfg->src_rect.x2) >> 1;917 pipe_cfg->dst_rect.x2 = (pipe_cfg->dst_rect.x1 + pipe_cfg->dst_rect.x2) >> 1;918 r_pipe_cfg->src_rect.x1 = pipe_cfg->src_rect.x2;919 r_pipe_cfg->dst_rect.x1 = pipe_cfg->dst_rect.x2;920 }921 922 drm_rect_rotate_inv(&pipe_cfg->src_rect,923 new_plane_state->fb->width, new_plane_state->fb->height,924 new_plane_state->rotation);925 if (r_pipe->sspp)926 drm_rect_rotate_inv(&r_pipe_cfg->src_rect,927 new_plane_state->fb->width, new_plane_state->fb->height,928 new_plane_state->rotation);929 930 ret = dpu_plane_atomic_check_pipe(pdpu, pipe, pipe_cfg, fmt, &crtc_state->adjusted_mode);931 if (ret)932 return ret;933 934 if (r_pipe->sspp) {935 ret = dpu_plane_atomic_check_pipe(pdpu, r_pipe, r_pipe_cfg, fmt,936 &crtc_state->adjusted_mode);937 if (ret)938 return ret;939 }940 941 supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0;942 943 if (pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION))944 supported_rotations |= DRM_MODE_ROTATE_90;945 946 rotation = drm_rotation_simplify(new_plane_state->rotation,947 supported_rotations);948 949 if ((pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION)) &&950 (rotation & DRM_MODE_ROTATE_90)) {951 ret = dpu_plane_check_inline_rotation(pdpu, sblk, pipe_cfg->src_rect, fmt);952 if (ret)953 return ret;954 }955 956 pstate->rotation = rotation;957 pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state);958 959 return 0;960}961 962static void dpu_plane_flush_csc(struct dpu_plane *pdpu, struct dpu_sw_pipe *pipe)963{964 const struct msm_format *format =965 msm_framebuffer_format(pdpu->base.state->fb);966 const struct dpu_csc_cfg *csc_ptr;967 968 if (!pipe->sspp || !pipe->sspp->ops.setup_csc)969 return;970 971 csc_ptr = _dpu_plane_get_csc(pipe, format);972 if (!csc_ptr)973 return;974 975 DPU_DEBUG_PLANE(pdpu, "using 0x%X 0x%X 0x%X...\n",976 csc_ptr->csc_mv[0],977 csc_ptr->csc_mv[1],978 csc_ptr->csc_mv[2]);979 980 pipe->sspp->ops.setup_csc(pipe->sspp, csc_ptr);981 982}983 984void dpu_plane_flush(struct drm_plane *plane)985{986 struct dpu_plane *pdpu;987 struct dpu_plane_state *pstate;988 989 if (!plane || !plane->state) {990 DPU_ERROR("invalid plane\n");991 return;992 }993 994 pdpu = to_dpu_plane(plane);995 pstate = to_dpu_plane_state(plane->state);996 997 /*998 * These updates have to be done immediately before the plane flush999 * timing, and may not be moved to the atomic_update/mode_set functions.1000 */1001 if (pdpu->is_error)1002 /* force white frame with 100% alpha pipe output on error */1003 _dpu_plane_color_fill(pdpu, 0xFFFFFF, 0xFF);1004 else if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG)1005 /* force 100% alpha */1006 _dpu_plane_color_fill(pdpu, pdpu->color_fill, 0xFF);1007 else {1008 dpu_plane_flush_csc(pdpu, &pstate->pipe);1009 dpu_plane_flush_csc(pdpu, &pstate->r_pipe);1010 }1011 1012 /* flag h/w flush complete */1013 if (plane->state)1014 pstate->pending = false;1015}1016 1017/**1018 * dpu_plane_set_error: enable/disable error condition1019 * @plane: pointer to drm_plane structure1020 * @error: error value to set1021 */1022void dpu_plane_set_error(struct drm_plane *plane, bool error)1023{1024 struct dpu_plane *pdpu;1025 1026 if (!plane)1027 return;1028 1029 pdpu = to_dpu_plane(plane);1030 pdpu->is_error = error;1031}1032 1033static void dpu_plane_sspp_update_pipe(struct drm_plane *plane,1034 struct dpu_sw_pipe *pipe,1035 struct dpu_sw_pipe_cfg *pipe_cfg,1036 const struct msm_format *fmt,1037 int frame_rate,1038 struct dpu_hw_fmt_layout *layout)1039{1040 uint32_t src_flags;1041 struct dpu_plane *pdpu = to_dpu_plane(plane);1042 struct drm_plane_state *state = plane->state;1043 struct dpu_plane_state *pstate = to_dpu_plane_state(state);1044 1045 if (layout && pipe->sspp->ops.setup_sourceaddress) {1046 trace_dpu_plane_set_scanout(pipe, layout);1047 pipe->sspp->ops.setup_sourceaddress(pipe, layout);1048 }1049 1050 /* override for color fill */1051 if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG) {1052 _dpu_plane_set_qos_ctrl(plane, pipe, false);1053 1054 /* skip remaining processing on color fill */1055 return;1056 }1057 1058 if (pipe->sspp->ops.setup_rects) {1059 pipe->sspp->ops.setup_rects(pipe,1060 pipe_cfg);1061 }1062 1063 _dpu_plane_setup_scaler(pipe, fmt, false, pipe_cfg, pstate->rotation);1064 1065 if (pipe->sspp->ops.setup_multirect)1066 pipe->sspp->ops.setup_multirect(1067 pipe);1068 1069 if (pipe->sspp->ops.setup_format) {1070 unsigned int rotation = pstate->rotation;1071 1072 src_flags = 0x0;1073 1074 if (rotation & DRM_MODE_REFLECT_X)1075 src_flags |= DPU_SSPP_FLIP_LR;1076 1077 if (rotation & DRM_MODE_REFLECT_Y)1078 src_flags |= DPU_SSPP_FLIP_UD;1079 1080 if (rotation & DRM_MODE_ROTATE_90)1081 src_flags |= DPU_SSPP_ROT_90;1082 1083 /* update format */1084 pipe->sspp->ops.setup_format(pipe, fmt, src_flags);1085 1086 if (pipe->sspp->ops.setup_cdp) {1087 const struct dpu_perf_cfg *perf = pdpu->catalog->perf;1088 1089 pipe->sspp->ops.setup_cdp(pipe, fmt,1090 perf->cdp_cfg[DPU_PERF_CDP_USAGE_RT].rd_enable);1091 }1092 }1093 1094 _dpu_plane_set_qos_lut(plane, pipe, fmt, pipe_cfg);1095 1096 if (pipe->sspp->idx != SSPP_CURSOR0 &&1097 pipe->sspp->idx != SSPP_CURSOR1)1098 _dpu_plane_set_ot_limit(plane, pipe, pipe_cfg, frame_rate);1099 1100 if (pstate->needs_qos_remap)1101 _dpu_plane_set_qos_remap(plane, pipe);1102}1103 1104static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)1105{1106 struct dpu_plane *pdpu = to_dpu_plane(plane);1107 struct drm_plane_state *state = plane->state;1108 struct dpu_plane_state *pstate = to_dpu_plane_state(state);1109 struct dpu_sw_pipe *pipe = &pstate->pipe;1110 struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;1111 struct drm_crtc *crtc = state->crtc;1112 struct drm_framebuffer *fb = state->fb;1113 bool is_rt_pipe;1114 const struct msm_format *fmt =1115 msm_framebuffer_format(fb);1116 struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;1117 struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;1118 struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);1119 struct msm_gem_address_space *aspace = kms->base.aspace;1120 struct dpu_hw_fmt_layout layout;1121 bool layout_valid = false;1122 int ret;1123 1124 ret = dpu_format_populate_layout(aspace, fb, &layout);1125 if (ret)1126 DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);1127 else1128 layout_valid = true;1129 1130 pstate->pending = true;1131 1132 is_rt_pipe = (dpu_crtc_get_client_type(crtc) != NRT_CLIENT);1133 pstate->needs_qos_remap |= (is_rt_pipe != pdpu->is_rt_pipe);1134 pdpu->is_rt_pipe = is_rt_pipe;1135 1136 DPU_DEBUG_PLANE(pdpu, "FB[%u] " DRM_RECT_FP_FMT "->crtc%u " DRM_RECT_FMT1137 ", %p4cc ubwc %d\n", fb->base.id, DRM_RECT_FP_ARG(&state->src),1138 crtc->base.id, DRM_RECT_ARG(&state->dst),1139 &fmt->pixel_format, MSM_FORMAT_IS_UBWC(fmt));1140 1141 dpu_plane_sspp_update_pipe(plane, pipe, pipe_cfg, fmt,1142 drm_mode_vrefresh(&crtc->mode),1143 layout_valid ? &layout : NULL);1144 1145 if (r_pipe->sspp) {1146 dpu_plane_sspp_update_pipe(plane, r_pipe, r_pipe_cfg, fmt,1147 drm_mode_vrefresh(&crtc->mode),1148 layout_valid ? &layout : NULL);1149 }1150 1151 if (pstate->needs_qos_remap)1152 pstate->needs_qos_remap = false;1153 1154 pstate->plane_fetch_bw = _dpu_plane_calc_bw(pdpu->catalog, fmt,1155 &crtc->mode, pipe_cfg);1156 1157 pstate->plane_clk = _dpu_plane_calc_clk(&crtc->mode, pipe_cfg);1158 1159 if (r_pipe->sspp) {1160 pstate->plane_fetch_bw += _dpu_plane_calc_bw(pdpu->catalog, fmt, &crtc->mode, r_pipe_cfg);1161 1162 pstate->plane_clk = max(pstate->plane_clk, _dpu_plane_calc_clk(&crtc->mode, r_pipe_cfg));1163 }1164}1165 1166static void _dpu_plane_atomic_disable(struct drm_plane *plane)1167{1168 struct drm_plane_state *state = plane->state;1169 struct dpu_plane_state *pstate = to_dpu_plane_state(state);1170 struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;1171 1172 trace_dpu_plane_disable(DRMID(plane), false,1173 pstate->pipe.multirect_mode);1174 1175 if (r_pipe->sspp) {1176 r_pipe->multirect_index = DPU_SSPP_RECT_SOLO;1177 r_pipe->multirect_mode = DPU_SSPP_MULTIRECT_NONE;1178 1179 if (r_pipe->sspp->ops.setup_multirect)1180 r_pipe->sspp->ops.setup_multirect(r_pipe);1181 }1182 1183 pstate->pending = true;1184}1185 1186static void dpu_plane_atomic_update(struct drm_plane *plane,1187 struct drm_atomic_state *state)1188{1189 struct dpu_plane *pdpu = to_dpu_plane(plane);1190 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,1191 plane);1192 1193 pdpu->is_error = false;1194 1195 DPU_DEBUG_PLANE(pdpu, "\n");1196 1197 if (!new_state->visible) {1198 _dpu_plane_atomic_disable(plane);1199 } else {1200 dpu_plane_sspp_atomic_update(plane);1201 }1202}1203 1204static void dpu_plane_destroy_state(struct drm_plane *plane,1205 struct drm_plane_state *state)1206{1207 __drm_atomic_helper_plane_destroy_state(state);1208 kfree(to_dpu_plane_state(state));1209}1210 1211static struct drm_plane_state *1212dpu_plane_duplicate_state(struct drm_plane *plane)1213{1214 struct dpu_plane *pdpu;1215 struct dpu_plane_state *pstate;1216 struct dpu_plane_state *old_state;1217 1218 if (!plane) {1219 DPU_ERROR("invalid plane\n");1220 return NULL;1221 } else if (!plane->state) {1222 DPU_ERROR("invalid plane state\n");1223 return NULL;1224 }1225 1226 old_state = to_dpu_plane_state(plane->state);1227 pdpu = to_dpu_plane(plane);1228 pstate = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL);1229 if (!pstate) {1230 DPU_ERROR_PLANE(pdpu, "failed to allocate state\n");1231 return NULL;1232 }1233 1234 DPU_DEBUG_PLANE(pdpu, "\n");1235 1236 pstate->pending = false;1237 1238 __drm_atomic_helper_plane_duplicate_state(plane, &pstate->base);1239 1240 return &pstate->base;1241}1242 1243static const char * const multirect_mode_name[] = {1244 [DPU_SSPP_MULTIRECT_NONE] = "none",1245 [DPU_SSPP_MULTIRECT_PARALLEL] = "parallel",1246 [DPU_SSPP_MULTIRECT_TIME_MX] = "time_mx",1247};1248 1249static const char * const multirect_index_name[] = {1250 [DPU_SSPP_RECT_SOLO] = "solo",1251 [DPU_SSPP_RECT_0] = "rect_0",1252 [DPU_SSPP_RECT_1] = "rect_1",1253};1254 1255static const char *dpu_get_multirect_mode(enum dpu_sspp_multirect_mode mode)1256{1257 if (WARN_ON(mode >= ARRAY_SIZE(multirect_mode_name)))1258 return "unknown";1259 1260 return multirect_mode_name[mode];1261}1262 1263static const char *dpu_get_multirect_index(enum dpu_sspp_multirect_index index)1264{1265 if (WARN_ON(index >= ARRAY_SIZE(multirect_index_name)))1266 return "unknown";1267 1268 return multirect_index_name[index];1269}1270 1271static void dpu_plane_atomic_print_state(struct drm_printer *p,1272 const struct drm_plane_state *state)1273{1274 const struct dpu_plane_state *pstate = to_dpu_plane_state(state);1275 const struct dpu_sw_pipe *pipe = &pstate->pipe;1276 const struct dpu_sw_pipe_cfg *pipe_cfg = &pstate->pipe_cfg;1277 const struct dpu_sw_pipe *r_pipe = &pstate->r_pipe;1278 const struct dpu_sw_pipe_cfg *r_pipe_cfg = &pstate->r_pipe_cfg;1279 1280 drm_printf(p, "\tstage=%d\n", pstate->stage);1281 1282 drm_printf(p, "\tsspp[0]=%s\n", pipe->sspp->cap->name);1283 drm_printf(p, "\tmultirect_mode[0]=%s\n", dpu_get_multirect_mode(pipe->multirect_mode));1284 drm_printf(p, "\tmultirect_index[0]=%s\n",1285 dpu_get_multirect_index(pipe->multirect_index));1286 drm_printf(p, "\tsrc[0]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&pipe_cfg->src_rect));1287 drm_printf(p, "\tdst[0]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&pipe_cfg->dst_rect));1288 1289 if (r_pipe->sspp) {1290 drm_printf(p, "\tsspp[1]=%s\n", r_pipe->sspp->cap->name);1291 drm_printf(p, "\tmultirect_mode[1]=%s\n",1292 dpu_get_multirect_mode(r_pipe->multirect_mode));1293 drm_printf(p, "\tmultirect_index[1]=%s\n",1294 dpu_get_multirect_index(r_pipe->multirect_index));1295 drm_printf(p, "\tsrc[1]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&r_pipe_cfg->src_rect));1296 drm_printf(p, "\tdst[1]=" DRM_RECT_FMT "\n", DRM_RECT_ARG(&r_pipe_cfg->dst_rect));1297 }1298}1299 1300static void dpu_plane_reset(struct drm_plane *plane)1301{1302 struct dpu_plane *pdpu;1303 struct dpu_plane_state *pstate;1304 struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);1305 1306 if (!plane) {1307 DPU_ERROR("invalid plane\n");1308 return;1309 }1310 1311 pdpu = to_dpu_plane(plane);1312 DPU_DEBUG_PLANE(pdpu, "\n");1313 1314 /* remove previous state, if present */1315 if (plane->state) {1316 dpu_plane_destroy_state(plane, plane->state);1317 plane->state = NULL;1318 }1319 1320 pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);1321 if (!pstate) {1322 DPU_ERROR_PLANE(pdpu, "failed to allocate state\n");1323 return;1324 }1325 1326 /*1327 * Set the SSPP here until we have proper virtualized DPU planes.1328 * This is the place where the state is allocated, so fill it fully.1329 */1330 pstate->pipe.sspp = dpu_rm_get_sspp(&dpu_kms->rm, pdpu->pipe);1331 pstate->pipe.multirect_index = DPU_SSPP_RECT_SOLO;1332 pstate->pipe.multirect_mode = DPU_SSPP_MULTIRECT_NONE;1333 1334 pstate->r_pipe.sspp = NULL;1335 1336 __drm_atomic_helper_plane_reset(plane, &pstate->base);1337}1338 1339#ifdef CONFIG_DEBUG_FS1340void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable)1341{1342 struct dpu_plane *pdpu = to_dpu_plane(plane);1343 struct dpu_plane_state *pstate = to_dpu_plane_state(plane->state);1344 struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);1345 1346 if (!pdpu->is_rt_pipe)1347 return;1348 1349 pm_runtime_get_sync(&dpu_kms->pdev->dev);1350 _dpu_plane_set_qos_ctrl(plane, &pstate->pipe, enable);1351 if (pstate->r_pipe.sspp)1352 _dpu_plane_set_qos_ctrl(plane, &pstate->r_pipe, enable);1353 pm_runtime_put_sync(&dpu_kms->pdev->dev);1354}1355#endif1356 1357static bool dpu_plane_format_mod_supported(struct drm_plane *plane,1358 uint32_t format, uint64_t modifier)1359{1360 struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);1361 bool has_no_ubwc = (dpu_kms->mdss->ubwc_enc_version == 0) &&1362 (dpu_kms->mdss->ubwc_dec_version == 0);1363 1364 if (modifier == DRM_FORMAT_MOD_LINEAR)1365 return true;1366 1367 if (modifier == DRM_FORMAT_MOD_QCOM_COMPRESSED && !has_no_ubwc)1368 return dpu_find_format(format, qcom_compressed_supported_formats,1369 ARRAY_SIZE(qcom_compressed_supported_formats));1370 1371 return false;1372}1373 1374static const struct drm_plane_funcs dpu_plane_funcs = {1375 .update_plane = drm_atomic_helper_update_plane,1376 .disable_plane = drm_atomic_helper_disable_plane,1377 .reset = dpu_plane_reset,1378 .atomic_duplicate_state = dpu_plane_duplicate_state,1379 .atomic_destroy_state = dpu_plane_destroy_state,1380 .atomic_print_state = dpu_plane_atomic_print_state,1381 .format_mod_supported = dpu_plane_format_mod_supported,1382};1383 1384static const struct drm_plane_helper_funcs dpu_plane_helper_funcs = {1385 .prepare_fb = dpu_plane_prepare_fb,1386 .cleanup_fb = dpu_plane_cleanup_fb,1387 .atomic_check = dpu_plane_atomic_check,1388 .atomic_update = dpu_plane_atomic_update,1389};1390 1391/* initialize plane */1392struct drm_plane *dpu_plane_init(struct drm_device *dev,1393 uint32_t pipe, enum drm_plane_type type,1394 unsigned long possible_crtcs)1395{1396 struct drm_plane *plane = NULL;1397 const uint32_t *format_list;1398 struct dpu_plane *pdpu;1399 struct msm_drm_private *priv = dev->dev_private;1400 struct dpu_kms *kms = to_dpu_kms(priv->kms);1401 struct dpu_hw_sspp *pipe_hw;1402 uint32_t num_formats;1403 uint32_t supported_rotations;1404 int ret;1405 1406 /* initialize underlying h/w driver */1407 pipe_hw = dpu_rm_get_sspp(&kms->rm, pipe);1408 if (!pipe_hw || !pipe_hw->cap || !pipe_hw->cap->sblk) {1409 DPU_ERROR("[%u]SSPP is invalid\n", pipe);1410 return ERR_PTR(-EINVAL);1411 }1412 1413 format_list = pipe_hw->cap->sblk->format_list;1414 num_formats = pipe_hw->cap->sblk->num_formats;1415 1416 pdpu = drmm_universal_plane_alloc(dev, struct dpu_plane, base,1417 0xff, &dpu_plane_funcs,1418 format_list, num_formats,1419 supported_format_modifiers, type, NULL);1420 if (IS_ERR(pdpu))1421 return ERR_CAST(pdpu);1422 1423 /* cache local stuff for later */1424 plane = &pdpu->base;1425 pdpu->pipe = pipe;1426 1427 pdpu->catalog = kms->catalog;1428 1429 ret = drm_plane_create_zpos_property(plane, 0, 0, DPU_ZPOS_MAX);1430 if (ret)1431 DPU_ERROR("failed to install zpos property, rc = %d\n", ret);1432 1433 drm_plane_create_alpha_property(plane);1434 drm_plane_create_blend_mode_property(plane,1435 BIT(DRM_MODE_BLEND_PIXEL_NONE) |1436 BIT(DRM_MODE_BLEND_PREMULTI) |1437 BIT(DRM_MODE_BLEND_COVERAGE));1438 1439 supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180;1440 1441 if (pipe_hw->cap->features & BIT(DPU_SSPP_INLINE_ROTATION))1442 supported_rotations |= DRM_MODE_ROTATE_MASK;1443 1444 drm_plane_create_rotation_property(plane,1445 DRM_MODE_ROTATE_0, supported_rotations);1446 1447 drm_plane_enable_fb_damage_clips(plane);1448 1449 /* success! finalize initialization */1450 drm_plane_helper_add(plane, &dpu_plane_helper_funcs);1451 1452 DPU_DEBUG("%s created for pipe:%u id:%u\n", plane->name,1453 pipe, plane->base.id);1454 return plane;1455}1456