cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dpu_plane.c (45904B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Copyright (C) 2014-2018 The Linux Foundation. All rights reserved.
      4 * Copyright (C) 2013 Red Hat
      5 * 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_damage_helper.h>
     16#include <drm/drm_gem_atomic_helper.h>
     17
     18#include "msm_drv.h"
     19#include "dpu_kms.h"
     20#include "dpu_formats.h"
     21#include "dpu_hw_sspp.h"
     22#include "dpu_trace.h"
     23#include "dpu_crtc.h"
     24#include "dpu_vbif.h"
     25#include "dpu_plane.h"
     26
     27#define DPU_DEBUG_PLANE(pl, fmt, ...) DRM_DEBUG_ATOMIC("plane%d " fmt,\
     28		(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
     29
     30#define DPU_ERROR_PLANE(pl, fmt, ...) DPU_ERROR("plane%d " fmt,\
     31		(pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
     32
     33#define DECIMATED_DIMENSION(dim, deci) (((dim) + ((1 << (deci)) - 1)) >> (deci))
     34#define PHASE_STEP_SHIFT	21
     35#define PHASE_STEP_UNIT_SCALE   ((int) (1 << PHASE_STEP_SHIFT))
     36#define PHASE_RESIDUAL		15
     37
     38#define SHARP_STRENGTH_DEFAULT	32
     39#define SHARP_EDGE_THR_DEFAULT	112
     40#define SHARP_SMOOTH_THR_DEFAULT	8
     41#define SHARP_NOISE_THR_DEFAULT	2
     42
     43#define DPU_NAME_SIZE  12
     44
     45#define DPU_PLANE_COLOR_FILL_FLAG	BIT(31)
     46#define DPU_ZPOS_MAX 255
     47
     48/* multirect rect index */
     49enum {
     50	R0,
     51	R1,
     52	R_MAX
     53};
     54
     55/*
     56 * Default Preload Values
     57 */
     58#define DPU_QSEED3_DEFAULT_PRELOAD_H 0x4
     59#define DPU_QSEED3_DEFAULT_PRELOAD_V 0x3
     60#define DPU_QSEED4_DEFAULT_PRELOAD_V 0x2
     61#define DPU_QSEED4_DEFAULT_PRELOAD_H 0x4
     62
     63#define DEFAULT_REFRESH_RATE	60
     64
     65static const uint32_t qcom_compressed_supported_formats[] = {
     66	DRM_FORMAT_ABGR8888,
     67	DRM_FORMAT_ARGB8888,
     68	DRM_FORMAT_XBGR8888,
     69	DRM_FORMAT_XRGB8888,
     70	DRM_FORMAT_BGR565,
     71
     72	DRM_FORMAT_NV12,
     73};
     74
     75/**
     76 * enum dpu_plane_qos - Different qos configurations for each pipe
     77 *
     78 * @DPU_PLANE_QOS_VBLANK_CTRL: Setup VBLANK qos for the pipe.
     79 * @DPU_PLANE_QOS_VBLANK_AMORTIZE: Enables Amortization within pipe.
     80 *	this configuration is mutually exclusive from VBLANK_CTRL.
     81 * @DPU_PLANE_QOS_PANIC_CTRL: Setup panic for the pipe.
     82 */
     83enum dpu_plane_qos {
     84	DPU_PLANE_QOS_VBLANK_CTRL = BIT(0),
     85	DPU_PLANE_QOS_VBLANK_AMORTIZE = BIT(1),
     86	DPU_PLANE_QOS_PANIC_CTRL = BIT(2),
     87};
     88
     89/*
     90 * struct dpu_plane - local dpu plane structure
     91 * @aspace: address space pointer
     92 * @mplane_list: List of multirect planes of the same pipe
     93 * @catalog: Points to dpu catalog structure
     94 * @revalidate: force revalidation of all the plane properties
     95 */
     96struct dpu_plane {
     97	struct drm_plane base;
     98
     99	struct mutex lock;
    100
    101	enum dpu_sspp pipe;
    102
    103	struct dpu_hw_pipe *pipe_hw;
    104	uint32_t color_fill;
    105	bool is_error;
    106	bool is_rt_pipe;
    107	bool is_virtual;
    108	struct list_head mplane_list;
    109	struct dpu_mdss_cfg *catalog;
    110};
    111
    112static const uint64_t supported_format_modifiers[] = {
    113	DRM_FORMAT_MOD_QCOM_COMPRESSED,
    114	DRM_FORMAT_MOD_LINEAR,
    115	DRM_FORMAT_MOD_INVALID
    116};
    117
    118#define to_dpu_plane(x) container_of(x, struct dpu_plane, base)
    119
    120static struct dpu_kms *_dpu_plane_get_kms(struct drm_plane *plane)
    121{
    122	struct msm_drm_private *priv = plane->dev->dev_private;
    123
    124	return to_dpu_kms(priv->kms);
    125}
    126
    127/**
    128 * _dpu_plane_calc_bw - calculate bandwidth required for a plane
    129 * @plane: Pointer to drm plane.
    130 * @fb:   Pointer to framebuffer associated with the given plane
    131 * @pipe_cfg: Pointer to pipe configuration
    132 * Result: Updates calculated bandwidth in the plane state.
    133 * BW Equation: src_w * src_h * bpp * fps * (v_total / v_dest)
    134 * Prefill BW Equation: line src bytes * line_time
    135 */
    136static void _dpu_plane_calc_bw(struct drm_plane *plane,
    137	struct drm_framebuffer *fb,
    138	struct dpu_hw_pipe_cfg *pipe_cfg)
    139{
    140	struct dpu_plane_state *pstate;
    141	struct drm_display_mode *mode;
    142	const struct dpu_format *fmt = NULL;
    143	struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
    144	int src_width, src_height, dst_height, fps;
    145	u64 plane_prefill_bw;
    146	u64 plane_bw;
    147	u32 hw_latency_lines;
    148	u64 scale_factor;
    149	int vbp, vpw, vfp;
    150
    151	pstate = to_dpu_plane_state(plane->state);
    152	mode = &plane->state->crtc->mode;
    153
    154	fmt = dpu_get_dpu_format_ext(fb->format->format, fb->modifier);
    155
    156	src_width = drm_rect_width(&pipe_cfg->src_rect);
    157	src_height = drm_rect_height(&pipe_cfg->src_rect);
    158	dst_height = drm_rect_height(&pipe_cfg->dst_rect);
    159	fps = drm_mode_vrefresh(mode);
    160	vbp = mode->vtotal - mode->vsync_end;
    161	vpw = mode->vsync_end - mode->vsync_start;
    162	vfp = mode->vsync_start - mode->vdisplay;
    163	hw_latency_lines =  dpu_kms->catalog->perf.min_prefill_lines;
    164	scale_factor = src_height > dst_height ?
    165		mult_frac(src_height, 1, dst_height) : 1;
    166
    167	plane_bw =
    168		src_width * mode->vtotal * fps * fmt->bpp *
    169		scale_factor;
    170
    171	plane_prefill_bw =
    172		src_width * hw_latency_lines * fps * fmt->bpp *
    173		scale_factor * mode->vtotal;
    174
    175	if ((vbp+vpw) > hw_latency_lines)
    176		do_div(plane_prefill_bw, (vbp+vpw));
    177	else if ((vbp+vpw+vfp) < hw_latency_lines)
    178		do_div(plane_prefill_bw, (vbp+vpw+vfp));
    179	else
    180		do_div(plane_prefill_bw, hw_latency_lines);
    181
    182
    183	pstate->plane_fetch_bw = max(plane_bw, plane_prefill_bw);
    184}
    185
    186/**
    187 * _dpu_plane_calc_clk - calculate clock required for a plane
    188 * @plane: Pointer to drm plane.
    189 * @pipe_cfg: Pointer to pipe configuration
    190 * Result: Updates calculated clock in the plane state.
    191 * Clock equation: dst_w * v_total * fps * (src_h / dst_h)
    192 */
    193static void _dpu_plane_calc_clk(struct drm_plane *plane, struct dpu_hw_pipe_cfg *pipe_cfg)
    194{
    195	struct dpu_plane_state *pstate;
    196	struct drm_display_mode *mode;
    197	int dst_width, src_height, dst_height, fps;
    198
    199	pstate = to_dpu_plane_state(plane->state);
    200	mode = &plane->state->crtc->mode;
    201
    202	src_height = drm_rect_height(&pipe_cfg->src_rect);
    203	dst_width = drm_rect_width(&pipe_cfg->dst_rect);
    204	dst_height = drm_rect_height(&pipe_cfg->dst_rect);
    205	fps = drm_mode_vrefresh(mode);
    206
    207	pstate->plane_clk =
    208		dst_width * mode->vtotal * fps;
    209
    210	if (src_height > dst_height) {
    211		pstate->plane_clk *= src_height;
    212		do_div(pstate->plane_clk, dst_height);
    213	}
    214}
    215
    216/**
    217 * _dpu_plane_calc_fill_level - calculate fill level of the given source format
    218 * @plane:		Pointer to drm plane
    219 * @fmt:		Pointer to source buffer format
    220 * @src_width:		width of source buffer
    221 * Return: fill level corresponding to the source buffer/format or 0 if error
    222 */
    223static int _dpu_plane_calc_fill_level(struct drm_plane *plane,
    224		const struct dpu_format *fmt, u32 src_width)
    225{
    226	struct dpu_plane *pdpu, *tmp;
    227	struct dpu_plane_state *pstate;
    228	u32 fixed_buff_size;
    229	u32 total_fl;
    230
    231	if (!fmt || !plane->state || !src_width || !fmt->bpp) {
    232		DPU_ERROR("invalid arguments\n");
    233		return 0;
    234	}
    235
    236	pdpu = to_dpu_plane(plane);
    237	pstate = to_dpu_plane_state(plane->state);
    238	fixed_buff_size = pdpu->catalog->caps->pixel_ram_size;
    239
    240	list_for_each_entry(tmp, &pdpu->mplane_list, mplane_list) {
    241		u32 tmp_width;
    242
    243		if (!tmp->base.state->visible)
    244			continue;
    245		tmp_width = drm_rect_width(&tmp->base.state->src) >> 16;
    246		DPU_DEBUG("plane%d/%d src_width:%d/%d\n",
    247				pdpu->base.base.id, tmp->base.base.id,
    248				src_width,
    249				tmp_width);
    250		src_width = max_t(u32, src_width,
    251				  tmp_width);
    252	}
    253
    254	if (fmt->fetch_planes == DPU_PLANE_PSEUDO_PLANAR) {
    255		if (fmt->chroma_sample == DPU_CHROMA_420) {
    256			/* NV12 */
    257			total_fl = (fixed_buff_size / 2) /
    258				((src_width + 32) * fmt->bpp);
    259		} else {
    260			/* non NV12 */
    261			total_fl = (fixed_buff_size / 2) * 2 /
    262				((src_width + 32) * fmt->bpp);
    263		}
    264	} else {
    265		if (pstate->multirect_mode == DPU_SSPP_MULTIRECT_PARALLEL) {
    266			total_fl = (fixed_buff_size / 2) * 2 /
    267				((src_width + 32) * fmt->bpp);
    268		} else {
    269			total_fl = (fixed_buff_size) * 2 /
    270				((src_width + 32) * fmt->bpp);
    271		}
    272	}
    273
    274	DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %4.4s w:%u fl:%u\n",
    275			pdpu->pipe - SSPP_VIG0,
    276			(char *)&fmt->base.pixel_format,
    277			src_width, total_fl);
    278
    279	return total_fl;
    280}
    281
    282/**
    283 * _dpu_plane_set_qos_lut - set QoS LUT of the given plane
    284 * @plane:		Pointer to drm plane
    285 * @fb:			Pointer to framebuffer associated with the given plane
    286 * @pipe_cfg:		Pointer to pipe configuration
    287 */
    288static void _dpu_plane_set_qos_lut(struct drm_plane *plane,
    289		struct drm_framebuffer *fb, struct dpu_hw_pipe_cfg *pipe_cfg)
    290{
    291	struct dpu_plane *pdpu = to_dpu_plane(plane);
    292	const struct dpu_format *fmt = NULL;
    293	u64 qos_lut;
    294	u32 total_fl = 0, lut_usage;
    295
    296	if (!pdpu->is_rt_pipe) {
    297		lut_usage = DPU_QOS_LUT_USAGE_NRT;
    298	} else {
    299		fmt = dpu_get_dpu_format_ext(
    300				fb->format->format,
    301				fb->modifier);
    302		total_fl = _dpu_plane_calc_fill_level(plane, fmt,
    303				drm_rect_width(&pipe_cfg->src_rect));
    304
    305		if (fmt && DPU_FORMAT_IS_LINEAR(fmt))
    306			lut_usage = DPU_QOS_LUT_USAGE_LINEAR;
    307		else
    308			lut_usage = DPU_QOS_LUT_USAGE_MACROTILE;
    309	}
    310
    311	qos_lut = _dpu_hw_get_qos_lut(
    312			&pdpu->catalog->perf.qos_lut_tbl[lut_usage], total_fl);
    313
    314	trace_dpu_perf_set_qos_luts(pdpu->pipe - SSPP_VIG0,
    315			(fmt) ? fmt->base.pixel_format : 0,
    316			pdpu->is_rt_pipe, total_fl, qos_lut, lut_usage);
    317
    318	DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %4.4s rt:%d fl:%u lut:0x%llx\n",
    319			pdpu->pipe - SSPP_VIG0,
    320			fmt ? (char *)&fmt->base.pixel_format : NULL,
    321			pdpu->is_rt_pipe, total_fl, qos_lut);
    322
    323	pdpu->pipe_hw->ops.setup_creq_lut(pdpu->pipe_hw, qos_lut);
    324}
    325
    326/**
    327 * _dpu_plane_set_danger_lut - set danger/safe LUT of the given plane
    328 * @plane:		Pointer to drm plane
    329 * @fb:			Pointer to framebuffer associated with the given plane
    330 */
    331static void _dpu_plane_set_danger_lut(struct drm_plane *plane,
    332		struct drm_framebuffer *fb)
    333{
    334	struct dpu_plane *pdpu = to_dpu_plane(plane);
    335	const struct dpu_format *fmt = NULL;
    336	u32 danger_lut, safe_lut;
    337
    338	if (!pdpu->is_rt_pipe) {
    339		danger_lut = pdpu->catalog->perf.danger_lut_tbl
    340				[DPU_QOS_LUT_USAGE_NRT];
    341		safe_lut = pdpu->catalog->perf.safe_lut_tbl
    342				[DPU_QOS_LUT_USAGE_NRT];
    343	} else {
    344		fmt = dpu_get_dpu_format_ext(
    345				fb->format->format,
    346				fb->modifier);
    347
    348		if (fmt && DPU_FORMAT_IS_LINEAR(fmt)) {
    349			danger_lut = pdpu->catalog->perf.danger_lut_tbl
    350					[DPU_QOS_LUT_USAGE_LINEAR];
    351			safe_lut = pdpu->catalog->perf.safe_lut_tbl
    352					[DPU_QOS_LUT_USAGE_LINEAR];
    353		} else {
    354			danger_lut = pdpu->catalog->perf.danger_lut_tbl
    355					[DPU_QOS_LUT_USAGE_MACROTILE];
    356			safe_lut = pdpu->catalog->perf.safe_lut_tbl
    357					[DPU_QOS_LUT_USAGE_MACROTILE];
    358		}
    359	}
    360
    361	trace_dpu_perf_set_danger_luts(pdpu->pipe - SSPP_VIG0,
    362			(fmt) ? fmt->base.pixel_format : 0,
    363			(fmt) ? fmt->fetch_mode : 0,
    364			danger_lut,
    365			safe_lut);
    366
    367	DPU_DEBUG_PLANE(pdpu, "pnum:%d fmt: %4.4s mode:%d luts[0x%x, 0x%x]\n",
    368		pdpu->pipe - SSPP_VIG0,
    369		fmt ? (char *)&fmt->base.pixel_format : NULL,
    370		fmt ? fmt->fetch_mode : -1,
    371		danger_lut,
    372		safe_lut);
    373
    374	pdpu->pipe_hw->ops.setup_danger_safe_lut(pdpu->pipe_hw,
    375			danger_lut, safe_lut);
    376}
    377
    378/**
    379 * _dpu_plane_set_qos_ctrl - set QoS control of the given plane
    380 * @plane:		Pointer to drm plane
    381 * @enable:		true to enable QoS control
    382 * @flags:		QoS control mode (enum dpu_plane_qos)
    383 */
    384static void _dpu_plane_set_qos_ctrl(struct drm_plane *plane,
    385	bool enable, u32 flags)
    386{
    387	struct dpu_plane *pdpu = to_dpu_plane(plane);
    388	struct dpu_hw_pipe_qos_cfg pipe_qos_cfg;
    389
    390	memset(&pipe_qos_cfg, 0, sizeof(pipe_qos_cfg));
    391
    392	if (flags & DPU_PLANE_QOS_VBLANK_CTRL) {
    393		pipe_qos_cfg.creq_vblank = pdpu->pipe_hw->cap->sblk->creq_vblank;
    394		pipe_qos_cfg.danger_vblank =
    395				pdpu->pipe_hw->cap->sblk->danger_vblank;
    396		pipe_qos_cfg.vblank_en = enable;
    397	}
    398
    399	if (flags & DPU_PLANE_QOS_VBLANK_AMORTIZE) {
    400		/* this feature overrules previous VBLANK_CTRL */
    401		pipe_qos_cfg.vblank_en = false;
    402		pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */
    403	}
    404
    405	if (flags & DPU_PLANE_QOS_PANIC_CTRL)
    406		pipe_qos_cfg.danger_safe_en = enable;
    407
    408	if (!pdpu->is_rt_pipe) {
    409		pipe_qos_cfg.vblank_en = false;
    410		pipe_qos_cfg.danger_safe_en = false;
    411	}
    412
    413	DPU_DEBUG_PLANE(pdpu, "pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n",
    414		pdpu->pipe - SSPP_VIG0,
    415		pipe_qos_cfg.danger_safe_en,
    416		pipe_qos_cfg.vblank_en,
    417		pipe_qos_cfg.creq_vblank,
    418		pipe_qos_cfg.danger_vblank,
    419		pdpu->is_rt_pipe);
    420
    421	pdpu->pipe_hw->ops.setup_qos_ctrl(pdpu->pipe_hw,
    422			&pipe_qos_cfg);
    423}
    424
    425/**
    426 * _dpu_plane_set_ot_limit - set OT limit for the given plane
    427 * @plane:		Pointer to drm plane
    428 * @crtc:		Pointer to drm crtc
    429 * @pipe_cfg:		Pointer to pipe configuration
    430 */
    431static void _dpu_plane_set_ot_limit(struct drm_plane *plane,
    432		struct drm_crtc *crtc, struct dpu_hw_pipe_cfg *pipe_cfg)
    433{
    434	struct dpu_plane *pdpu = to_dpu_plane(plane);
    435	struct dpu_vbif_set_ot_params ot_params;
    436	struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
    437
    438	memset(&ot_params, 0, sizeof(ot_params));
    439	ot_params.xin_id = pdpu->pipe_hw->cap->xin_id;
    440	ot_params.num = pdpu->pipe_hw->idx - SSPP_NONE;
    441	ot_params.width = drm_rect_width(&pipe_cfg->src_rect);
    442	ot_params.height = drm_rect_height(&pipe_cfg->src_rect);
    443	ot_params.is_wfd = !pdpu->is_rt_pipe;
    444	ot_params.frame_rate = drm_mode_vrefresh(&crtc->mode);
    445	ot_params.vbif_idx = VBIF_RT;
    446	ot_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl;
    447	ot_params.rd = true;
    448
    449	dpu_vbif_set_ot_limit(dpu_kms, &ot_params);
    450}
    451
    452/**
    453 * _dpu_plane_set_qos_remap - set vbif QoS for the given plane
    454 * @plane:		Pointer to drm plane
    455 */
    456static void _dpu_plane_set_qos_remap(struct drm_plane *plane)
    457{
    458	struct dpu_plane *pdpu = to_dpu_plane(plane);
    459	struct dpu_vbif_set_qos_params qos_params;
    460	struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
    461
    462	memset(&qos_params, 0, sizeof(qos_params));
    463	qos_params.vbif_idx = VBIF_RT;
    464	qos_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl;
    465	qos_params.xin_id = pdpu->pipe_hw->cap->xin_id;
    466	qos_params.num = pdpu->pipe_hw->idx - SSPP_VIG0;
    467	qos_params.is_rt = pdpu->is_rt_pipe;
    468
    469	DPU_DEBUG_PLANE(pdpu, "pipe:%d vbif:%d xin:%d rt:%d, clk_ctrl:%d\n",
    470			qos_params.num,
    471			qos_params.vbif_idx,
    472			qos_params.xin_id, qos_params.is_rt,
    473			qos_params.clk_ctrl);
    474
    475	dpu_vbif_set_qos_remap(dpu_kms, &qos_params);
    476}
    477
    478static void _dpu_plane_set_scanout(struct drm_plane *plane,
    479		struct dpu_plane_state *pstate,
    480		struct dpu_hw_pipe_cfg *pipe_cfg,
    481		struct drm_framebuffer *fb)
    482{
    483	struct dpu_plane *pdpu = to_dpu_plane(plane);
    484	struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);
    485	struct msm_gem_address_space *aspace = kms->base.aspace;
    486	int ret;
    487
    488	ret = dpu_format_populate_layout(aspace, fb, &pipe_cfg->layout);
    489	if (ret == -EAGAIN)
    490		DPU_DEBUG_PLANE(pdpu, "not updating same src addrs\n");
    491	else if (ret)
    492		DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);
    493	else if (pdpu->pipe_hw->ops.setup_sourceaddress) {
    494		trace_dpu_plane_set_scanout(pdpu->pipe_hw->idx,
    495					    &pipe_cfg->layout,
    496					    pstate->multirect_index);
    497		pdpu->pipe_hw->ops.setup_sourceaddress(pdpu->pipe_hw, pipe_cfg,
    498						pstate->multirect_index);
    499	}
    500}
    501
    502static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu,
    503		struct dpu_plane_state *pstate,
    504		uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h,
    505		struct dpu_hw_scaler3_cfg *scale_cfg,
    506		const struct dpu_format *fmt,
    507		uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v)
    508{
    509	uint32_t i;
    510	bool inline_rotation = pstate->rotation & DRM_MODE_ROTATE_90;
    511
    512	/*
    513	 * For inline rotation cases, scaler config is post-rotation,
    514	 * so swap the dimensions here. However, pixel extension will
    515	 * need pre-rotation settings.
    516	 */
    517	if (inline_rotation)
    518		swap(src_w, src_h);
    519
    520	scale_cfg->phase_step_x[DPU_SSPP_COMP_0] =
    521		mult_frac((1 << PHASE_STEP_SHIFT), src_w, dst_w);
    522	scale_cfg->phase_step_y[DPU_SSPP_COMP_0] =
    523		mult_frac((1 << PHASE_STEP_SHIFT), src_h, dst_h);
    524
    525
    526	scale_cfg->phase_step_y[DPU_SSPP_COMP_1_2] =
    527		scale_cfg->phase_step_y[DPU_SSPP_COMP_0] / chroma_subsmpl_v;
    528	scale_cfg->phase_step_x[DPU_SSPP_COMP_1_2] =
    529		scale_cfg->phase_step_x[DPU_SSPP_COMP_0] / chroma_subsmpl_h;
    530
    531	scale_cfg->phase_step_x[DPU_SSPP_COMP_2] =
    532		scale_cfg->phase_step_x[DPU_SSPP_COMP_1_2];
    533	scale_cfg->phase_step_y[DPU_SSPP_COMP_2] =
    534		scale_cfg->phase_step_y[DPU_SSPP_COMP_1_2];
    535
    536	scale_cfg->phase_step_x[DPU_SSPP_COMP_3] =
    537		scale_cfg->phase_step_x[DPU_SSPP_COMP_0];
    538	scale_cfg->phase_step_y[DPU_SSPP_COMP_3] =
    539		scale_cfg->phase_step_y[DPU_SSPP_COMP_0];
    540
    541	for (i = 0; i < DPU_MAX_PLANES; i++) {
    542		scale_cfg->src_width[i] = src_w;
    543		scale_cfg->src_height[i] = src_h;
    544		if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) {
    545			scale_cfg->src_width[i] /= chroma_subsmpl_h;
    546			scale_cfg->src_height[i] /= chroma_subsmpl_v;
    547		}
    548
    549		if (pdpu->pipe_hw->cap->features &
    550			BIT(DPU_SSPP_SCALER_QSEED4)) {
    551			scale_cfg->preload_x[i] = DPU_QSEED4_DEFAULT_PRELOAD_H;
    552			scale_cfg->preload_y[i] = DPU_QSEED4_DEFAULT_PRELOAD_V;
    553		} else {
    554			scale_cfg->preload_x[i] = DPU_QSEED3_DEFAULT_PRELOAD_H;
    555			scale_cfg->preload_y[i] = DPU_QSEED3_DEFAULT_PRELOAD_V;
    556		}
    557	}
    558	if (!(DPU_FORMAT_IS_YUV(fmt)) && (src_h == dst_h)
    559		&& (src_w == dst_w))
    560		return;
    561
    562	scale_cfg->dst_width = dst_w;
    563	scale_cfg->dst_height = dst_h;
    564	scale_cfg->y_rgb_filter_cfg = DPU_SCALE_BIL;
    565	scale_cfg->uv_filter_cfg = DPU_SCALE_BIL;
    566	scale_cfg->alpha_filter_cfg = DPU_SCALE_ALPHA_BIL;
    567	scale_cfg->lut_flag = 0;
    568	scale_cfg->blend_cfg = 1;
    569	scale_cfg->enable = 1;
    570}
    571
    572static void _dpu_plane_setup_pixel_ext(struct dpu_hw_scaler3_cfg *scale_cfg,
    573				struct dpu_hw_pixel_ext *pixel_ext,
    574				uint32_t src_w, uint32_t src_h,
    575				uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v)
    576{
    577	int i;
    578
    579	for (i = 0; i < DPU_MAX_PLANES; i++) {
    580		if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) {
    581			src_w /= chroma_subsmpl_h;
    582			src_h /= chroma_subsmpl_v;
    583		}
    584
    585		pixel_ext->num_ext_pxls_top[i] = src_h;
    586		pixel_ext->num_ext_pxls_left[i] = src_w;
    587	}
    588}
    589
    590static const struct dpu_csc_cfg dpu_csc_YUV2RGB_601L = {
    591	{
    592		/* S15.16 format */
    593		0x00012A00, 0x00000000, 0x00019880,
    594		0x00012A00, 0xFFFF9B80, 0xFFFF3000,
    595		0x00012A00, 0x00020480, 0x00000000,
    596	},
    597	/* signed bias */
    598	{ 0xfff0, 0xff80, 0xff80,},
    599	{ 0x0, 0x0, 0x0,},
    600	/* unsigned clamp */
    601	{ 0x10, 0xeb, 0x10, 0xf0, 0x10, 0xf0,},
    602	{ 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,},
    603};
    604
    605static const struct dpu_csc_cfg dpu_csc10_YUV2RGB_601L = {
    606	{
    607		/* S15.16 format */
    608		0x00012A00, 0x00000000, 0x00019880,
    609		0x00012A00, 0xFFFF9B80, 0xFFFF3000,
    610		0x00012A00, 0x00020480, 0x00000000,
    611		},
    612	/* signed bias */
    613	{ 0xffc0, 0xfe00, 0xfe00,},
    614	{ 0x0, 0x0, 0x0,},
    615	/* unsigned clamp */
    616	{ 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,},
    617	{ 0x00, 0x3ff, 0x00, 0x3ff, 0x00, 0x3ff,},
    618};
    619
    620static const struct dpu_csc_cfg *_dpu_plane_get_csc(struct dpu_plane *pdpu, const struct dpu_format *fmt)
    621{
    622	const struct dpu_csc_cfg *csc_ptr;
    623
    624	if (!pdpu) {
    625		DPU_ERROR("invalid plane\n");
    626		return NULL;
    627	}
    628
    629	if (!DPU_FORMAT_IS_YUV(fmt))
    630		return NULL;
    631
    632	if (BIT(DPU_SSPP_CSC_10BIT) & pdpu->pipe_hw->cap->features)
    633		csc_ptr = &dpu_csc10_YUV2RGB_601L;
    634	else
    635		csc_ptr = &dpu_csc_YUV2RGB_601L;
    636
    637	DPU_DEBUG_PLANE(pdpu, "using 0x%X 0x%X 0x%X...\n",
    638			csc_ptr->csc_mv[0],
    639			csc_ptr->csc_mv[1],
    640			csc_ptr->csc_mv[2]);
    641
    642	return csc_ptr;
    643}
    644
    645static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu,
    646		struct dpu_plane_state *pstate,
    647		const struct dpu_format *fmt, bool color_fill,
    648		struct dpu_hw_pipe_cfg *pipe_cfg)
    649{
    650	const struct drm_format_info *info = drm_format_info(fmt->base.pixel_format);
    651	struct dpu_hw_scaler3_cfg scaler3_cfg;
    652	struct dpu_hw_pixel_ext pixel_ext;
    653	u32 src_width = drm_rect_width(&pipe_cfg->src_rect);
    654	u32 src_height = drm_rect_height(&pipe_cfg->src_rect);
    655	u32 dst_width = drm_rect_width(&pipe_cfg->dst_rect);
    656	u32 dst_height = drm_rect_height(&pipe_cfg->dst_rect);
    657
    658	memset(&scaler3_cfg, 0, sizeof(scaler3_cfg));
    659	memset(&pixel_ext, 0, sizeof(pixel_ext));
    660
    661	/* don't chroma subsample if decimating */
    662	/* update scaler. calculate default config for QSEED3 */
    663	_dpu_plane_setup_scaler3(pdpu, pstate,
    664			src_width,
    665			src_height,
    666			dst_width,
    667			dst_height,
    668			&scaler3_cfg, fmt,
    669			info->hsub, info->vsub);
    670
    671	/* configure pixel extension based on scalar config */
    672	_dpu_plane_setup_pixel_ext(&scaler3_cfg, &pixel_ext,
    673			src_width, src_height, info->hsub, info->vsub);
    674
    675	if (pdpu->pipe_hw->ops.setup_pe)
    676		pdpu->pipe_hw->ops.setup_pe(pdpu->pipe_hw,
    677				&pixel_ext);
    678
    679	/**
    680	 * when programmed in multirect mode, scalar block will be
    681	 * bypassed. Still we need to update alpha and bitwidth
    682	 * ONLY for RECT0
    683	 */
    684	if (pdpu->pipe_hw->ops.setup_scaler &&
    685			pstate->multirect_index != DPU_SSPP_RECT_1)
    686		pdpu->pipe_hw->ops.setup_scaler(pdpu->pipe_hw,
    687				pipe_cfg,
    688				&scaler3_cfg);
    689}
    690
    691/**
    692 * _dpu_plane_color_fill - enables color fill on plane
    693 * @pdpu:   Pointer to DPU plane object
    694 * @color:  RGB fill color value, [23..16] Blue, [15..8] Green, [7..0] Red
    695 * @alpha:  8-bit fill alpha value, 255 selects 100% alpha
    696 * Returns: 0 on success
    697 */
    698static int _dpu_plane_color_fill(struct dpu_plane *pdpu,
    699		uint32_t color, uint32_t alpha)
    700{
    701	const struct dpu_format *fmt;
    702	const struct drm_plane *plane = &pdpu->base;
    703	struct dpu_plane_state *pstate = to_dpu_plane_state(plane->state);
    704	struct dpu_hw_pipe_cfg pipe_cfg;
    705
    706	DPU_DEBUG_PLANE(pdpu, "\n");
    707
    708	/*
    709	 * select fill format to match user property expectation,
    710	 * h/w only supports RGB variants
    711	 */
    712	fmt = dpu_get_dpu_format(DRM_FORMAT_ABGR8888);
    713
    714	/* update sspp */
    715	if (fmt && pdpu->pipe_hw->ops.setup_solidfill) {
    716		pdpu->pipe_hw->ops.setup_solidfill(pdpu->pipe_hw,
    717				(color & 0xFFFFFF) | ((alpha & 0xFF) << 24),
    718				pstate->multirect_index);
    719
    720		/* override scaler/decimation if solid fill */
    721		pipe_cfg.dst_rect = pstate->base.dst;
    722
    723		pipe_cfg.src_rect.x1 = 0;
    724		pipe_cfg.src_rect.y1 = 0;
    725		pipe_cfg.src_rect.x2 =
    726			drm_rect_width(&pipe_cfg.dst_rect);
    727		pipe_cfg.src_rect.y2 =
    728			drm_rect_height(&pipe_cfg.dst_rect);
    729
    730		if (pdpu->pipe_hw->ops.setup_format)
    731			pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw,
    732					fmt, DPU_SSPP_SOLID_FILL,
    733					pstate->multirect_index);
    734
    735		if (pdpu->pipe_hw->ops.setup_rects)
    736			pdpu->pipe_hw->ops.setup_rects(pdpu->pipe_hw,
    737					&pipe_cfg,
    738					pstate->multirect_index);
    739
    740		_dpu_plane_setup_scaler(pdpu, pstate, fmt, true, &pipe_cfg);
    741	}
    742
    743	return 0;
    744}
    745
    746void dpu_plane_clear_multirect(const struct drm_plane_state *drm_state)
    747{
    748	struct dpu_plane_state *pstate = to_dpu_plane_state(drm_state);
    749
    750	pstate->multirect_index = DPU_SSPP_RECT_SOLO;
    751	pstate->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
    752}
    753
    754int dpu_plane_validate_multirect_v2(struct dpu_multirect_plane_states *plane)
    755{
    756	struct dpu_plane_state *pstate[R_MAX];
    757	const struct drm_plane_state *drm_state[R_MAX];
    758	struct drm_rect src[R_MAX], dst[R_MAX];
    759	struct dpu_plane *dpu_plane[R_MAX];
    760	const struct dpu_format *fmt[R_MAX];
    761	int i, buffer_lines;
    762	unsigned int max_tile_height = 1;
    763	bool parallel_fetch_qualified = true;
    764	bool has_tiled_rect = false;
    765
    766	for (i = 0; i < R_MAX; i++) {
    767		const struct msm_format *msm_fmt;
    768
    769		drm_state[i] = i ? plane->r1 : plane->r0;
    770		msm_fmt = msm_framebuffer_format(drm_state[i]->fb);
    771		fmt[i] = to_dpu_format(msm_fmt);
    772
    773		if (DPU_FORMAT_IS_UBWC(fmt[i])) {
    774			has_tiled_rect = true;
    775			if (fmt[i]->tile_height > max_tile_height)
    776				max_tile_height = fmt[i]->tile_height;
    777		}
    778	}
    779
    780	for (i = 0; i < R_MAX; i++) {
    781		int width_threshold;
    782
    783		pstate[i] = to_dpu_plane_state(drm_state[i]);
    784		dpu_plane[i] = to_dpu_plane(drm_state[i]->plane);
    785
    786		if (pstate[i] == NULL) {
    787			DPU_ERROR("DPU plane state of plane id %d is NULL\n",
    788				drm_state[i]->plane->base.id);
    789			return -EINVAL;
    790		}
    791
    792		src[i].x1 = drm_state[i]->src_x >> 16;
    793		src[i].y1 = drm_state[i]->src_y >> 16;
    794		src[i].x2 = src[i].x1 + (drm_state[i]->src_w >> 16);
    795		src[i].y2 = src[i].y1 + (drm_state[i]->src_h >> 16);
    796
    797		dst[i] = drm_plane_state_dest(drm_state[i]);
    798
    799		if (drm_rect_calc_hscale(&src[i], &dst[i], 1, 1) != 1 ||
    800		    drm_rect_calc_vscale(&src[i], &dst[i], 1, 1) != 1) {
    801			DPU_ERROR_PLANE(dpu_plane[i],
    802				"scaling is not supported in multirect mode\n");
    803			return -EINVAL;
    804		}
    805
    806		if (DPU_FORMAT_IS_YUV(fmt[i])) {
    807			DPU_ERROR_PLANE(dpu_plane[i],
    808				"Unsupported format for multirect mode\n");
    809			return -EINVAL;
    810		}
    811
    812		/**
    813		 * SSPP PD_MEM is split half - one for each RECT.
    814		 * Tiled formats need 5 lines of buffering while fetching
    815		 * whereas linear formats need only 2 lines.
    816		 * So we cannot support more than half of the supported SSPP
    817		 * width for tiled formats.
    818		 */
    819		width_threshold = dpu_plane[i]->catalog->caps->max_linewidth;
    820		if (has_tiled_rect)
    821			width_threshold /= 2;
    822
    823		if (parallel_fetch_qualified &&
    824		    drm_rect_width(&src[i]) > width_threshold)
    825			parallel_fetch_qualified = false;
    826
    827	}
    828
    829	/* Validate RECT's and set the mode */
    830
    831	/* Prefer PARALLEL FETCH Mode over TIME_MX Mode */
    832	if (parallel_fetch_qualified) {
    833		pstate[R0]->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;
    834		pstate[R1]->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;
    835
    836		goto done;
    837	}
    838
    839	/* TIME_MX Mode */
    840	buffer_lines = 2 * max_tile_height;
    841
    842	if (dst[R1].y1 >= dst[R0].y2 + buffer_lines ||
    843	    dst[R0].y1 >= dst[R1].y2 + buffer_lines) {
    844		pstate[R0]->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX;
    845		pstate[R1]->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX;
    846	} else {
    847		DPU_ERROR(
    848			"No multirect mode possible for the planes (%d - %d)\n",
    849			drm_state[R0]->plane->base.id,
    850			drm_state[R1]->plane->base.id);
    851		return -EINVAL;
    852	}
    853
    854done:
    855	if (dpu_plane[R0]->is_virtual) {
    856		pstate[R0]->multirect_index = DPU_SSPP_RECT_1;
    857		pstate[R1]->multirect_index = DPU_SSPP_RECT_0;
    858	} else {
    859		pstate[R0]->multirect_index = DPU_SSPP_RECT_0;
    860		pstate[R1]->multirect_index = DPU_SSPP_RECT_1;
    861	}
    862
    863	DPU_DEBUG_PLANE(dpu_plane[R0], "R0: %d - %d\n",
    864		pstate[R0]->multirect_mode, pstate[R0]->multirect_index);
    865	DPU_DEBUG_PLANE(dpu_plane[R1], "R1: %d - %d\n",
    866		pstate[R1]->multirect_mode, pstate[R1]->multirect_index);
    867	return 0;
    868}
    869
    870/**
    871 * dpu_plane_get_ctl_flush - get control flush for the given plane
    872 * @plane: Pointer to drm plane structure
    873 * @ctl: Pointer to hardware control driver
    874 * @flush_sspp: Pointer to sspp flush control word
    875 */
    876void dpu_plane_get_ctl_flush(struct drm_plane *plane, struct dpu_hw_ctl *ctl,
    877		u32 *flush_sspp)
    878{
    879	*flush_sspp = ctl->ops.get_bitmask_sspp(ctl, dpu_plane_pipe(plane));
    880}
    881
    882static int dpu_plane_prepare_fb(struct drm_plane *plane,
    883		struct drm_plane_state *new_state)
    884{
    885	struct drm_framebuffer *fb = new_state->fb;
    886	struct dpu_plane *pdpu = to_dpu_plane(plane);
    887	struct dpu_plane_state *pstate = to_dpu_plane_state(new_state);
    888	struct dpu_hw_fmt_layout layout;
    889	struct dpu_kms *kms = _dpu_plane_get_kms(&pdpu->base);
    890	int ret;
    891
    892	if (!new_state->fb)
    893		return 0;
    894
    895	DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", fb->base.id);
    896
    897	/* cache aspace */
    898	pstate->aspace = kms->base.aspace;
    899
    900	/*
    901	 * TODO: Need to sort out the msm_framebuffer_prepare() call below so
    902	 *       we can use msm_atomic_prepare_fb() instead of doing the
    903	 *       implicit fence and fb prepare by hand here.
    904	 */
    905	drm_gem_plane_helper_prepare_fb(plane, new_state);
    906
    907	if (pstate->aspace) {
    908		ret = msm_framebuffer_prepare(new_state->fb,
    909				pstate->aspace, pstate->needs_dirtyfb);
    910		if (ret) {
    911			DPU_ERROR("failed to prepare framebuffer\n");
    912			return ret;
    913		}
    914	}
    915
    916	/* validate framebuffer layout before commit */
    917	ret = dpu_format_populate_layout(pstate->aspace,
    918			new_state->fb, &layout);
    919	if (ret) {
    920		DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);
    921		return ret;
    922	}
    923
    924	return 0;
    925}
    926
    927static void dpu_plane_cleanup_fb(struct drm_plane *plane,
    928		struct drm_plane_state *old_state)
    929{
    930	struct dpu_plane *pdpu = to_dpu_plane(plane);
    931	struct dpu_plane_state *old_pstate;
    932
    933	if (!old_state || !old_state->fb)
    934		return;
    935
    936	old_pstate = to_dpu_plane_state(old_state);
    937
    938	DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", old_state->fb->base.id);
    939
    940	msm_framebuffer_cleanup(old_state->fb, old_pstate->aspace,
    941				old_pstate->needs_dirtyfb);
    942}
    943
    944static bool dpu_plane_validate_src(struct drm_rect *src,
    945				   struct drm_rect *fb_rect,
    946				   uint32_t min_src_size)
    947{
    948	/* Ensure fb size is supported */
    949	if (drm_rect_width(fb_rect) > MAX_IMG_WIDTH ||
    950	    drm_rect_height(fb_rect) > MAX_IMG_HEIGHT)
    951		return false;
    952
    953	/* Ensure src rect is above the minimum size */
    954	if (drm_rect_width(src) < min_src_size ||
    955	    drm_rect_height(src) < min_src_size)
    956		return false;
    957
    958	/* Ensure src is fully encapsulated in fb */
    959	return drm_rect_intersect(fb_rect, src) &&
    960		drm_rect_equals(fb_rect, src);
    961}
    962
    963static int dpu_plane_check_inline_rotation(struct dpu_plane *pdpu,
    964						const struct dpu_sspp_sub_blks *sblk,
    965						struct drm_rect src, const struct dpu_format *fmt)
    966{
    967	size_t num_formats;
    968	const u32 *supported_formats;
    969
    970	if (!sblk->rotation_cfg) {
    971		DPU_ERROR("invalid rotation cfg\n");
    972		return -EINVAL;
    973	}
    974
    975	if (drm_rect_width(&src) > sblk->rotation_cfg->rot_maxheight) {
    976		DPU_DEBUG_PLANE(pdpu, "invalid height for inline rot:%d max:%d\n",
    977				src.y2, sblk->rotation_cfg->rot_maxheight);
    978		return -EINVAL;
    979	}
    980
    981	supported_formats = sblk->rotation_cfg->rot_format_list;
    982	num_formats = sblk->rotation_cfg->rot_num_formats;
    983
    984	if (!DPU_FORMAT_IS_UBWC(fmt) ||
    985		!dpu_find_format(fmt->base.pixel_format, supported_formats, num_formats))
    986		return -EINVAL;
    987
    988	return 0;
    989}
    990
    991static int dpu_plane_atomic_check(struct drm_plane *plane,
    992				  struct drm_atomic_state *state)
    993{
    994	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
    995										 plane);
    996	int ret = 0, min_scale;
    997	struct dpu_plane *pdpu = to_dpu_plane(plane);
    998	struct dpu_plane_state *pstate = to_dpu_plane_state(new_plane_state);
    999	const struct drm_crtc_state *crtc_state = NULL;
   1000	const struct dpu_format *fmt;
   1001	struct drm_rect src, dst, fb_rect = { 0 };
   1002	uint32_t min_src_size, max_linewidth;
   1003	unsigned int rotation;
   1004	uint32_t supported_rotations;
   1005	const struct dpu_sspp_cfg *pipe_hw_caps = pdpu->pipe_hw->cap;
   1006	const struct dpu_sspp_sub_blks *sblk = pdpu->pipe_hw->cap->sblk;
   1007
   1008	if (new_plane_state->crtc)
   1009		crtc_state = drm_atomic_get_new_crtc_state(state,
   1010							   new_plane_state->crtc);
   1011
   1012	min_scale = FRAC_16_16(1, sblk->maxupscale);
   1013	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
   1014						  min_scale,
   1015						  sblk->maxdwnscale << 16,
   1016						  true, true);
   1017	if (ret) {
   1018		DPU_DEBUG_PLANE(pdpu, "Check plane state failed (%d)\n", ret);
   1019		return ret;
   1020	}
   1021	if (!new_plane_state->visible)
   1022		return 0;
   1023
   1024	src.x1 = new_plane_state->src_x >> 16;
   1025	src.y1 = new_plane_state->src_y >> 16;
   1026	src.x2 = src.x1 + (new_plane_state->src_w >> 16);
   1027	src.y2 = src.y1 + (new_plane_state->src_h >> 16);
   1028
   1029	dst = drm_plane_state_dest(new_plane_state);
   1030
   1031	fb_rect.x2 = new_plane_state->fb->width;
   1032	fb_rect.y2 = new_plane_state->fb->height;
   1033
   1034	max_linewidth = pdpu->catalog->caps->max_linewidth;
   1035
   1036	fmt = to_dpu_format(msm_framebuffer_format(new_plane_state->fb));
   1037
   1038	min_src_size = DPU_FORMAT_IS_YUV(fmt) ? 2 : 1;
   1039
   1040	if (DPU_FORMAT_IS_YUV(fmt) &&
   1041		(!(pipe_hw_caps->features & DPU_SSPP_SCALER) ||
   1042		 !(pipe_hw_caps->features & DPU_SSPP_CSC_ANY))) {
   1043		DPU_DEBUG_PLANE(pdpu,
   1044				"plane doesn't have scaler/csc for yuv\n");
   1045		return -EINVAL;
   1046
   1047	/* check src bounds */
   1048	} else if (!dpu_plane_validate_src(&src, &fb_rect, min_src_size)) {
   1049		DPU_DEBUG_PLANE(pdpu, "invalid source " DRM_RECT_FMT "\n",
   1050				DRM_RECT_ARG(&src));
   1051		return -E2BIG;
   1052
   1053	/* valid yuv image */
   1054	} else if (DPU_FORMAT_IS_YUV(fmt) &&
   1055		   (src.x1 & 0x1 || src.y1 & 0x1 ||
   1056		    drm_rect_width(&src) & 0x1 ||
   1057		    drm_rect_height(&src) & 0x1)) {
   1058		DPU_DEBUG_PLANE(pdpu, "invalid yuv source " DRM_RECT_FMT "\n",
   1059				DRM_RECT_ARG(&src));
   1060		return -EINVAL;
   1061
   1062	/* min dst support */
   1063	} else if (drm_rect_width(&dst) < 0x1 || drm_rect_height(&dst) < 0x1) {
   1064		DPU_DEBUG_PLANE(pdpu, "invalid dest rect " DRM_RECT_FMT "\n",
   1065				DRM_RECT_ARG(&dst));
   1066		return -EINVAL;
   1067
   1068	/* check decimated source width */
   1069	} else if (drm_rect_width(&src) > max_linewidth) {
   1070		DPU_DEBUG_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
   1071				DRM_RECT_ARG(&src), max_linewidth);
   1072		return -E2BIG;
   1073	}
   1074
   1075	supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0;
   1076
   1077	if (pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION))
   1078		supported_rotations |= DRM_MODE_ROTATE_90;
   1079
   1080	rotation = drm_rotation_simplify(new_plane_state->rotation,
   1081					supported_rotations);
   1082
   1083	if ((pipe_hw_caps->features & BIT(DPU_SSPP_INLINE_ROTATION)) &&
   1084		(rotation & DRM_MODE_ROTATE_90)) {
   1085		ret = dpu_plane_check_inline_rotation(pdpu, sblk, src, fmt);
   1086		if (ret)
   1087			return ret;
   1088	}
   1089
   1090	pstate->rotation = rotation;
   1091	pstate->needs_qos_remap = drm_atomic_crtc_needs_modeset(crtc_state);
   1092
   1093	return 0;
   1094}
   1095
   1096void dpu_plane_flush(struct drm_plane *plane)
   1097{
   1098	struct dpu_plane *pdpu;
   1099	struct dpu_plane_state *pstate;
   1100
   1101	if (!plane || !plane->state) {
   1102		DPU_ERROR("invalid plane\n");
   1103		return;
   1104	}
   1105
   1106	pdpu = to_dpu_plane(plane);
   1107	pstate = to_dpu_plane_state(plane->state);
   1108
   1109	/*
   1110	 * These updates have to be done immediately before the plane flush
   1111	 * timing, and may not be moved to the atomic_update/mode_set functions.
   1112	 */
   1113	if (pdpu->is_error)
   1114		/* force white frame with 100% alpha pipe output on error */
   1115		_dpu_plane_color_fill(pdpu, 0xFFFFFF, 0xFF);
   1116	else if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG)
   1117		/* force 100% alpha */
   1118		_dpu_plane_color_fill(pdpu, pdpu->color_fill, 0xFF);
   1119	else if (pdpu->pipe_hw && pdpu->pipe_hw->ops.setup_csc) {
   1120		const struct dpu_format *fmt = to_dpu_format(msm_framebuffer_format(plane->state->fb));
   1121		const struct dpu_csc_cfg *csc_ptr = _dpu_plane_get_csc(pdpu, fmt);
   1122
   1123		if (csc_ptr)
   1124			pdpu->pipe_hw->ops.setup_csc(pdpu->pipe_hw, csc_ptr);
   1125	}
   1126
   1127	/* flag h/w flush complete */
   1128	if (plane->state)
   1129		pstate->pending = false;
   1130}
   1131
   1132/**
   1133 * dpu_plane_set_error: enable/disable error condition
   1134 * @plane: pointer to drm_plane structure
   1135 * @error: error value to set
   1136 */
   1137void dpu_plane_set_error(struct drm_plane *plane, bool error)
   1138{
   1139	struct dpu_plane *pdpu;
   1140
   1141	if (!plane)
   1142		return;
   1143
   1144	pdpu = to_dpu_plane(plane);
   1145	pdpu->is_error = error;
   1146}
   1147
   1148static void dpu_plane_sspp_atomic_update(struct drm_plane *plane)
   1149{
   1150	uint32_t src_flags;
   1151	struct dpu_plane *pdpu = to_dpu_plane(plane);
   1152	struct drm_plane_state *state = plane->state;
   1153	struct dpu_plane_state *pstate = to_dpu_plane_state(state);
   1154	struct drm_crtc *crtc = state->crtc;
   1155	struct drm_framebuffer *fb = state->fb;
   1156	bool is_rt_pipe, update_qos_remap;
   1157	const struct dpu_format *fmt =
   1158		to_dpu_format(msm_framebuffer_format(fb));
   1159	struct dpu_hw_pipe_cfg pipe_cfg;
   1160
   1161	memset(&pipe_cfg, 0, sizeof(struct dpu_hw_pipe_cfg));
   1162
   1163	_dpu_plane_set_scanout(plane, pstate, &pipe_cfg, fb);
   1164
   1165	pstate->pending = true;
   1166
   1167	is_rt_pipe = (dpu_crtc_get_client_type(crtc) != NRT_CLIENT);
   1168	_dpu_plane_set_qos_ctrl(plane, false, DPU_PLANE_QOS_PANIC_CTRL);
   1169
   1170	DPU_DEBUG_PLANE(pdpu, "FB[%u] " DRM_RECT_FP_FMT "->crtc%u " DRM_RECT_FMT
   1171			", %4.4s ubwc %d\n", fb->base.id, DRM_RECT_FP_ARG(&state->src),
   1172			crtc->base.id, DRM_RECT_ARG(&state->dst),
   1173			(char *)&fmt->base.pixel_format, DPU_FORMAT_IS_UBWC(fmt));
   1174
   1175	pipe_cfg.src_rect = state->src;
   1176
   1177	/* state->src is 16.16, src_rect is not */
   1178	pipe_cfg.src_rect.x1 >>= 16;
   1179	pipe_cfg.src_rect.x2 >>= 16;
   1180	pipe_cfg.src_rect.y1 >>= 16;
   1181	pipe_cfg.src_rect.y2 >>= 16;
   1182
   1183	pipe_cfg.dst_rect = state->dst;
   1184
   1185	/* override for color fill */
   1186	if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG) {
   1187		/* skip remaining processing on color fill */
   1188		return;
   1189	}
   1190
   1191	if (pdpu->pipe_hw->ops.setup_rects) {
   1192		pdpu->pipe_hw->ops.setup_rects(pdpu->pipe_hw,
   1193				&pipe_cfg,
   1194				pstate->multirect_index);
   1195	}
   1196
   1197	_dpu_plane_setup_scaler(pdpu, pstate, fmt, false, &pipe_cfg);
   1198
   1199	if (pdpu->pipe_hw->ops.setup_multirect)
   1200		pdpu->pipe_hw->ops.setup_multirect(
   1201				pdpu->pipe_hw,
   1202				pstate->multirect_index,
   1203				pstate->multirect_mode);
   1204
   1205	if (pdpu->pipe_hw->ops.setup_format) {
   1206		unsigned int rotation = pstate->rotation;
   1207
   1208		src_flags = 0x0;
   1209
   1210		if (rotation & DRM_MODE_REFLECT_X)
   1211			src_flags |= DPU_SSPP_FLIP_LR;
   1212
   1213		if (rotation & DRM_MODE_REFLECT_Y)
   1214			src_flags |= DPU_SSPP_FLIP_UD;
   1215
   1216		if (rotation & DRM_MODE_ROTATE_90)
   1217			src_flags |= DPU_SSPP_ROT_90;
   1218
   1219		/* update format */
   1220		pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw, fmt, src_flags,
   1221				pstate->multirect_index);
   1222
   1223		if (pdpu->pipe_hw->ops.setup_cdp) {
   1224			struct dpu_hw_cdp_cfg cdp_cfg;
   1225
   1226			memset(&cdp_cfg, 0, sizeof(struct dpu_hw_cdp_cfg));
   1227
   1228			cdp_cfg.enable = pdpu->catalog->perf.cdp_cfg
   1229					[DPU_PERF_CDP_USAGE_RT].rd_enable;
   1230			cdp_cfg.ubwc_meta_enable =
   1231					DPU_FORMAT_IS_UBWC(fmt);
   1232			cdp_cfg.tile_amortize_enable =
   1233					DPU_FORMAT_IS_UBWC(fmt) ||
   1234					DPU_FORMAT_IS_TILE(fmt);
   1235			cdp_cfg.preload_ahead = DPU_SSPP_CDP_PRELOAD_AHEAD_64;
   1236
   1237			pdpu->pipe_hw->ops.setup_cdp(pdpu->pipe_hw, &cdp_cfg, pstate->multirect_index);
   1238		}
   1239	}
   1240
   1241	_dpu_plane_set_qos_lut(plane, fb, &pipe_cfg);
   1242	_dpu_plane_set_danger_lut(plane, fb);
   1243
   1244	if (plane->type != DRM_PLANE_TYPE_CURSOR) {
   1245		_dpu_plane_set_qos_ctrl(plane, true, DPU_PLANE_QOS_PANIC_CTRL);
   1246		_dpu_plane_set_ot_limit(plane, crtc, &pipe_cfg);
   1247	}
   1248
   1249	update_qos_remap = (is_rt_pipe != pdpu->is_rt_pipe) ||
   1250			pstate->needs_qos_remap;
   1251
   1252	if (update_qos_remap) {
   1253		if (is_rt_pipe != pdpu->is_rt_pipe)
   1254			pdpu->is_rt_pipe = is_rt_pipe;
   1255		else if (pstate->needs_qos_remap)
   1256			pstate->needs_qos_remap = false;
   1257		_dpu_plane_set_qos_remap(plane);
   1258	}
   1259
   1260	_dpu_plane_calc_bw(plane, fb, &pipe_cfg);
   1261
   1262	_dpu_plane_calc_clk(plane, &pipe_cfg);
   1263}
   1264
   1265static void _dpu_plane_atomic_disable(struct drm_plane *plane)
   1266{
   1267	struct dpu_plane *pdpu = to_dpu_plane(plane);
   1268	struct drm_plane_state *state = plane->state;
   1269	struct dpu_plane_state *pstate = to_dpu_plane_state(state);
   1270
   1271	trace_dpu_plane_disable(DRMID(plane), is_dpu_plane_virtual(plane),
   1272				pstate->multirect_mode);
   1273
   1274	pstate->pending = true;
   1275
   1276	if (is_dpu_plane_virtual(plane) &&
   1277			pdpu->pipe_hw && pdpu->pipe_hw->ops.setup_multirect)
   1278		pdpu->pipe_hw->ops.setup_multirect(pdpu->pipe_hw,
   1279				DPU_SSPP_RECT_SOLO, DPU_SSPP_MULTIRECT_NONE);
   1280}
   1281
   1282static void dpu_plane_atomic_update(struct drm_plane *plane,
   1283				struct drm_atomic_state *state)
   1284{
   1285	struct dpu_plane *pdpu = to_dpu_plane(plane);
   1286	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
   1287									   plane);
   1288
   1289	pdpu->is_error = false;
   1290
   1291	DPU_DEBUG_PLANE(pdpu, "\n");
   1292
   1293	if (!new_state->visible) {
   1294		_dpu_plane_atomic_disable(plane);
   1295	} else {
   1296		dpu_plane_sspp_atomic_update(plane);
   1297	}
   1298}
   1299
   1300static void dpu_plane_destroy(struct drm_plane *plane)
   1301{
   1302	struct dpu_plane *pdpu = plane ? to_dpu_plane(plane) : NULL;
   1303
   1304	DPU_DEBUG_PLANE(pdpu, "\n");
   1305
   1306	if (pdpu) {
   1307		_dpu_plane_set_qos_ctrl(plane, false, DPU_PLANE_QOS_PANIC_CTRL);
   1308
   1309		mutex_destroy(&pdpu->lock);
   1310
   1311		/* this will destroy the states as well */
   1312		drm_plane_cleanup(plane);
   1313
   1314		dpu_hw_sspp_destroy(pdpu->pipe_hw);
   1315
   1316		kfree(pdpu);
   1317	}
   1318}
   1319
   1320static void dpu_plane_destroy_state(struct drm_plane *plane,
   1321		struct drm_plane_state *state)
   1322{
   1323	__drm_atomic_helper_plane_destroy_state(state);
   1324	kfree(to_dpu_plane_state(state));
   1325}
   1326
   1327static struct drm_plane_state *
   1328dpu_plane_duplicate_state(struct drm_plane *plane)
   1329{
   1330	struct dpu_plane *pdpu;
   1331	struct dpu_plane_state *pstate;
   1332	struct dpu_plane_state *old_state;
   1333
   1334	if (!plane) {
   1335		DPU_ERROR("invalid plane\n");
   1336		return NULL;
   1337	} else if (!plane->state) {
   1338		DPU_ERROR("invalid plane state\n");
   1339		return NULL;
   1340	}
   1341
   1342	old_state = to_dpu_plane_state(plane->state);
   1343	pdpu = to_dpu_plane(plane);
   1344	pstate = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL);
   1345	if (!pstate) {
   1346		DPU_ERROR_PLANE(pdpu, "failed to allocate state\n");
   1347		return NULL;
   1348	}
   1349
   1350	DPU_DEBUG_PLANE(pdpu, "\n");
   1351
   1352	pstate->pending = false;
   1353
   1354	__drm_atomic_helper_plane_duplicate_state(plane, &pstate->base);
   1355
   1356	return &pstate->base;
   1357}
   1358
   1359static const char * const multirect_mode_name[] = {
   1360	[DPU_SSPP_MULTIRECT_NONE] = "none",
   1361	[DPU_SSPP_MULTIRECT_PARALLEL] = "parallel",
   1362	[DPU_SSPP_MULTIRECT_TIME_MX] = "time_mx",
   1363};
   1364
   1365static const char * const multirect_index_name[] = {
   1366	[DPU_SSPP_RECT_SOLO] = "solo",
   1367	[DPU_SSPP_RECT_0] = "rect_0",
   1368	[DPU_SSPP_RECT_1] = "rect_1",
   1369};
   1370
   1371static const char *dpu_get_multirect_mode(enum dpu_sspp_multirect_mode mode)
   1372{
   1373	if (WARN_ON(mode >= ARRAY_SIZE(multirect_mode_name)))
   1374		return "unknown";
   1375
   1376	return multirect_mode_name[mode];
   1377}
   1378
   1379static const char *dpu_get_multirect_index(enum dpu_sspp_multirect_index index)
   1380{
   1381	if (WARN_ON(index >= ARRAY_SIZE(multirect_index_name)))
   1382		return "unknown";
   1383
   1384	return multirect_index_name[index];
   1385}
   1386
   1387static void dpu_plane_atomic_print_state(struct drm_printer *p,
   1388		const struct drm_plane_state *state)
   1389{
   1390	const struct dpu_plane_state *pstate = to_dpu_plane_state(state);
   1391	const struct dpu_plane *pdpu = to_dpu_plane(state->plane);
   1392
   1393	drm_printf(p, "\tstage=%d\n", pstate->stage);
   1394	drm_printf(p, "\tsspp=%s\n", pdpu->pipe_hw->cap->name);
   1395	drm_printf(p, "\tmultirect_mode=%s\n", dpu_get_multirect_mode(pstate->multirect_mode));
   1396	drm_printf(p, "\tmultirect_index=%s\n", dpu_get_multirect_index(pstate->multirect_index));
   1397}
   1398
   1399static void dpu_plane_reset(struct drm_plane *plane)
   1400{
   1401	struct dpu_plane *pdpu;
   1402	struct dpu_plane_state *pstate;
   1403
   1404	if (!plane) {
   1405		DPU_ERROR("invalid plane\n");
   1406		return;
   1407	}
   1408
   1409	pdpu = to_dpu_plane(plane);
   1410	DPU_DEBUG_PLANE(pdpu, "\n");
   1411
   1412	/* remove previous state, if present */
   1413	if (plane->state) {
   1414		dpu_plane_destroy_state(plane, plane->state);
   1415		plane->state = NULL;
   1416	}
   1417
   1418	pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
   1419	if (!pstate) {
   1420		DPU_ERROR_PLANE(pdpu, "failed to allocate state\n");
   1421		return;
   1422	}
   1423
   1424	__drm_atomic_helper_plane_reset(plane, &pstate->base);
   1425}
   1426
   1427#ifdef CONFIG_DEBUG_FS
   1428void dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable)
   1429{
   1430	struct dpu_plane *pdpu = to_dpu_plane(plane);
   1431	struct dpu_kms *dpu_kms = _dpu_plane_get_kms(plane);
   1432
   1433	if (!pdpu->is_rt_pipe)
   1434		return;
   1435
   1436	pm_runtime_get_sync(&dpu_kms->pdev->dev);
   1437	_dpu_plane_set_qos_ctrl(plane, enable, DPU_PLANE_QOS_PANIC_CTRL);
   1438	pm_runtime_put_sync(&dpu_kms->pdev->dev);
   1439}
   1440
   1441/* SSPP live inside dpu_plane private data only. Enumerate them here. */
   1442void dpu_debugfs_sspp_init(struct dpu_kms *dpu_kms, struct dentry *debugfs_root)
   1443{
   1444	struct drm_plane *plane;
   1445	struct dentry *entry = debugfs_create_dir("sspp", debugfs_root);
   1446
   1447	if (IS_ERR(entry))
   1448		return;
   1449
   1450	drm_for_each_plane(plane, dpu_kms->dev) {
   1451		struct dpu_plane *pdpu = to_dpu_plane(plane);
   1452
   1453		_dpu_hw_sspp_init_debugfs(pdpu->pipe_hw, dpu_kms, entry);
   1454	}
   1455}
   1456#endif
   1457
   1458static bool dpu_plane_format_mod_supported(struct drm_plane *plane,
   1459		uint32_t format, uint64_t modifier)
   1460{
   1461	if (modifier == DRM_FORMAT_MOD_LINEAR)
   1462		return true;
   1463
   1464	if (modifier == DRM_FORMAT_MOD_QCOM_COMPRESSED)
   1465		return dpu_find_format(format, qcom_compressed_supported_formats,
   1466				ARRAY_SIZE(qcom_compressed_supported_formats));
   1467
   1468	return false;
   1469}
   1470
   1471static const struct drm_plane_funcs dpu_plane_funcs = {
   1472		.update_plane = drm_atomic_helper_update_plane,
   1473		.disable_plane = drm_atomic_helper_disable_plane,
   1474		.destroy = dpu_plane_destroy,
   1475		.reset = dpu_plane_reset,
   1476		.atomic_duplicate_state = dpu_plane_duplicate_state,
   1477		.atomic_destroy_state = dpu_plane_destroy_state,
   1478		.atomic_print_state = dpu_plane_atomic_print_state,
   1479		.format_mod_supported = dpu_plane_format_mod_supported,
   1480};
   1481
   1482static const struct drm_plane_helper_funcs dpu_plane_helper_funcs = {
   1483		.prepare_fb = dpu_plane_prepare_fb,
   1484		.cleanup_fb = dpu_plane_cleanup_fb,
   1485		.atomic_check = dpu_plane_atomic_check,
   1486		.atomic_update = dpu_plane_atomic_update,
   1487};
   1488
   1489enum dpu_sspp dpu_plane_pipe(struct drm_plane *plane)
   1490{
   1491	return plane ? to_dpu_plane(plane)->pipe : SSPP_NONE;
   1492}
   1493
   1494bool is_dpu_plane_virtual(struct drm_plane *plane)
   1495{
   1496	return plane ? to_dpu_plane(plane)->is_virtual : false;
   1497}
   1498
   1499/* initialize plane */
   1500struct drm_plane *dpu_plane_init(struct drm_device *dev,
   1501		uint32_t pipe, enum drm_plane_type type,
   1502		unsigned long possible_crtcs, u32 master_plane_id)
   1503{
   1504	struct drm_plane *plane = NULL, *master_plane = NULL;
   1505	const uint32_t *format_list;
   1506	struct dpu_plane *pdpu;
   1507	struct msm_drm_private *priv = dev->dev_private;
   1508	struct dpu_kms *kms = to_dpu_kms(priv->kms);
   1509	int zpos_max = DPU_ZPOS_MAX;
   1510	uint32_t num_formats;
   1511	uint32_t supported_rotations;
   1512	int ret = -EINVAL;
   1513
   1514	/* create and zero local structure */
   1515	pdpu = kzalloc(sizeof(*pdpu), GFP_KERNEL);
   1516	if (!pdpu) {
   1517		DPU_ERROR("[%u]failed to allocate local plane struct\n", pipe);
   1518		ret = -ENOMEM;
   1519		return ERR_PTR(ret);
   1520	}
   1521
   1522	/* cache local stuff for later */
   1523	plane = &pdpu->base;
   1524	pdpu->pipe = pipe;
   1525	pdpu->is_virtual = (master_plane_id != 0);
   1526	INIT_LIST_HEAD(&pdpu->mplane_list);
   1527	master_plane = drm_plane_find(dev, NULL, master_plane_id);
   1528	if (master_plane) {
   1529		struct dpu_plane *mpdpu = to_dpu_plane(master_plane);
   1530
   1531		list_add_tail(&pdpu->mplane_list, &mpdpu->mplane_list);
   1532	}
   1533
   1534	/* initialize underlying h/w driver */
   1535	pdpu->pipe_hw = dpu_hw_sspp_init(pipe, kms->mmio, kms->catalog,
   1536							master_plane_id != 0);
   1537	if (IS_ERR(pdpu->pipe_hw)) {
   1538		DPU_ERROR("[%u]SSPP init failed\n", pipe);
   1539		ret = PTR_ERR(pdpu->pipe_hw);
   1540		goto clean_plane;
   1541	} else if (!pdpu->pipe_hw->cap || !pdpu->pipe_hw->cap->sblk) {
   1542		DPU_ERROR("[%u]SSPP init returned invalid cfg\n", pipe);
   1543		goto clean_sspp;
   1544	}
   1545
   1546	if (pdpu->is_virtual) {
   1547		format_list = pdpu->pipe_hw->cap->sblk->virt_format_list;
   1548		num_formats = pdpu->pipe_hw->cap->sblk->virt_num_formats;
   1549	}
   1550	else {
   1551		format_list = pdpu->pipe_hw->cap->sblk->format_list;
   1552		num_formats = pdpu->pipe_hw->cap->sblk->num_formats;
   1553	}
   1554
   1555	ret = drm_universal_plane_init(dev, plane, 0xff, &dpu_plane_funcs,
   1556				format_list, num_formats,
   1557				supported_format_modifiers, type, NULL);
   1558	if (ret)
   1559		goto clean_sspp;
   1560
   1561	pdpu->catalog = kms->catalog;
   1562
   1563	if (kms->catalog->mixer_count &&
   1564		kms->catalog->mixer[0].sblk->maxblendstages) {
   1565		zpos_max = kms->catalog->mixer[0].sblk->maxblendstages - 1;
   1566		if (zpos_max > DPU_STAGE_MAX - DPU_STAGE_0 - 1)
   1567			zpos_max = DPU_STAGE_MAX - DPU_STAGE_0 - 1;
   1568	}
   1569
   1570	ret = drm_plane_create_zpos_property(plane, 0, 0, zpos_max);
   1571	if (ret)
   1572		DPU_ERROR("failed to install zpos property, rc = %d\n", ret);
   1573
   1574	drm_plane_create_alpha_property(plane);
   1575	drm_plane_create_blend_mode_property(plane,
   1576			BIT(DRM_MODE_BLEND_PIXEL_NONE) |
   1577			BIT(DRM_MODE_BLEND_PREMULTI) |
   1578			BIT(DRM_MODE_BLEND_COVERAGE));
   1579
   1580	supported_rotations = DRM_MODE_REFLECT_MASK | DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_180;
   1581
   1582	if (pdpu->pipe_hw->cap->features & BIT(DPU_SSPP_INLINE_ROTATION))
   1583		supported_rotations |= DRM_MODE_ROTATE_MASK;
   1584
   1585	drm_plane_create_rotation_property(plane,
   1586		    DRM_MODE_ROTATE_0, supported_rotations);
   1587
   1588	drm_plane_enable_fb_damage_clips(plane);
   1589
   1590	/* success! finalize initialization */
   1591	drm_plane_helper_add(plane, &dpu_plane_helper_funcs);
   1592
   1593	mutex_init(&pdpu->lock);
   1594
   1595	DPU_DEBUG("%s created for pipe:%u id:%u virtual:%u\n", plane->name,
   1596					pipe, plane->base.id, master_plane_id);
   1597	return plane;
   1598
   1599clean_sspp:
   1600	if (pdpu && pdpu->pipe_hw)
   1601		dpu_hw_sspp_destroy(pdpu->pipe_hw);
   1602clean_plane:
   1603	list_del(&pdpu->mplane_list);
   1604	kfree(pdpu);
   1605	return ERR_PTR(ret);
   1606}