drm/msm: dpu: Allow planes to extend past active display
[linux-2.6-block.git] / drivers / gpu / drm / msm / disp / dpu1 / dpu_plane.c
CommitLineData
25fdd593
JS
1/*
2 * Copyright (C) 2014-2018 The Linux Foundation. All rights reserved.
3 * Copyright (C) 2013 Red Hat
4 * Author: Rob Clark <robdclark@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 as published by
8 * the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along with
16 * this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__
20
21#include <linux/debugfs.h>
22#include <linux/dma-buf.h>
23
72fdb40c
DV
24#include <drm/drm_atomic_uapi.h>
25
25fdd593
JS
26#include "msm_drv.h"
27#include "dpu_kms.h"
28#include "dpu_formats.h"
29#include "dpu_hw_sspp.h"
30#include "dpu_hw_catalog_format.h"
31#include "dpu_trace.h"
32#include "dpu_crtc.h"
33#include "dpu_vbif.h"
34#include "dpu_plane.h"
35
36#define DPU_DEBUG_PLANE(pl, fmt, ...) DPU_DEBUG("plane%d " fmt,\
37 (pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
38
39#define DPU_ERROR_PLANE(pl, fmt, ...) DPU_ERROR("plane%d " fmt,\
40 (pl) ? (pl)->base.base.id : -1, ##__VA_ARGS__)
41
42#define DECIMATED_DIMENSION(dim, deci) (((dim) + ((1 << (deci)) - 1)) >> (deci))
43#define PHASE_STEP_SHIFT 21
44#define PHASE_STEP_UNIT_SCALE ((int) (1 << PHASE_STEP_SHIFT))
45#define PHASE_RESIDUAL 15
46
47#define SHARP_STRENGTH_DEFAULT 32
48#define SHARP_EDGE_THR_DEFAULT 112
49#define SHARP_SMOOTH_THR_DEFAULT 8
50#define SHARP_NOISE_THR_DEFAULT 2
51
52#define DPU_NAME_SIZE 12
53
54#define DPU_PLANE_COLOR_FILL_FLAG BIT(31)
55#define DPU_ZPOS_MAX 255
56
57/* multirect rect index */
58enum {
59 R0,
60 R1,
61 R_MAX
62};
63
64#define DPU_QSEED3_DEFAULT_PRELOAD_H 0x4
65#define DPU_QSEED3_DEFAULT_PRELOAD_V 0x3
66
67#define DEFAULT_REFRESH_RATE 60
68
69/**
70 * enum dpu_plane_qos - Different qos configurations for each pipe
71 *
72 * @DPU_PLANE_QOS_VBLANK_CTRL: Setup VBLANK qos for the pipe.
73 * @DPU_PLANE_QOS_VBLANK_AMORTIZE: Enables Amortization within pipe.
74 * this configuration is mutually exclusive from VBLANK_CTRL.
75 * @DPU_PLANE_QOS_PANIC_CTRL: Setup panic for the pipe.
76 */
77enum dpu_plane_qos {
78 DPU_PLANE_QOS_VBLANK_CTRL = BIT(0),
79 DPU_PLANE_QOS_VBLANK_AMORTIZE = BIT(1),
80 DPU_PLANE_QOS_PANIC_CTRL = BIT(2),
81};
82
83/*
84 * struct dpu_plane - local dpu plane structure
85 * @aspace: address space pointer
86 * @csc_ptr: Points to dpu_csc_cfg structure to use for current
87 * @mplane_list: List of multirect planes of the same pipe
88 * @catalog: Points to dpu catalog structure
89 * @revalidate: force revalidation of all the plane properties
90 */
91struct dpu_plane {
92 struct drm_plane base;
93
94 struct mutex lock;
95
96 enum dpu_sspp pipe;
97 uint32_t features; /* capabilities from catalog */
98 uint32_t nformats;
99 uint32_t formats[64];
100
101 struct dpu_hw_pipe *pipe_hw;
102 struct dpu_hw_pipe_cfg pipe_cfg;
103 struct dpu_hw_pipe_qos_cfg pipe_qos_cfg;
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 struct dpu_csc_cfg *csc_ptr;
112
113 const struct dpu_sspp_sub_blks *pipe_sblk;
114 char pipe_name[DPU_NAME_SIZE];
115
116 /* debugfs related stuff */
117 struct dentry *debugfs_root;
118 struct dpu_debugfs_regset32 debugfs_src;
119 struct dpu_debugfs_regset32 debugfs_scaler;
120 struct dpu_debugfs_regset32 debugfs_csc;
121 bool debugfs_default_scale;
122};
123
124#define to_dpu_plane(x) container_of(x, struct dpu_plane, base)
125
126static struct dpu_kms *_dpu_plane_get_kms(struct drm_plane *plane)
127{
128 struct msm_drm_private *priv;
129
130 if (!plane || !plane->dev)
131 return NULL;
132 priv = plane->dev->dev_private;
133 if (!priv)
134 return NULL;
135 return to_dpu_kms(priv->kms);
136}
137
138static bool dpu_plane_enabled(struct drm_plane_state *state)
139{
140 return state && state->fb && state->crtc;
141}
142
143static bool dpu_plane_sspp_enabled(struct drm_plane_state *state)
144{
145 return state && state->crtc;
146}
147
148/**
149 * _dpu_plane_calc_fill_level - calculate fill level of the given source format
150 * @plane: Pointer to drm plane
151 * @fmt: Pointer to source buffer format
152 * @src_wdith: width of source buffer
153 * Return: fill level corresponding to the source buffer/format or 0 if error
154 */
155static inline int _dpu_plane_calc_fill_level(struct drm_plane *plane,
156 const struct dpu_format *fmt, u32 src_width)
157{
158 struct dpu_plane *pdpu, *tmp;
159 struct dpu_plane_state *pstate;
160 u32 fixed_buff_size;
161 u32 total_fl;
162
163 if (!plane || !fmt || !plane->state || !src_width || !fmt->bpp) {
164 DPU_ERROR("invalid arguments\n");
165 return 0;
166 }
167
168 pdpu = to_dpu_plane(plane);
169 pstate = to_dpu_plane_state(plane->state);
170 fixed_buff_size = pdpu->pipe_sblk->common->pixel_ram_size;
171
172 list_for_each_entry(tmp, &pdpu->mplane_list, mplane_list) {
173 if (!dpu_plane_enabled(tmp->base.state))
174 continue;
175 DPU_DEBUG("plane%d/%d src_width:%d/%d\n",
176 pdpu->base.base.id, tmp->base.base.id,
177 src_width,
178 drm_rect_width(&tmp->pipe_cfg.src_rect));
179 src_width = max_t(u32, src_width,
180 drm_rect_width(&tmp->pipe_cfg.src_rect));
181 }
182
183 if (fmt->fetch_planes == DPU_PLANE_PSEUDO_PLANAR) {
184 if (fmt->chroma_sample == DPU_CHROMA_420) {
185 /* NV12 */
186 total_fl = (fixed_buff_size / 2) /
187 ((src_width + 32) * fmt->bpp);
188 } else {
189 /* non NV12 */
190 total_fl = (fixed_buff_size / 2) * 2 /
191 ((src_width + 32) * fmt->bpp);
192 }
193 } else {
194 if (pstate->multirect_mode == DPU_SSPP_MULTIRECT_PARALLEL) {
195 total_fl = (fixed_buff_size / 2) * 2 /
196 ((src_width + 32) * fmt->bpp);
197 } else {
198 total_fl = (fixed_buff_size) * 2 /
199 ((src_width + 32) * fmt->bpp);
200 }
201 }
202
203 DPU_DEBUG("plane%u: pnum:%d fmt: %4.4s w:%u fl:%u\n",
204 plane->base.id, pdpu->pipe - SSPP_VIG0,
205 (char *)&fmt->base.pixel_format,
206 src_width, total_fl);
207
208 return total_fl;
209}
210
211/**
212 * _dpu_plane_get_qos_lut - get LUT mapping based on fill level
213 * @tbl: Pointer to LUT table
214 * @total_fl: fill level
215 * Return: LUT setting corresponding to the fill level
216 */
217static u64 _dpu_plane_get_qos_lut(const struct dpu_qos_lut_tbl *tbl,
218 u32 total_fl)
219{
220 int i;
221
222 if (!tbl || !tbl->nentry || !tbl->entries)
223 return 0;
224
225 for (i = 0; i < tbl->nentry; i++)
226 if (total_fl <= tbl->entries[i].fl)
227 return tbl->entries[i].lut;
228
229 /* if last fl is zero, use as default */
230 if (!tbl->entries[i-1].fl)
231 return tbl->entries[i-1].lut;
232
233 return 0;
234}
235
236/**
237 * _dpu_plane_set_qos_lut - set QoS LUT of the given plane
238 * @plane: Pointer to drm plane
239 * @fb: Pointer to framebuffer associated with the given plane
240 */
241static void _dpu_plane_set_qos_lut(struct drm_plane *plane,
242 struct drm_framebuffer *fb)
243{
244 struct dpu_plane *pdpu;
245 const struct dpu_format *fmt = NULL;
246 u64 qos_lut;
247 u32 total_fl = 0, lut_usage;
248
249 if (!plane || !fb) {
250 DPU_ERROR("invalid arguments plane %d fb %d\n",
251 plane != 0, fb != 0);
252 return;
253 }
254
255 pdpu = to_dpu_plane(plane);
256
257 if (!pdpu->pipe_hw || !pdpu->pipe_sblk || !pdpu->catalog) {
258 DPU_ERROR("invalid arguments\n");
259 return;
260 } else if (!pdpu->pipe_hw->ops.setup_creq_lut) {
261 return;
262 }
263
264 if (!pdpu->is_rt_pipe) {
265 lut_usage = DPU_QOS_LUT_USAGE_NRT;
266 } else {
267 fmt = dpu_get_dpu_format_ext(
268 fb->format->format,
269 fb->modifier);
270 total_fl = _dpu_plane_calc_fill_level(plane, fmt,
271 drm_rect_width(&pdpu->pipe_cfg.src_rect));
272
273 if (fmt && DPU_FORMAT_IS_LINEAR(fmt))
274 lut_usage = DPU_QOS_LUT_USAGE_LINEAR;
275 else
276 lut_usage = DPU_QOS_LUT_USAGE_MACROTILE;
277 }
278
279 qos_lut = _dpu_plane_get_qos_lut(
280 &pdpu->catalog->perf.qos_lut_tbl[lut_usage], total_fl);
281
282 pdpu->pipe_qos_cfg.creq_lut = qos_lut;
283
284 trace_dpu_perf_set_qos_luts(pdpu->pipe - SSPP_VIG0,
285 (fmt) ? fmt->base.pixel_format : 0,
286 pdpu->is_rt_pipe, total_fl, qos_lut, lut_usage);
287
288 DPU_DEBUG("plane%u: pnum:%d fmt: %4.4s rt:%d fl:%u lut:0x%llx\n",
289 plane->base.id,
290 pdpu->pipe - SSPP_VIG0,
291 fmt ? (char *)&fmt->base.pixel_format : NULL,
292 pdpu->is_rt_pipe, total_fl, qos_lut);
293
294 pdpu->pipe_hw->ops.setup_creq_lut(pdpu->pipe_hw, &pdpu->pipe_qos_cfg);
295}
296
297/**
298 * _dpu_plane_set_panic_lut - set danger/safe LUT of the given plane
299 * @plane: Pointer to drm plane
300 * @fb: Pointer to framebuffer associated with the given plane
301 */
302static void _dpu_plane_set_danger_lut(struct drm_plane *plane,
303 struct drm_framebuffer *fb)
304{
305 struct dpu_plane *pdpu;
306 const struct dpu_format *fmt = NULL;
307 u32 danger_lut, safe_lut;
308
309 if (!plane || !fb) {
310 DPU_ERROR("invalid arguments\n");
311 return;
312 }
313
314 pdpu = to_dpu_plane(plane);
315
316 if (!pdpu->pipe_hw || !pdpu->pipe_sblk || !pdpu->catalog) {
317 DPU_ERROR("invalid arguments\n");
318 return;
319 } else if (!pdpu->pipe_hw->ops.setup_danger_safe_lut) {
320 return;
321 }
322
323 if (!pdpu->is_rt_pipe) {
324 danger_lut = pdpu->catalog->perf.danger_lut_tbl
325 [DPU_QOS_LUT_USAGE_NRT];
326 safe_lut = pdpu->catalog->perf.safe_lut_tbl
327 [DPU_QOS_LUT_USAGE_NRT];
328 } else {
329 fmt = dpu_get_dpu_format_ext(
330 fb->format->format,
331 fb->modifier);
332
333 if (fmt && DPU_FORMAT_IS_LINEAR(fmt)) {
334 danger_lut = pdpu->catalog->perf.danger_lut_tbl
335 [DPU_QOS_LUT_USAGE_LINEAR];
336 safe_lut = pdpu->catalog->perf.safe_lut_tbl
337 [DPU_QOS_LUT_USAGE_LINEAR];
338 } else {
339 danger_lut = pdpu->catalog->perf.danger_lut_tbl
340 [DPU_QOS_LUT_USAGE_MACROTILE];
341 safe_lut = pdpu->catalog->perf.safe_lut_tbl
342 [DPU_QOS_LUT_USAGE_MACROTILE];
343 }
344 }
345
346 pdpu->pipe_qos_cfg.danger_lut = danger_lut;
347 pdpu->pipe_qos_cfg.safe_lut = safe_lut;
348
349 trace_dpu_perf_set_danger_luts(pdpu->pipe - SSPP_VIG0,
350 (fmt) ? fmt->base.pixel_format : 0,
351 (fmt) ? fmt->fetch_mode : 0,
352 pdpu->pipe_qos_cfg.danger_lut,
353 pdpu->pipe_qos_cfg.safe_lut);
354
355 DPU_DEBUG("plane%u: pnum:%d fmt: %4.4s mode:%d luts[0x%x, 0x%x]\n",
356 plane->base.id,
357 pdpu->pipe - SSPP_VIG0,
358 fmt ? (char *)&fmt->base.pixel_format : NULL,
359 fmt ? fmt->fetch_mode : -1,
360 pdpu->pipe_qos_cfg.danger_lut,
361 pdpu->pipe_qos_cfg.safe_lut);
362
363 pdpu->pipe_hw->ops.setup_danger_safe_lut(pdpu->pipe_hw,
364 &pdpu->pipe_qos_cfg);
365}
366
367/**
368 * _dpu_plane_set_qos_ctrl - set QoS control of the given plane
369 * @plane: Pointer to drm plane
370 * @enable: true to enable QoS control
371 * @flags: QoS control mode (enum dpu_plane_qos)
372 */
373static void _dpu_plane_set_qos_ctrl(struct drm_plane *plane,
374 bool enable, u32 flags)
375{
376 struct dpu_plane *pdpu;
377
378 if (!plane) {
379 DPU_ERROR("invalid arguments\n");
380 return;
381 }
382
383 pdpu = to_dpu_plane(plane);
384
385 if (!pdpu->pipe_hw || !pdpu->pipe_sblk) {
386 DPU_ERROR("invalid arguments\n");
387 return;
388 } else if (!pdpu->pipe_hw->ops.setup_qos_ctrl) {
389 return;
390 }
391
392 if (flags & DPU_PLANE_QOS_VBLANK_CTRL) {
393 pdpu->pipe_qos_cfg.creq_vblank = pdpu->pipe_sblk->creq_vblank;
394 pdpu->pipe_qos_cfg.danger_vblank =
395 pdpu->pipe_sblk->danger_vblank;
396 pdpu->pipe_qos_cfg.vblank_en = enable;
397 }
398
399 if (flags & DPU_PLANE_QOS_VBLANK_AMORTIZE) {
400 /* this feature overrules previous VBLANK_CTRL */
401 pdpu->pipe_qos_cfg.vblank_en = false;
402 pdpu->pipe_qos_cfg.creq_vblank = 0; /* clear vblank bits */
403 }
404
405 if (flags & DPU_PLANE_QOS_PANIC_CTRL)
406 pdpu->pipe_qos_cfg.danger_safe_en = enable;
407
408 if (!pdpu->is_rt_pipe) {
409 pdpu->pipe_qos_cfg.vblank_en = false;
410 pdpu->pipe_qos_cfg.danger_safe_en = false;
411 }
412
413 DPU_DEBUG("plane%u: pnum:%d ds:%d vb:%d pri[0x%x, 0x%x] is_rt:%d\n",
414 plane->base.id,
415 pdpu->pipe - SSPP_VIG0,
416 pdpu->pipe_qos_cfg.danger_safe_en,
417 pdpu->pipe_qos_cfg.vblank_en,
418 pdpu->pipe_qos_cfg.creq_vblank,
419 pdpu->pipe_qos_cfg.danger_vblank,
420 pdpu->is_rt_pipe);
421
422 pdpu->pipe_hw->ops.setup_qos_ctrl(pdpu->pipe_hw,
423 &pdpu->pipe_qos_cfg);
424}
425
426int dpu_plane_danger_signal_ctrl(struct drm_plane *plane, bool enable)
427{
428 struct dpu_plane *pdpu;
429 struct msm_drm_private *priv;
430 struct dpu_kms *dpu_kms;
431
432 if (!plane || !plane->dev) {
433 DPU_ERROR("invalid arguments\n");
434 return -EINVAL;
435 }
436
437 priv = plane->dev->dev_private;
438 if (!priv || !priv->kms) {
439 DPU_ERROR("invalid KMS reference\n");
440 return -EINVAL;
441 }
442
443 dpu_kms = to_dpu_kms(priv->kms);
444 pdpu = to_dpu_plane(plane);
445
446 if (!pdpu->is_rt_pipe)
447 goto end;
448
449 pm_runtime_get_sync(&dpu_kms->pdev->dev);
450 _dpu_plane_set_qos_ctrl(plane, enable, DPU_PLANE_QOS_PANIC_CTRL);
451 pm_runtime_put_sync(&dpu_kms->pdev->dev);
452
453end:
454 return 0;
455}
456
457/**
458 * _dpu_plane_set_ot_limit - set OT limit for the given plane
459 * @plane: Pointer to drm plane
460 * @crtc: Pointer to drm crtc
461 */
462static void _dpu_plane_set_ot_limit(struct drm_plane *plane,
463 struct drm_crtc *crtc)
464{
465 struct dpu_plane *pdpu;
466 struct dpu_vbif_set_ot_params ot_params;
467 struct msm_drm_private *priv;
468 struct dpu_kms *dpu_kms;
469
470 if (!plane || !plane->dev || !crtc) {
471 DPU_ERROR("invalid arguments plane %d crtc %d\n",
472 plane != 0, crtc != 0);
473 return;
474 }
475
476 priv = plane->dev->dev_private;
477 if (!priv || !priv->kms) {
478 DPU_ERROR("invalid KMS reference\n");
479 return;
480 }
481
482 dpu_kms = to_dpu_kms(priv->kms);
483 pdpu = to_dpu_plane(plane);
484 if (!pdpu->pipe_hw) {
485 DPU_ERROR("invalid pipe reference\n");
486 return;
487 }
488
489 memset(&ot_params, 0, sizeof(ot_params));
490 ot_params.xin_id = pdpu->pipe_hw->cap->xin_id;
491 ot_params.num = pdpu->pipe_hw->idx - SSPP_NONE;
492 ot_params.width = drm_rect_width(&pdpu->pipe_cfg.src_rect);
493 ot_params.height = drm_rect_height(&pdpu->pipe_cfg.src_rect);
494 ot_params.is_wfd = !pdpu->is_rt_pipe;
495 ot_params.frame_rate = crtc->mode.vrefresh;
496 ot_params.vbif_idx = VBIF_RT;
497 ot_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl;
498 ot_params.rd = true;
499
500 dpu_vbif_set_ot_limit(dpu_kms, &ot_params);
501}
502
503/**
504 * _dpu_plane_set_vbif_qos - set vbif QoS for the given plane
505 * @plane: Pointer to drm plane
506 */
507static void _dpu_plane_set_qos_remap(struct drm_plane *plane)
508{
509 struct dpu_plane *pdpu;
510 struct dpu_vbif_set_qos_params qos_params;
511 struct msm_drm_private *priv;
512 struct dpu_kms *dpu_kms;
513
514 if (!plane || !plane->dev) {
515 DPU_ERROR("invalid arguments\n");
516 return;
517 }
518
519 priv = plane->dev->dev_private;
520 if (!priv || !priv->kms) {
521 DPU_ERROR("invalid KMS reference\n");
522 return;
523 }
524
525 dpu_kms = to_dpu_kms(priv->kms);
526 pdpu = to_dpu_plane(plane);
527 if (!pdpu->pipe_hw) {
528 DPU_ERROR("invalid pipe reference\n");
529 return;
530 }
531
532 memset(&qos_params, 0, sizeof(qos_params));
533 qos_params.vbif_idx = VBIF_RT;
534 qos_params.clk_ctrl = pdpu->pipe_hw->cap->clk_ctrl;
535 qos_params.xin_id = pdpu->pipe_hw->cap->xin_id;
536 qos_params.num = pdpu->pipe_hw->idx - SSPP_VIG0;
537 qos_params.is_rt = pdpu->is_rt_pipe;
538
539 DPU_DEBUG("plane%d pipe:%d vbif:%d xin:%d rt:%d, clk_ctrl:%d\n",
540 plane->base.id, qos_params.num,
541 qos_params.vbif_idx,
542 qos_params.xin_id, qos_params.is_rt,
543 qos_params.clk_ctrl);
544
545 dpu_vbif_set_qos_remap(dpu_kms, &qos_params);
546}
547
548/**
549 * _dpu_plane_get_aspace: gets the address space
550 */
551static int _dpu_plane_get_aspace(
552 struct dpu_plane *pdpu,
553 struct dpu_plane_state *pstate,
554 struct msm_gem_address_space **aspace)
555{
556 struct dpu_kms *kms;
557
558 if (!pdpu || !pstate || !aspace) {
559 DPU_ERROR("invalid parameters\n");
560 return -EINVAL;
561 }
562
563 kms = _dpu_plane_get_kms(&pdpu->base);
564 if (!kms) {
565 DPU_ERROR("invalid kms\n");
566 return -EINVAL;
567 }
568
569 *aspace = kms->base.aspace;
570
571 return 0;
572}
573
574static inline void _dpu_plane_set_scanout(struct drm_plane *plane,
575 struct dpu_plane_state *pstate,
576 struct dpu_hw_pipe_cfg *pipe_cfg,
577 struct drm_framebuffer *fb)
578{
579 struct dpu_plane *pdpu;
580 struct msm_gem_address_space *aspace = NULL;
581 int ret;
582
583 if (!plane || !pstate || !pipe_cfg || !fb) {
584 DPU_ERROR(
585 "invalid arg(s), plane %d state %d cfg %d fb %d\n",
586 plane != 0, pstate != 0, pipe_cfg != 0, fb != 0);
587 return;
588 }
589
590 pdpu = to_dpu_plane(plane);
591 if (!pdpu->pipe_hw) {
592 DPU_ERROR_PLANE(pdpu, "invalid pipe_hw\n");
593 return;
594 }
595
596 ret = _dpu_plane_get_aspace(pdpu, pstate, &aspace);
597 if (ret) {
598 DPU_ERROR_PLANE(pdpu, "Failed to get aspace %d\n", ret);
599 return;
600 }
601
602 ret = dpu_format_populate_layout(aspace, fb, &pipe_cfg->layout);
603 if (ret == -EAGAIN)
604 DPU_DEBUG_PLANE(pdpu, "not updating same src addrs\n");
605 else if (ret)
606 DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);
607 else if (pdpu->pipe_hw->ops.setup_sourceaddress) {
608 trace_dpu_plane_set_scanout(pdpu->pipe_hw->idx,
609 &pipe_cfg->layout,
610 pstate->multirect_index);
611 pdpu->pipe_hw->ops.setup_sourceaddress(pdpu->pipe_hw, pipe_cfg,
612 pstate->multirect_index);
613 }
614}
615
616static void _dpu_plane_setup_scaler3(struct dpu_plane *pdpu,
617 struct dpu_plane_state *pstate,
618 uint32_t src_w, uint32_t src_h, uint32_t dst_w, uint32_t dst_h,
619 struct dpu_hw_scaler3_cfg *scale_cfg,
620 const struct dpu_format *fmt,
621 uint32_t chroma_subsmpl_h, uint32_t chroma_subsmpl_v)
622{
623 uint32_t i;
624
625 if (!pdpu || !pstate || !scale_cfg || !fmt || !chroma_subsmpl_h ||
626 !chroma_subsmpl_v) {
627 DPU_ERROR(
628 "pdpu %d pstate %d scale_cfg %d fmt %d smp_h %d smp_v %d\n",
629 !!pdpu, !!pstate, !!scale_cfg, !!fmt, chroma_subsmpl_h,
630 chroma_subsmpl_v);
631 return;
632 }
633
634 memset(scale_cfg, 0, sizeof(*scale_cfg));
635 memset(&pstate->pixel_ext, 0, sizeof(struct dpu_hw_pixel_ext));
636
637 scale_cfg->phase_step_x[DPU_SSPP_COMP_0] =
638 mult_frac((1 << PHASE_STEP_SHIFT), src_w, dst_w);
639 scale_cfg->phase_step_y[DPU_SSPP_COMP_0] =
640 mult_frac((1 << PHASE_STEP_SHIFT), src_h, dst_h);
641
642
643 scale_cfg->phase_step_y[DPU_SSPP_COMP_1_2] =
644 scale_cfg->phase_step_y[DPU_SSPP_COMP_0] / chroma_subsmpl_v;
645 scale_cfg->phase_step_x[DPU_SSPP_COMP_1_2] =
646 scale_cfg->phase_step_x[DPU_SSPP_COMP_0] / chroma_subsmpl_h;
647
648 scale_cfg->phase_step_x[DPU_SSPP_COMP_2] =
649 scale_cfg->phase_step_x[DPU_SSPP_COMP_1_2];
650 scale_cfg->phase_step_y[DPU_SSPP_COMP_2] =
651 scale_cfg->phase_step_y[DPU_SSPP_COMP_1_2];
652
653 scale_cfg->phase_step_x[DPU_SSPP_COMP_3] =
654 scale_cfg->phase_step_x[DPU_SSPP_COMP_0];
655 scale_cfg->phase_step_y[DPU_SSPP_COMP_3] =
656 scale_cfg->phase_step_y[DPU_SSPP_COMP_0];
657
658 for (i = 0; i < DPU_MAX_PLANES; i++) {
659 scale_cfg->src_width[i] = src_w;
660 scale_cfg->src_height[i] = src_h;
661 if (i == DPU_SSPP_COMP_1_2 || i == DPU_SSPP_COMP_2) {
662 scale_cfg->src_width[i] /= chroma_subsmpl_h;
663 scale_cfg->src_height[i] /= chroma_subsmpl_v;
664 }
665 scale_cfg->preload_x[i] = DPU_QSEED3_DEFAULT_PRELOAD_H;
666 scale_cfg->preload_y[i] = DPU_QSEED3_DEFAULT_PRELOAD_V;
667 pstate->pixel_ext.num_ext_pxls_top[i] =
668 scale_cfg->src_height[i];
669 pstate->pixel_ext.num_ext_pxls_left[i] =
670 scale_cfg->src_width[i];
671 }
672 if (!(DPU_FORMAT_IS_YUV(fmt)) && (src_h == dst_h)
673 && (src_w == dst_w))
674 return;
675
676 scale_cfg->dst_width = dst_w;
677 scale_cfg->dst_height = dst_h;
678 scale_cfg->y_rgb_filter_cfg = DPU_SCALE_BIL;
679 scale_cfg->uv_filter_cfg = DPU_SCALE_BIL;
680 scale_cfg->alpha_filter_cfg = DPU_SCALE_ALPHA_BIL;
681 scale_cfg->lut_flag = 0;
682 scale_cfg->blend_cfg = 1;
683 scale_cfg->enable = 1;
684}
685
686static inline void _dpu_plane_setup_csc(struct dpu_plane *pdpu)
687{
688 static const struct dpu_csc_cfg dpu_csc_YUV2RGB_601L = {
689 {
690 /* S15.16 format */
691 0x00012A00, 0x00000000, 0x00019880,
692 0x00012A00, 0xFFFF9B80, 0xFFFF3000,
693 0x00012A00, 0x00020480, 0x00000000,
694 },
695 /* signed bias */
696 { 0xfff0, 0xff80, 0xff80,},
697 { 0x0, 0x0, 0x0,},
698 /* unsigned clamp */
699 { 0x10, 0xeb, 0x10, 0xf0, 0x10, 0xf0,},
700 { 0x00, 0xff, 0x00, 0xff, 0x00, 0xff,},
701 };
702 static const struct dpu_csc_cfg dpu_csc10_YUV2RGB_601L = {
703 {
704 /* S15.16 format */
705 0x00012A00, 0x00000000, 0x00019880,
706 0x00012A00, 0xFFFF9B80, 0xFFFF3000,
707 0x00012A00, 0x00020480, 0x00000000,
708 },
709 /* signed bias */
710 { 0xffc0, 0xfe00, 0xfe00,},
711 { 0x0, 0x0, 0x0,},
712 /* unsigned clamp */
713 { 0x40, 0x3ac, 0x40, 0x3c0, 0x40, 0x3c0,},
714 { 0x00, 0x3ff, 0x00, 0x3ff, 0x00, 0x3ff,},
715 };
716
717 if (!pdpu) {
718 DPU_ERROR("invalid plane\n");
719 return;
720 }
721
722 if (BIT(DPU_SSPP_CSC_10BIT) & pdpu->features)
723 pdpu->csc_ptr = (struct dpu_csc_cfg *)&dpu_csc10_YUV2RGB_601L;
724 else
725 pdpu->csc_ptr = (struct dpu_csc_cfg *)&dpu_csc_YUV2RGB_601L;
726
727 DPU_DEBUG_PLANE(pdpu, "using 0x%X 0x%X 0x%X...\n",
728 pdpu->csc_ptr->csc_mv[0],
729 pdpu->csc_ptr->csc_mv[1],
730 pdpu->csc_ptr->csc_mv[2]);
731}
732
733static void _dpu_plane_setup_scaler(struct dpu_plane *pdpu,
734 struct dpu_plane_state *pstate,
735 const struct dpu_format *fmt, bool color_fill)
736{
737 struct dpu_hw_pixel_ext *pe;
738 uint32_t chroma_subsmpl_h, chroma_subsmpl_v;
739
740 if (!pdpu || !fmt || !pstate) {
741 DPU_ERROR("invalid arg(s), plane %d fmt %d state %d\n",
742 pdpu != 0, fmt != 0, pstate != 0);
743 return;
744 }
745
746 pe = &pstate->pixel_ext;
747
748 /* don't chroma subsample if decimating */
749 chroma_subsmpl_h =
750 drm_format_horz_chroma_subsampling(fmt->base.pixel_format);
751 chroma_subsmpl_v =
752 drm_format_vert_chroma_subsampling(fmt->base.pixel_format);
753
754 /* update scaler. calculate default config for QSEED3 */
755 _dpu_plane_setup_scaler3(pdpu, pstate,
756 drm_rect_width(&pdpu->pipe_cfg.src_rect),
757 drm_rect_height(&pdpu->pipe_cfg.src_rect),
758 drm_rect_width(&pdpu->pipe_cfg.dst_rect),
759 drm_rect_height(&pdpu->pipe_cfg.dst_rect),
760 &pstate->scaler3_cfg, fmt,
761 chroma_subsmpl_h, chroma_subsmpl_v);
762}
763
764/**
765 * _dpu_plane_color_fill - enables color fill on plane
766 * @pdpu: Pointer to DPU plane object
767 * @color: RGB fill color value, [23..16] Blue, [15..8] Green, [7..0] Red
768 * @alpha: 8-bit fill alpha value, 255 selects 100% alpha
769 * Returns: 0 on success
770 */
771static int _dpu_plane_color_fill(struct dpu_plane *pdpu,
772 uint32_t color, uint32_t alpha)
773{
774 const struct dpu_format *fmt;
775 const struct drm_plane *plane;
776 struct dpu_plane_state *pstate;
777
778 if (!pdpu || !pdpu->base.state) {
779 DPU_ERROR("invalid plane\n");
780 return -EINVAL;
781 }
782
783 if (!pdpu->pipe_hw) {
784 DPU_ERROR_PLANE(pdpu, "invalid plane h/w pointer\n");
785 return -EINVAL;
786 }
787
788 plane = &pdpu->base;
789 pstate = to_dpu_plane_state(plane->state);
790
791 DPU_DEBUG_PLANE(pdpu, "\n");
792
793 /*
794 * select fill format to match user property expectation,
795 * h/w only supports RGB variants
796 */
797 fmt = dpu_get_dpu_format(DRM_FORMAT_ABGR8888);
798
799 /* update sspp */
800 if (fmt && pdpu->pipe_hw->ops.setup_solidfill) {
801 pdpu->pipe_hw->ops.setup_solidfill(pdpu->pipe_hw,
802 (color & 0xFFFFFF) | ((alpha & 0xFF) << 24),
803 pstate->multirect_index);
804
805 /* override scaler/decimation if solid fill */
806 pdpu->pipe_cfg.src_rect.x1 = 0;
807 pdpu->pipe_cfg.src_rect.y1 = 0;
808 pdpu->pipe_cfg.src_rect.x2 =
809 drm_rect_width(&pdpu->pipe_cfg.dst_rect);
810 pdpu->pipe_cfg.src_rect.y2 =
811 drm_rect_height(&pdpu->pipe_cfg.dst_rect);
812 _dpu_plane_setup_scaler(pdpu, pstate, fmt, true);
813
814 if (pdpu->pipe_hw->ops.setup_format)
815 pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw,
816 fmt, DPU_SSPP_SOLID_FILL,
817 pstate->multirect_index);
818
819 if (pdpu->pipe_hw->ops.setup_rects)
820 pdpu->pipe_hw->ops.setup_rects(pdpu->pipe_hw,
821 &pdpu->pipe_cfg,
822 pstate->multirect_index);
823
824 if (pdpu->pipe_hw->ops.setup_pe)
825 pdpu->pipe_hw->ops.setup_pe(pdpu->pipe_hw,
826 &pstate->pixel_ext);
827
828 if (pdpu->pipe_hw->ops.setup_scaler &&
829 pstate->multirect_index != DPU_SSPP_RECT_1)
830 pdpu->pipe_hw->ops.setup_scaler(pdpu->pipe_hw,
831 &pdpu->pipe_cfg, &pstate->pixel_ext,
832 &pstate->scaler3_cfg);
833 }
834
835 return 0;
836}
837
838void dpu_plane_clear_multirect(const struct drm_plane_state *drm_state)
839{
840 struct dpu_plane_state *pstate;
841
842 if (!drm_state)
843 return;
844
845 pstate = to_dpu_plane_state(drm_state);
846
847 pstate->multirect_index = DPU_SSPP_RECT_SOLO;
848 pstate->multirect_mode = DPU_SSPP_MULTIRECT_NONE;
849}
850
851int dpu_plane_validate_multirect_v2(struct dpu_multirect_plane_states *plane)
852{
853 struct dpu_plane_state *pstate[R_MAX];
854 const struct drm_plane_state *drm_state[R_MAX];
855 struct drm_rect src[R_MAX], dst[R_MAX];
856 struct dpu_plane *dpu_plane[R_MAX];
857 const struct dpu_format *fmt[R_MAX];
858 int i, buffer_lines;
859 unsigned int max_tile_height = 1;
860 bool parallel_fetch_qualified = true;
861 bool has_tiled_rect = false;
862
863 for (i = 0; i < R_MAX; i++) {
864 const struct msm_format *msm_fmt;
865
866 drm_state[i] = i ? plane->r1 : plane->r0;
867 msm_fmt = msm_framebuffer_format(drm_state[i]->fb);
868 fmt[i] = to_dpu_format(msm_fmt);
869
870 if (DPU_FORMAT_IS_UBWC(fmt[i])) {
871 has_tiled_rect = true;
872 if (fmt[i]->tile_height > max_tile_height)
873 max_tile_height = fmt[i]->tile_height;
874 }
875 }
876
877 for (i = 0; i < R_MAX; i++) {
878 int width_threshold;
879
880 pstate[i] = to_dpu_plane_state(drm_state[i]);
881 dpu_plane[i] = to_dpu_plane(drm_state[i]->plane);
882
883 if (pstate[i] == NULL) {
884 DPU_ERROR("DPU plane state of plane id %d is NULL\n",
885 drm_state[i]->plane->base.id);
886 return -EINVAL;
887 }
888
889 src[i].x1 = drm_state[i]->src_x >> 16;
890 src[i].y1 = drm_state[i]->src_y >> 16;
891 src[i].x2 = src[i].x1 + (drm_state[i]->src_w >> 16);
892 src[i].y2 = src[i].y1 + (drm_state[i]->src_h >> 16);
893
894 dst[i] = drm_plane_state_dest(drm_state[i]);
895
896 if (drm_rect_calc_hscale(&src[i], &dst[i], 1, 1) != 1 ||
897 drm_rect_calc_vscale(&src[i], &dst[i], 1, 1) != 1) {
898 DPU_ERROR_PLANE(dpu_plane[i],
899 "scaling is not supported in multirect mode\n");
900 return -EINVAL;
901 }
902
903 if (DPU_FORMAT_IS_YUV(fmt[i])) {
904 DPU_ERROR_PLANE(dpu_plane[i],
905 "Unsupported format for multirect mode\n");
906 return -EINVAL;
907 }
908
909 /**
910 * SSPP PD_MEM is split half - one for each RECT.
911 * Tiled formats need 5 lines of buffering while fetching
912 * whereas linear formats need only 2 lines.
913 * So we cannot support more than half of the supported SSPP
914 * width for tiled formats.
915 */
916 width_threshold = dpu_plane[i]->pipe_sblk->common->maxlinewidth;
917 if (has_tiled_rect)
918 width_threshold /= 2;
919
920 if (parallel_fetch_qualified &&
921 drm_rect_width(&src[i]) > width_threshold)
922 parallel_fetch_qualified = false;
923
924 }
925
926 /* Validate RECT's and set the mode */
927
928 /* Prefer PARALLEL FETCH Mode over TIME_MX Mode */
929 if (parallel_fetch_qualified) {
930 pstate[R0]->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;
931 pstate[R1]->multirect_mode = DPU_SSPP_MULTIRECT_PARALLEL;
932
933 goto done;
934 }
935
936 /* TIME_MX Mode */
937 buffer_lines = 2 * max_tile_height;
938
939 if (dst[R1].y1 >= dst[R0].y2 + buffer_lines ||
940 dst[R0].y1 >= dst[R1].y2 + buffer_lines) {
941 pstate[R0]->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX;
942 pstate[R1]->multirect_mode = DPU_SSPP_MULTIRECT_TIME_MX;
943 } else {
944 DPU_ERROR(
945 "No multirect mode possible for the planes (%d - %d)\n",
946 drm_state[R0]->plane->base.id,
947 drm_state[R1]->plane->base.id);
948 return -EINVAL;
949 }
950
951done:
952 if (dpu_plane[R0]->is_virtual) {
953 pstate[R0]->multirect_index = DPU_SSPP_RECT_1;
954 pstate[R1]->multirect_index = DPU_SSPP_RECT_0;
955 } else {
956 pstate[R0]->multirect_index = DPU_SSPP_RECT_0;
957 pstate[R1]->multirect_index = DPU_SSPP_RECT_1;
958 };
959
960 DPU_DEBUG_PLANE(dpu_plane[R0], "R0: %d - %d\n",
961 pstate[R0]->multirect_mode, pstate[R0]->multirect_index);
962 DPU_DEBUG_PLANE(dpu_plane[R1], "R1: %d - %d\n",
963 pstate[R1]->multirect_mode, pstate[R1]->multirect_index);
964 return 0;
965}
966
967/**
968 * dpu_plane_get_ctl_flush - get control flush for the given plane
969 * @plane: Pointer to drm plane structure
970 * @ctl: Pointer to hardware control driver
971 * @flush_sspp: Pointer to sspp flush control word
972 */
973void dpu_plane_get_ctl_flush(struct drm_plane *plane, struct dpu_hw_ctl *ctl,
974 u32 *flush_sspp)
975{
976 struct dpu_plane_state *pstate;
977
978 if (!plane || !flush_sspp) {
979 DPU_ERROR("invalid parameters\n");
980 return;
981 }
982
983 pstate = to_dpu_plane_state(plane->state);
984
985 *flush_sspp = ctl->ops.get_bitmask_sspp(ctl, dpu_plane_pipe(plane));
986}
987
988static int dpu_plane_prepare_fb(struct drm_plane *plane,
989 struct drm_plane_state *new_state)
990{
991 struct drm_framebuffer *fb = new_state->fb;
992 struct dpu_plane *pdpu = to_dpu_plane(plane);
993 struct dpu_plane_state *pstate = to_dpu_plane_state(new_state);
994 struct dpu_hw_fmt_layout layout;
995 struct drm_gem_object *obj;
996 struct msm_gem_object *msm_obj;
997 struct dma_fence *fence;
998 struct msm_gem_address_space *aspace;
999 int ret;
1000
1001 if (!new_state->fb)
1002 return 0;
1003
1004 DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", fb->base.id);
1005
1006 ret = _dpu_plane_get_aspace(pdpu, pstate, &aspace);
1007 if (ret) {
1008 DPU_ERROR_PLANE(pdpu, "Failed to get aspace\n");
1009 return ret;
1010 }
1011
1012 /* cache aspace */
1013 pstate->aspace = aspace;
1014
1015 /*
1016 * TODO: Need to sort out the msm_framebuffer_prepare() call below so
1017 * we can use msm_atomic_prepare_fb() instead of doing the
1018 * implicit fence and fb prepare by hand here.
1019 */
1020 obj = msm_framebuffer_bo(new_state->fb, 0);
1021 msm_obj = to_msm_bo(obj);
1022 fence = reservation_object_get_excl_rcu(msm_obj->resv);
1023 if (fence)
1024 drm_atomic_set_fence_for_plane(new_state, fence);
1025
1026 if (pstate->aspace) {
1027 ret = msm_framebuffer_prepare(new_state->fb,
1028 pstate->aspace);
1029 if (ret) {
1030 DPU_ERROR("failed to prepare framebuffer\n");
1031 return ret;
1032 }
1033 }
1034
1035 /* validate framebuffer layout before commit */
1036 ret = dpu_format_populate_layout(pstate->aspace,
1037 new_state->fb, &layout);
1038 if (ret) {
1039 DPU_ERROR_PLANE(pdpu, "failed to get format layout, %d\n", ret);
1040 return ret;
1041 }
1042
1043 return 0;
1044}
1045
1046static void dpu_plane_cleanup_fb(struct drm_plane *plane,
1047 struct drm_plane_state *old_state)
1048{
1049 struct dpu_plane *pdpu = to_dpu_plane(plane);
1050 struct dpu_plane_state *old_pstate;
1051
1052 if (!old_state || !old_state->fb)
1053 return;
1054
1055 old_pstate = to_dpu_plane_state(old_state);
1056
1057 DPU_DEBUG_PLANE(pdpu, "FB[%u]\n", old_state->fb->base.id);
1058
1059 msm_framebuffer_cleanup(old_state->fb, old_pstate->aspace);
1060}
1061
1062static bool dpu_plane_validate_src(struct drm_rect *src,
1063 struct drm_rect *fb_rect,
1064 uint32_t min_src_size)
1065{
1066 /* Ensure fb size is supported */
1067 if (drm_rect_width(fb_rect) > MAX_IMG_WIDTH ||
1068 drm_rect_height(fb_rect) > MAX_IMG_HEIGHT)
1069 return false;
1070
1071 /* Ensure src rect is above the minimum size */
1072 if (drm_rect_width(src) < min_src_size ||
1073 drm_rect_height(src) < min_src_size)
1074 return false;
1075
1076 /* Ensure src is fully encapsulated in fb */
1077 return drm_rect_intersect(fb_rect, src) &&
1078 drm_rect_equals(fb_rect, src);
1079}
1080
1081static int dpu_plane_sspp_atomic_check(struct drm_plane *plane,
1082 struct drm_plane_state *state)
1083{
1084 int ret = 0;
1085 struct dpu_plane *pdpu;
1086 struct dpu_plane_state *pstate;
1087 const struct dpu_format *fmt;
1088 struct drm_rect src, dst, fb_rect = { 0 };
1089 uint32_t max_upscale = 1, max_downscale = 1;
1090 uint32_t min_src_size, max_linewidth;
1091 int hscale = 1, vscale = 1;
1092
1093 if (!plane || !state) {
1094 DPU_ERROR("invalid arg(s), plane %d state %d\n",
1095 plane != 0, state != 0);
1096 ret = -EINVAL;
1097 goto exit;
1098 }
1099
1100 pdpu = to_dpu_plane(plane);
1101 pstate = to_dpu_plane_state(state);
1102
1103 if (!pdpu->pipe_sblk) {
1104 DPU_ERROR_PLANE(pdpu, "invalid catalog\n");
1105 ret = -EINVAL;
1106 goto exit;
1107 }
1108
1109 src.x1 = state->src_x >> 16;
1110 src.y1 = state->src_y >> 16;
1111 src.x2 = src.x1 + (state->src_w >> 16);
1112 src.y2 = src.y1 + (state->src_h >> 16);
1113
1114 dst = drm_plane_state_dest(state);
1115
1116 fb_rect.x2 = state->fb->width;
1117 fb_rect.y2 = state->fb->height;
1118
1119 max_linewidth = pdpu->pipe_sblk->common->maxlinewidth;
1120
1121 if (pdpu->features & DPU_SSPP_SCALER) {
1122 max_downscale = pdpu->pipe_sblk->maxdwnscale;
1123 max_upscale = pdpu->pipe_sblk->maxupscale;
1124 }
1125 if (drm_rect_width(&src) < drm_rect_width(&dst))
1126 hscale = drm_rect_calc_hscale(&src, &dst, 1, max_upscale);
1127 else
1128 hscale = drm_rect_calc_hscale(&dst, &src, 1, max_downscale);
1129 if (drm_rect_height(&src) < drm_rect_height(&dst))
1130 vscale = drm_rect_calc_vscale(&src, &dst, 1, max_upscale);
1131 else
1132 vscale = drm_rect_calc_vscale(&dst, &src, 1, max_downscale);
1133
1134 DPU_DEBUG_PLANE(pdpu, "check %d -> %d\n",
1135 dpu_plane_enabled(plane->state), dpu_plane_enabled(state));
1136
1137 if (!dpu_plane_enabled(state))
1138 goto exit;
1139
1140 fmt = to_dpu_format(msm_framebuffer_format(state->fb));
1141
1142 min_src_size = DPU_FORMAT_IS_YUV(fmt) ? 2 : 1;
1143
1144 if (DPU_FORMAT_IS_YUV(fmt) &&
1145 (!(pdpu->features & DPU_SSPP_SCALER) ||
1146 !(pdpu->features & (BIT(DPU_SSPP_CSC)
1147 | BIT(DPU_SSPP_CSC_10BIT))))) {
1148 DPU_ERROR_PLANE(pdpu,
1149 "plane doesn't have scaler/csc for yuv\n");
1150 ret = -EINVAL;
1151
1152 /* check src bounds */
1153 } else if (!dpu_plane_validate_src(&src, &fb_rect, min_src_size)) {
1154 DPU_ERROR_PLANE(pdpu, "invalid source " DRM_RECT_FMT "\n",
1155 DRM_RECT_ARG(&src));
1156 ret = -E2BIG;
1157
1158 /* valid yuv image */
1159 } else if (DPU_FORMAT_IS_YUV(fmt) &&
1160 (src.x1 & 0x1 || src.y1 & 0x1 ||
1161 drm_rect_width(&src) & 0x1 ||
1162 drm_rect_height(&src) & 0x1)) {
1163 DPU_ERROR_PLANE(pdpu, "invalid yuv source " DRM_RECT_FMT "\n",
1164 DRM_RECT_ARG(&src));
1165 ret = -EINVAL;
1166
1167 /* min dst support */
1168 } else if (drm_rect_width(&dst) < 0x1 || drm_rect_height(&dst) < 0x1) {
1169 DPU_ERROR_PLANE(pdpu, "invalid dest rect " DRM_RECT_FMT "\n",
1170 DRM_RECT_ARG(&dst));
1171 ret = -EINVAL;
1172
1173 /* check decimated source width */
1174 } else if (drm_rect_width(&src) > max_linewidth) {
1175 DPU_ERROR_PLANE(pdpu, "invalid src " DRM_RECT_FMT " line:%u\n",
1176 DRM_RECT_ARG(&src), max_linewidth);
1177 ret = -E2BIG;
1178
1179 /* check scaler capability */
1180 } else if (hscale < 0 || vscale < 0) {
1181 DPU_ERROR_PLANE(pdpu, "invalid scaling requested src="
1182 DRM_RECT_FMT " dst=" DRM_RECT_FMT "\n",
1183 DRM_RECT_ARG(&src), DRM_RECT_ARG(&dst));
1184 ret = -E2BIG;
1185 }
1186
1187exit:
1188 return ret;
1189}
1190
1191static int dpu_plane_atomic_check(struct drm_plane *plane,
1192 struct drm_plane_state *state)
1193{
1194 if (!state->fb)
1195 return 0;
1196
1197 DPU_DEBUG_PLANE(to_dpu_plane(plane), "\n");
1198
1199 return dpu_plane_sspp_atomic_check(plane, state);
1200}
1201
1202void dpu_plane_flush(struct drm_plane *plane)
1203{
1204 struct dpu_plane *pdpu;
1205 struct dpu_plane_state *pstate;
1206
1207 if (!plane || !plane->state) {
1208 DPU_ERROR("invalid plane\n");
1209 return;
1210 }
1211
1212 pdpu = to_dpu_plane(plane);
1213 pstate = to_dpu_plane_state(plane->state);
1214
1215 /*
1216 * These updates have to be done immediately before the plane flush
1217 * timing, and may not be moved to the atomic_update/mode_set functions.
1218 */
1219 if (pdpu->is_error)
1220 /* force white frame with 100% alpha pipe output on error */
1221 _dpu_plane_color_fill(pdpu, 0xFFFFFF, 0xFF);
1222 else if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG)
1223 /* force 100% alpha */
1224 _dpu_plane_color_fill(pdpu, pdpu->color_fill, 0xFF);
1225 else if (pdpu->pipe_hw && pdpu->csc_ptr && pdpu->pipe_hw->ops.setup_csc)
1226 pdpu->pipe_hw->ops.setup_csc(pdpu->pipe_hw, pdpu->csc_ptr);
1227
1228 /* flag h/w flush complete */
1229 if (plane->state)
1230 pstate->pending = false;
1231}
1232
1233/**
1234 * dpu_plane_set_error: enable/disable error condition
1235 * @plane: pointer to drm_plane structure
1236 */
1237void dpu_plane_set_error(struct drm_plane *plane, bool error)
1238{
1239 struct dpu_plane *pdpu;
1240
1241 if (!plane)
1242 return;
1243
1244 pdpu = to_dpu_plane(plane);
1245 pdpu->is_error = error;
1246}
1247
1248static int dpu_plane_sspp_atomic_update(struct drm_plane *plane,
1249 struct drm_plane_state *old_state)
1250{
1251 uint32_t nplanes, src_flags;
1252 struct dpu_plane *pdpu;
1253 struct drm_plane_state *state;
1254 struct dpu_plane_state *pstate;
1255 struct dpu_plane_state *old_pstate;
1256 const struct dpu_format *fmt;
1257 struct drm_crtc *crtc;
1258 struct drm_framebuffer *fb;
96fc56a7 1259 int ret, min_scale;
25fdd593
JS
1260
1261 if (!plane) {
1262 DPU_ERROR("invalid plane\n");
1263 return -EINVAL;
1264 } else if (!plane->state) {
1265 DPU_ERROR("invalid plane state\n");
1266 return -EINVAL;
1267 } else if (!old_state) {
1268 DPU_ERROR("invalid old state\n");
1269 return -EINVAL;
1270 }
1271
1272 pdpu = to_dpu_plane(plane);
1273 state = plane->state;
1274
1275 pstate = to_dpu_plane_state(state);
1276
1277 old_pstate = to_dpu_plane_state(old_state);
1278
1279 crtc = state->crtc;
1280 fb = state->fb;
1281 if (!crtc || !fb) {
1282 DPU_ERROR_PLANE(pdpu, "invalid crtc %d or fb %d\n",
1283 crtc != 0, fb != 0);
1284 return -EINVAL;
1285 }
1286 fmt = to_dpu_format(msm_framebuffer_format(fb));
1287 nplanes = fmt->num_planes;
1288
1289 memset(&(pdpu->pipe_cfg), 0, sizeof(struct dpu_hw_pipe_cfg));
1290
1291 _dpu_plane_set_scanout(plane, pstate, &pdpu->pipe_cfg, fb);
1292
1293 pstate->pending = true;
1294
1295 pdpu->is_rt_pipe = (dpu_crtc_get_client_type(crtc) != NRT_CLIENT);
1296 _dpu_plane_set_qos_ctrl(plane, false, DPU_PLANE_QOS_PANIC_CTRL);
1297
96fc56a7
SP
1298 min_scale = FRAC_16_16(1, pdpu->pipe_sblk->maxdwnscale);
1299 ret = drm_atomic_helper_check_plane_state(state, crtc->state, min_scale,
1300 pdpu->pipe_sblk->maxupscale << 16,
1301 true, false);
1302 if (ret) {
1303 DPU_ERROR_PLANE(pdpu, "Check plane state failed (%d)\n", ret);
1304 return ret;
1305 }
25fdd593 1306
96fc56a7
SP
1307 DPU_DEBUG_PLANE(pdpu, "FB[%u] " DRM_RECT_FP_FMT "->crtc%u " DRM_RECT_FMT
1308 ", %4.4s ubwc %d\n", fb->base.id, DRM_RECT_FP_ARG(&state->src),
1309 crtc->base.id, DRM_RECT_ARG(&state->dst),
1310 (char *)&fmt->base.pixel_format, DPU_FORMAT_IS_UBWC(fmt));
25fdd593 1311
96fc56a7
SP
1312 pdpu->pipe_cfg.src_rect = state->src;
1313
1314 /* state->src is 16.16, src_rect is not */
1315 pdpu->pipe_cfg.src_rect.x1 >>= 16;
1316 pdpu->pipe_cfg.src_rect.x2 >>= 16;
1317 pdpu->pipe_cfg.src_rect.y1 >>= 16;
1318 pdpu->pipe_cfg.src_rect.y2 >>= 16;
25fdd593 1319
96fc56a7 1320 pdpu->pipe_cfg.dst_rect = state->dst;
25fdd593
JS
1321
1322 _dpu_plane_setup_scaler(pdpu, pstate, fmt, false);
1323
1324 /* override for color fill */
1325 if (pdpu->color_fill & DPU_PLANE_COLOR_FILL_FLAG) {
1326 /* skip remaining processing on color fill */
1327 return 0;
1328 }
1329
1330 if (pdpu->pipe_hw->ops.setup_rects) {
1331 pdpu->pipe_hw->ops.setup_rects(pdpu->pipe_hw,
1332 &pdpu->pipe_cfg,
1333 pstate->multirect_index);
1334 }
1335
1336 if (pdpu->pipe_hw->ops.setup_pe &&
1337 (pstate->multirect_index != DPU_SSPP_RECT_1))
1338 pdpu->pipe_hw->ops.setup_pe(pdpu->pipe_hw,
1339 &pstate->pixel_ext);
1340
1341 /**
1342 * when programmed in multirect mode, scalar block will be
1343 * bypassed. Still we need to update alpha and bitwidth
1344 * ONLY for RECT0
1345 */
1346 if (pdpu->pipe_hw->ops.setup_scaler &&
1347 pstate->multirect_index != DPU_SSPP_RECT_1)
1348 pdpu->pipe_hw->ops.setup_scaler(pdpu->pipe_hw,
1349 &pdpu->pipe_cfg, &pstate->pixel_ext,
1350 &pstate->scaler3_cfg);
1351
1352 if (pdpu->pipe_hw->ops.setup_multirect)
1353 pdpu->pipe_hw->ops.setup_multirect(
1354 pdpu->pipe_hw,
1355 pstate->multirect_index,
1356 pstate->multirect_mode);
1357
1358 if (pdpu->pipe_hw->ops.setup_format) {
1359 src_flags = 0x0;
1360
1361 /* update format */
1362 pdpu->pipe_hw->ops.setup_format(pdpu->pipe_hw, fmt, src_flags,
1363 pstate->multirect_index);
1364
1365 if (pdpu->pipe_hw->ops.setup_cdp) {
1366 struct dpu_hw_pipe_cdp_cfg *cdp_cfg = &pstate->cdp_cfg;
1367
1368 memset(cdp_cfg, 0, sizeof(struct dpu_hw_pipe_cdp_cfg));
1369
1370 cdp_cfg->enable = pdpu->catalog->perf.cdp_cfg
1371 [DPU_PERF_CDP_USAGE_RT].rd_enable;
1372 cdp_cfg->ubwc_meta_enable =
1373 DPU_FORMAT_IS_UBWC(fmt);
1374 cdp_cfg->tile_amortize_enable =
1375 DPU_FORMAT_IS_UBWC(fmt) ||
1376 DPU_FORMAT_IS_TILE(fmt);
1377 cdp_cfg->preload_ahead = DPU_SSPP_CDP_PRELOAD_AHEAD_64;
1378
1379 pdpu->pipe_hw->ops.setup_cdp(pdpu->pipe_hw, cdp_cfg);
1380 }
1381
1382 /* update csc */
1383 if (DPU_FORMAT_IS_YUV(fmt))
1384 _dpu_plane_setup_csc(pdpu);
1385 else
1386 pdpu->csc_ptr = 0;
1387 }
1388
1389 _dpu_plane_set_qos_lut(plane, fb);
1390 _dpu_plane_set_danger_lut(plane, fb);
1391
1392 if (plane->type != DRM_PLANE_TYPE_CURSOR) {
1393 _dpu_plane_set_qos_ctrl(plane, true, DPU_PLANE_QOS_PANIC_CTRL);
1394 _dpu_plane_set_ot_limit(plane, crtc);
1395 }
1396
1397 _dpu_plane_set_qos_remap(plane);
1398 return 0;
1399}
1400
1401static void _dpu_plane_atomic_disable(struct drm_plane *plane,
1402 struct drm_plane_state *old_state)
1403{
1404 struct dpu_plane *pdpu;
1405 struct drm_plane_state *state;
1406 struct dpu_plane_state *pstate;
1407
1408 if (!plane) {
1409 DPU_ERROR("invalid plane\n");
1410 return;
1411 } else if (!plane->state) {
1412 DPU_ERROR("invalid plane state\n");
1413 return;
1414 } else if (!old_state) {
1415 DPU_ERROR("invalid old state\n");
1416 return;
1417 }
1418
1419 pdpu = to_dpu_plane(plane);
1420 state = plane->state;
1421 pstate = to_dpu_plane_state(state);
1422
1423 trace_dpu_plane_disable(DRMID(plane), is_dpu_plane_virtual(plane),
1424 pstate->multirect_mode);
1425
1426 pstate->pending = true;
1427
1428 if (is_dpu_plane_virtual(plane) &&
1429 pdpu->pipe_hw && pdpu->pipe_hw->ops.setup_multirect)
1430 pdpu->pipe_hw->ops.setup_multirect(pdpu->pipe_hw,
1431 DPU_SSPP_RECT_SOLO, DPU_SSPP_MULTIRECT_NONE);
1432}
1433
1434static void dpu_plane_atomic_update(struct drm_plane *plane,
1435 struct drm_plane_state *old_state)
1436{
1437 struct dpu_plane *pdpu;
1438 struct drm_plane_state *state;
1439
1440 if (!plane) {
1441 DPU_ERROR("invalid plane\n");
1442 return;
1443 } else if (!plane->state) {
1444 DPU_ERROR("invalid plane state\n");
1445 return;
1446 }
1447
1448 pdpu = to_dpu_plane(plane);
1449 pdpu->is_error = false;
1450 state = plane->state;
1451
1452 DPU_DEBUG_PLANE(pdpu, "\n");
1453
1454 if (!dpu_plane_sspp_enabled(state)) {
1455 _dpu_plane_atomic_disable(plane, old_state);
1456 } else {
1457 int ret;
1458
1459 ret = dpu_plane_sspp_atomic_update(plane, old_state);
1460 /* atomic_check should have ensured that this doesn't fail */
1461 WARN_ON(ret < 0);
1462 }
1463}
1464
1465void dpu_plane_restore(struct drm_plane *plane)
1466{
1467 struct dpu_plane *pdpu;
1468
1469 if (!plane || !plane->state) {
1470 DPU_ERROR("invalid plane\n");
1471 return;
1472 }
1473
1474 pdpu = to_dpu_plane(plane);
1475
1476 DPU_DEBUG_PLANE(pdpu, "\n");
1477
1478 /* last plane state is same as current state */
1479 dpu_plane_atomic_update(plane, plane->state);
1480}
1481
1482static void dpu_plane_destroy(struct drm_plane *plane)
1483{
1484 struct dpu_plane *pdpu = plane ? to_dpu_plane(plane) : NULL;
1485
1486 DPU_DEBUG_PLANE(pdpu, "\n");
1487
1488 if (pdpu) {
1489 _dpu_plane_set_qos_ctrl(plane, false, DPU_PLANE_QOS_PANIC_CTRL);
1490
1491 mutex_destroy(&pdpu->lock);
1492
1493 drm_plane_helper_disable(plane, NULL);
1494
1495 /* this will destroy the states as well */
1496 drm_plane_cleanup(plane);
1497
1498 if (pdpu->pipe_hw)
1499 dpu_hw_sspp_destroy(pdpu->pipe_hw);
1500
1501 kfree(pdpu);
1502 }
1503}
1504
1505static void dpu_plane_destroy_state(struct drm_plane *plane,
1506 struct drm_plane_state *state)
1507{
1508 struct dpu_plane_state *pstate;
1509
1510 if (!plane || !state) {
1511 DPU_ERROR("invalid arg(s), plane %d state %d\n",
1512 plane != 0, state != 0);
1513 return;
1514 }
1515
1516 pstate = to_dpu_plane_state(state);
1517
1518 /* remove ref count for frame buffers */
1519 if (state->fb)
1520 drm_framebuffer_put(state->fb);
1521
1522 kfree(pstate);
1523}
1524
1525static struct drm_plane_state *
1526dpu_plane_duplicate_state(struct drm_plane *plane)
1527{
1528 struct dpu_plane *pdpu;
1529 struct dpu_plane_state *pstate;
1530 struct dpu_plane_state *old_state;
1531
1532 if (!plane) {
1533 DPU_ERROR("invalid plane\n");
1534 return NULL;
1535 } else if (!plane->state) {
1536 DPU_ERROR("invalid plane state\n");
1537 return NULL;
1538 }
1539
1540 old_state = to_dpu_plane_state(plane->state);
1541 pdpu = to_dpu_plane(plane);
1542 pstate = kmemdup(old_state, sizeof(*old_state), GFP_KERNEL);
1543 if (!pstate) {
1544 DPU_ERROR_PLANE(pdpu, "failed to allocate state\n");
1545 return NULL;
1546 }
1547
1548 DPU_DEBUG_PLANE(pdpu, "\n");
1549
1550 pstate->pending = false;
1551
1552 __drm_atomic_helper_plane_duplicate_state(plane, &pstate->base);
1553
1554 return &pstate->base;
1555}
1556
1557static void dpu_plane_reset(struct drm_plane *plane)
1558{
1559 struct dpu_plane *pdpu;
1560 struct dpu_plane_state *pstate;
1561
1562 if (!plane) {
1563 DPU_ERROR("invalid plane\n");
1564 return;
1565 }
1566
1567 pdpu = to_dpu_plane(plane);
1568 DPU_DEBUG_PLANE(pdpu, "\n");
1569
1570 /* remove previous state, if present */
1571 if (plane->state) {
1572 dpu_plane_destroy_state(plane, plane->state);
1573 plane->state = 0;
1574 }
1575
1576 pstate = kzalloc(sizeof(*pstate), GFP_KERNEL);
1577 if (!pstate) {
1578 DPU_ERROR_PLANE(pdpu, "failed to allocate state\n");
1579 return;
1580 }
1581
1582 pstate->base.plane = plane;
1583
1584 plane->state = &pstate->base;
1585}
1586
1587#ifdef CONFIG_DEBUG_FS
1588static ssize_t _dpu_plane_danger_read(struct file *file,
1589 char __user *buff, size_t count, loff_t *ppos)
1590{
1591 struct dpu_kms *kms = file->private_data;
1592 struct dpu_mdss_cfg *cfg = kms->catalog;
1593 int len = 0;
1594 char buf[40] = {'\0'};
1595
1596 if (!cfg)
1597 return -ENODEV;
1598
1599 if (*ppos)
1600 return 0; /* the end */
1601
1602 len = snprintf(buf, sizeof(buf), "%d\n", !kms->has_danger_ctrl);
1603 if (len < 0 || len >= sizeof(buf))
1604 return 0;
1605
1606 if ((count < sizeof(buf)) || copy_to_user(buff, buf, len))
1607 return -EFAULT;
1608
1609 *ppos += len; /* increase offset */
1610
1611 return len;
1612}
1613
1614static void _dpu_plane_set_danger_state(struct dpu_kms *kms, bool enable)
1615{
1616 struct drm_plane *plane;
1617
1618 drm_for_each_plane(plane, kms->dev) {
1619 if (plane->fb && plane->state) {
1620 dpu_plane_danger_signal_ctrl(plane, enable);
1621 DPU_DEBUG("plane:%d img:%dx%d ",
1622 plane->base.id, plane->fb->width,
1623 plane->fb->height);
1624 DPU_DEBUG("src[%d,%d,%d,%d] dst[%d,%d,%d,%d]\n",
1625 plane->state->src_x >> 16,
1626 plane->state->src_y >> 16,
1627 plane->state->src_w >> 16,
1628 plane->state->src_h >> 16,
1629 plane->state->crtc_x, plane->state->crtc_y,
1630 plane->state->crtc_w, plane->state->crtc_h);
1631 } else {
1632 DPU_DEBUG("Inactive plane:%d\n", plane->base.id);
1633 }
1634 }
1635}
1636
1637static ssize_t _dpu_plane_danger_write(struct file *file,
1638 const char __user *user_buf, size_t count, loff_t *ppos)
1639{
1640 struct dpu_kms *kms = file->private_data;
1641 struct dpu_mdss_cfg *cfg = kms->catalog;
1642 int disable_panic;
1643 char buf[10];
1644
1645 if (!cfg)
1646 return -EFAULT;
1647
1648 if (count >= sizeof(buf))
1649 return -EFAULT;
1650
1651 if (copy_from_user(buf, user_buf, count))
1652 return -EFAULT;
1653
1654 buf[count] = 0; /* end of string */
1655
1656 if (kstrtoint(buf, 0, &disable_panic))
1657 return -EFAULT;
1658
1659 if (disable_panic) {
1660 /* Disable panic signal for all active pipes */
1661 DPU_DEBUG("Disabling danger:\n");
1662 _dpu_plane_set_danger_state(kms, false);
1663 kms->has_danger_ctrl = false;
1664 } else {
1665 /* Enable panic signal for all active pipes */
1666 DPU_DEBUG("Enabling danger:\n");
1667 kms->has_danger_ctrl = true;
1668 _dpu_plane_set_danger_state(kms, true);
1669 }
1670
1671 return count;
1672}
1673
1674static const struct file_operations dpu_plane_danger_enable = {
1675 .open = simple_open,
1676 .read = _dpu_plane_danger_read,
1677 .write = _dpu_plane_danger_write,
1678};
1679
1680static int _dpu_plane_init_debugfs(struct drm_plane *plane)
1681{
1682 struct dpu_plane *pdpu;
1683 struct dpu_kms *kms;
1684 struct msm_drm_private *priv;
1685 const struct dpu_sspp_sub_blks *sblk = 0;
1686 const struct dpu_sspp_cfg *cfg = 0;
1687
1688 if (!plane || !plane->dev) {
1689 DPU_ERROR("invalid arguments\n");
1690 return -EINVAL;
1691 }
1692
1693 priv = plane->dev->dev_private;
1694 if (!priv || !priv->kms) {
1695 DPU_ERROR("invalid KMS reference\n");
1696 return -EINVAL;
1697 }
1698
1699 kms = to_dpu_kms(priv->kms);
1700 pdpu = to_dpu_plane(plane);
1701
1702 if (pdpu && pdpu->pipe_hw)
1703 cfg = pdpu->pipe_hw->cap;
1704 if (cfg)
1705 sblk = cfg->sblk;
1706
1707 if (!sblk)
1708 return 0;
1709
1710 /* create overall sub-directory for the pipe */
1711 pdpu->debugfs_root =
1712 debugfs_create_dir(pdpu->pipe_name,
1713 plane->dev->primary->debugfs_root);
1714
1715 if (!pdpu->debugfs_root)
1716 return -ENOMEM;
1717
1718 /* don't error check these */
1719 debugfs_create_x32("features", 0600,
1720 pdpu->debugfs_root, &pdpu->features);
1721
1722 /* add register dump support */
1723 dpu_debugfs_setup_regset32(&pdpu->debugfs_src,
1724 sblk->src_blk.base + cfg->base,
1725 sblk->src_blk.len,
1726 kms);
1727 dpu_debugfs_create_regset32("src_blk", 0400,
1728 pdpu->debugfs_root, &pdpu->debugfs_src);
1729
1730 if (cfg->features & BIT(DPU_SSPP_SCALER_QSEED3) ||
1731 cfg->features & BIT(DPU_SSPP_SCALER_QSEED2)) {
1732 dpu_debugfs_setup_regset32(&pdpu->debugfs_scaler,
1733 sblk->scaler_blk.base + cfg->base,
1734 sblk->scaler_blk.len,
1735 kms);
1736 dpu_debugfs_create_regset32("scaler_blk", 0400,
1737 pdpu->debugfs_root,
1738 &pdpu->debugfs_scaler);
1739 debugfs_create_bool("default_scaling",
1740 0600,
1741 pdpu->debugfs_root,
1742 &pdpu->debugfs_default_scale);
1743 }
1744
1745 if (cfg->features & BIT(DPU_SSPP_CSC) ||
1746 cfg->features & BIT(DPU_SSPP_CSC_10BIT)) {
1747 dpu_debugfs_setup_regset32(&pdpu->debugfs_csc,
1748 sblk->csc_blk.base + cfg->base,
1749 sblk->csc_blk.len,
1750 kms);
1751 dpu_debugfs_create_regset32("csc_blk", 0400,
1752 pdpu->debugfs_root, &pdpu->debugfs_csc);
1753 }
1754
1755 debugfs_create_u32("xin_id",
1756 0400,
1757 pdpu->debugfs_root,
1758 (u32 *) &cfg->xin_id);
1759 debugfs_create_u32("clk_ctrl",
1760 0400,
1761 pdpu->debugfs_root,
1762 (u32 *) &cfg->clk_ctrl);
1763 debugfs_create_x32("creq_vblank",
1764 0600,
1765 pdpu->debugfs_root,
1766 (u32 *) &sblk->creq_vblank);
1767 debugfs_create_x32("danger_vblank",
1768 0600,
1769 pdpu->debugfs_root,
1770 (u32 *) &sblk->danger_vblank);
1771
1772 debugfs_create_file("disable_danger",
1773 0600,
1774 pdpu->debugfs_root,
1775 kms, &dpu_plane_danger_enable);
1776
1777 return 0;
1778}
1779
1780static void _dpu_plane_destroy_debugfs(struct drm_plane *plane)
1781{
1782 struct dpu_plane *pdpu;
1783
1784 if (!plane)
1785 return;
1786 pdpu = to_dpu_plane(plane);
1787
1788 debugfs_remove_recursive(pdpu->debugfs_root);
1789}
1790#else
1791static int _dpu_plane_init_debugfs(struct drm_plane *plane)
1792{
1793 return 0;
1794}
1795static void _dpu_plane_destroy_debugfs(struct drm_plane *plane)
1796{
1797}
1798#endif
1799
1800static int dpu_plane_late_register(struct drm_plane *plane)
1801{
1802 return _dpu_plane_init_debugfs(plane);
1803}
1804
1805static void dpu_plane_early_unregister(struct drm_plane *plane)
1806{
1807 _dpu_plane_destroy_debugfs(plane);
1808}
1809
1810static const struct drm_plane_funcs dpu_plane_funcs = {
1811 .update_plane = drm_atomic_helper_update_plane,
1812 .disable_plane = drm_atomic_helper_disable_plane,
1813 .destroy = dpu_plane_destroy,
1814 .reset = dpu_plane_reset,
1815 .atomic_duplicate_state = dpu_plane_duplicate_state,
1816 .atomic_destroy_state = dpu_plane_destroy_state,
1817 .late_register = dpu_plane_late_register,
1818 .early_unregister = dpu_plane_early_unregister,
1819};
1820
1821static const struct drm_plane_helper_funcs dpu_plane_helper_funcs = {
1822 .prepare_fb = dpu_plane_prepare_fb,
1823 .cleanup_fb = dpu_plane_cleanup_fb,
1824 .atomic_check = dpu_plane_atomic_check,
1825 .atomic_update = dpu_plane_atomic_update,
1826};
1827
1828enum dpu_sspp dpu_plane_pipe(struct drm_plane *plane)
1829{
1830 return plane ? to_dpu_plane(plane)->pipe : SSPP_NONE;
1831}
1832
1833bool is_dpu_plane_virtual(struct drm_plane *plane)
1834{
1835 return plane ? to_dpu_plane(plane)->is_virtual : false;
1836}
1837
1838/* initialize plane */
1839struct drm_plane *dpu_plane_init(struct drm_device *dev,
07ca1fc0 1840 uint32_t pipe, enum drm_plane_type type,
25fdd593
JS
1841 unsigned long possible_crtcs, u32 master_plane_id)
1842{
1843 struct drm_plane *plane = NULL, *master_plane = NULL;
1844 const struct dpu_format_extended *format_list;
1845 struct dpu_plane *pdpu;
1846 struct msm_drm_private *priv;
1847 struct dpu_kms *kms;
25fdd593
JS
1848 int zpos_max = DPU_ZPOS_MAX;
1849 int ret = -EINVAL;
1850
1851 if (!dev) {
1852 DPU_ERROR("[%u]device is NULL\n", pipe);
1853 goto exit;
1854 }
1855
1856 priv = dev->dev_private;
1857 if (!priv) {
1858 DPU_ERROR("[%u]private data is NULL\n", pipe);
1859 goto exit;
1860 }
1861
1862 if (!priv->kms) {
1863 DPU_ERROR("[%u]invalid KMS reference\n", pipe);
1864 goto exit;
1865 }
1866 kms = to_dpu_kms(priv->kms);
1867
1868 if (!kms->catalog) {
1869 DPU_ERROR("[%u]invalid catalog reference\n", pipe);
1870 goto exit;
1871 }
1872
1873 /* create and zero local structure */
1874 pdpu = kzalloc(sizeof(*pdpu), GFP_KERNEL);
1875 if (!pdpu) {
1876 DPU_ERROR("[%u]failed to allocate local plane struct\n", pipe);
1877 ret = -ENOMEM;
1878 goto exit;
1879 }
1880
1881 /* cache local stuff for later */
1882 plane = &pdpu->base;
1883 pdpu->pipe = pipe;
1884 pdpu->is_virtual = (master_plane_id != 0);
1885 INIT_LIST_HEAD(&pdpu->mplane_list);
1886 master_plane = drm_plane_find(dev, NULL, master_plane_id);
1887 if (master_plane) {
1888 struct dpu_plane *mpdpu = to_dpu_plane(master_plane);
1889
1890 list_add_tail(&pdpu->mplane_list, &mpdpu->mplane_list);
1891 }
1892
1893 /* initialize underlying h/w driver */
1894 pdpu->pipe_hw = dpu_hw_sspp_init(pipe, kms->mmio, kms->catalog,
1895 master_plane_id != 0);
1896 if (IS_ERR(pdpu->pipe_hw)) {
1897 DPU_ERROR("[%u]SSPP init failed\n", pipe);
1898 ret = PTR_ERR(pdpu->pipe_hw);
1899 goto clean_plane;
1900 } else if (!pdpu->pipe_hw->cap || !pdpu->pipe_hw->cap->sblk) {
1901 DPU_ERROR("[%u]SSPP init returned invalid cfg\n", pipe);
1902 goto clean_sspp;
1903 }
1904
1905 /* cache features mask for later */
1906 pdpu->features = pdpu->pipe_hw->cap->features;
1907 pdpu->pipe_sblk = pdpu->pipe_hw->cap->sblk;
1908 if (!pdpu->pipe_sblk) {
1909 DPU_ERROR("[%u]invalid sblk\n", pipe);
1910 goto clean_sspp;
1911 }
1912
1913 if (!master_plane_id)
1914 format_list = pdpu->pipe_sblk->format_list;
1915 else
1916 format_list = pdpu->pipe_sblk->virt_format_list;
1917
1918 pdpu->nformats = dpu_populate_formats(format_list,
1919 pdpu->formats,
1920 0,
1921 ARRAY_SIZE(pdpu->formats));
1922
1923 if (!pdpu->nformats) {
1924 DPU_ERROR("[%u]no valid formats for plane\n", pipe);
1925 goto clean_sspp;
1926 }
1927
25fdd593
JS
1928 ret = drm_universal_plane_init(dev, plane, 0xff, &dpu_plane_funcs,
1929 pdpu->formats, pdpu->nformats,
1930 NULL, type, NULL);
1931 if (ret)
1932 goto clean_sspp;
1933
1934 pdpu->catalog = kms->catalog;
1935
1936 if (kms->catalog->mixer_count &&
1937 kms->catalog->mixer[0].sblk->maxblendstages) {
1938 zpos_max = kms->catalog->mixer[0].sblk->maxblendstages - 1;
1939 if (zpos_max > DPU_STAGE_MAX - DPU_STAGE_0 - 1)
1940 zpos_max = DPU_STAGE_MAX - DPU_STAGE_0 - 1;
1941 }
1942
1943 ret = drm_plane_create_zpos_property(plane, 0, 0, zpos_max);
1944 if (ret)
1945 DPU_ERROR("failed to install zpos property, rc = %d\n", ret);
1946
1947 /* success! finalize initialization */
1948 drm_plane_helper_add(plane, &dpu_plane_helper_funcs);
1949
1950 /* save user friendly pipe name for later */
1951 snprintf(pdpu->pipe_name, DPU_NAME_SIZE, "plane%u", plane->base.id);
1952
1953 mutex_init(&pdpu->lock);
1954
1955 DPU_DEBUG("%s created for pipe:%u id:%u virtual:%u\n", pdpu->pipe_name,
1956 pipe, plane->base.id, master_plane_id);
1957 return plane;
1958
1959clean_sspp:
1960 if (pdpu && pdpu->pipe_hw)
1961 dpu_hw_sspp_destroy(pdpu->pipe_hw);
1962clean_plane:
1963 kfree(pdpu);
1964exit:
1965 return ERR_PTR(ret);
1966}