2 * Copyright (C) 2015 Broadcom
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
10 * DOC: VC4 plane module
12 * Each DRM plane is a layer of pixels being scanned out by the HVS.
14 * At atomic modeset check time, we compute the HVS display element
15 * state that would be necessary for displaying the plane (giving us a
16 * chance to figure out if a plane configuration is invalid), then at
17 * atomic flush time the CRTC will ask us to write our element state
18 * into the region of the HVS that it has allocated for us.
21 #include <drm/drm_atomic.h>
22 #include <drm/drm_atomic_helper.h>
23 #include <drm/drm_fb_cma_helper.h>
24 #include <drm/drm_plane_helper.h>
25 #include <drm/drm_atomic_uapi.h>
27 #include "uapi/drm/vc4_drm.h"
31 static const struct hvs_format
{
32 u32 drm
; /* DRM_FORMAT_* */
33 u32 hvs
; /* HVS_FORMAT_* */
37 .drm
= DRM_FORMAT_XRGB8888
, .hvs
= HVS_PIXEL_FORMAT_RGBA8888
,
38 .pixel_order
= HVS_PIXEL_ORDER_ABGR
,
41 .drm
= DRM_FORMAT_ARGB8888
, .hvs
= HVS_PIXEL_FORMAT_RGBA8888
,
42 .pixel_order
= HVS_PIXEL_ORDER_ABGR
,
45 .drm
= DRM_FORMAT_ABGR8888
, .hvs
= HVS_PIXEL_FORMAT_RGBA8888
,
46 .pixel_order
= HVS_PIXEL_ORDER_ARGB
,
49 .drm
= DRM_FORMAT_XBGR8888
, .hvs
= HVS_PIXEL_FORMAT_RGBA8888
,
50 .pixel_order
= HVS_PIXEL_ORDER_ARGB
,
53 .drm
= DRM_FORMAT_RGB565
, .hvs
= HVS_PIXEL_FORMAT_RGB565
,
54 .pixel_order
= HVS_PIXEL_ORDER_XRGB
,
57 .drm
= DRM_FORMAT_BGR565
, .hvs
= HVS_PIXEL_FORMAT_RGB565
,
58 .pixel_order
= HVS_PIXEL_ORDER_XBGR
,
61 .drm
= DRM_FORMAT_ARGB1555
, .hvs
= HVS_PIXEL_FORMAT_RGBA5551
,
62 .pixel_order
= HVS_PIXEL_ORDER_ABGR
,
65 .drm
= DRM_FORMAT_XRGB1555
, .hvs
= HVS_PIXEL_FORMAT_RGBA5551
,
66 .pixel_order
= HVS_PIXEL_ORDER_ABGR
,
69 .drm
= DRM_FORMAT_RGB888
, .hvs
= HVS_PIXEL_FORMAT_RGB888
,
70 .pixel_order
= HVS_PIXEL_ORDER_XRGB
,
73 .drm
= DRM_FORMAT_BGR888
, .hvs
= HVS_PIXEL_FORMAT_RGB888
,
74 .pixel_order
= HVS_PIXEL_ORDER_XBGR
,
77 .drm
= DRM_FORMAT_YUV422
,
78 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE
,
79 .pixel_order
= HVS_PIXEL_ORDER_XYCBCR
,
82 .drm
= DRM_FORMAT_YVU422
,
83 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE
,
84 .pixel_order
= HVS_PIXEL_ORDER_XYCRCB
,
87 .drm
= DRM_FORMAT_YUV420
,
88 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE
,
89 .pixel_order
= HVS_PIXEL_ORDER_XYCBCR
,
92 .drm
= DRM_FORMAT_YVU420
,
93 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE
,
94 .pixel_order
= HVS_PIXEL_ORDER_XYCRCB
,
97 .drm
= DRM_FORMAT_NV12
,
98 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE
,
99 .pixel_order
= HVS_PIXEL_ORDER_XYCBCR
,
102 .drm
= DRM_FORMAT_NV21
,
103 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE
,
104 .pixel_order
= HVS_PIXEL_ORDER_XYCRCB
,
107 .drm
= DRM_FORMAT_NV16
,
108 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE
,
109 .pixel_order
= HVS_PIXEL_ORDER_XYCBCR
,
112 .drm
= DRM_FORMAT_NV61
,
113 .hvs
= HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE
,
114 .pixel_order
= HVS_PIXEL_ORDER_XYCRCB
,
118 static const struct hvs_format
*vc4_get_hvs_format(u32 drm_format
)
122 for (i
= 0; i
< ARRAY_SIZE(hvs_formats
); i
++) {
123 if (hvs_formats
[i
].drm
== drm_format
)
124 return &hvs_formats
[i
];
130 static enum vc4_scaling_mode
vc4_get_scaling_mode(u32 src
, u32 dst
)
133 return VC4_SCALING_NONE
;
134 if (3 * dst
>= 2 * src
)
135 return VC4_SCALING_PPF
;
137 return VC4_SCALING_TPZ
;
140 static bool plane_enabled(struct drm_plane_state
*state
)
142 return state
->fb
&& state
->crtc
;
145 static struct drm_plane_state
*vc4_plane_duplicate_state(struct drm_plane
*plane
)
147 struct vc4_plane_state
*vc4_state
;
149 if (WARN_ON(!plane
->state
))
152 vc4_state
= kmemdup(plane
->state
, sizeof(*vc4_state
), GFP_KERNEL
);
156 memset(&vc4_state
->lbm
, 0, sizeof(vc4_state
->lbm
));
157 vc4_state
->dlist_initialized
= 0;
159 __drm_atomic_helper_plane_duplicate_state(plane
, &vc4_state
->base
);
161 if (vc4_state
->dlist
) {
162 vc4_state
->dlist
= kmemdup(vc4_state
->dlist
,
163 vc4_state
->dlist_count
* 4,
165 if (!vc4_state
->dlist
) {
169 vc4_state
->dlist_size
= vc4_state
->dlist_count
;
172 return &vc4_state
->base
;
175 static void vc4_plane_destroy_state(struct drm_plane
*plane
,
176 struct drm_plane_state
*state
)
178 struct vc4_dev
*vc4
= to_vc4_dev(plane
->dev
);
179 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
181 if (vc4_state
->lbm
.allocated
) {
182 unsigned long irqflags
;
184 spin_lock_irqsave(&vc4
->hvs
->mm_lock
, irqflags
);
185 drm_mm_remove_node(&vc4_state
->lbm
);
186 spin_unlock_irqrestore(&vc4
->hvs
->mm_lock
, irqflags
);
189 kfree(vc4_state
->dlist
);
190 __drm_atomic_helper_plane_destroy_state(&vc4_state
->base
);
194 /* Called during init to allocate the plane's atomic state. */
195 static void vc4_plane_reset(struct drm_plane
*plane
)
197 struct vc4_plane_state
*vc4_state
;
199 WARN_ON(plane
->state
);
201 vc4_state
= kzalloc(sizeof(*vc4_state
), GFP_KERNEL
);
205 __drm_atomic_helper_plane_reset(plane
, &vc4_state
->base
);
208 static void vc4_dlist_write(struct vc4_plane_state
*vc4_state
, u32 val
)
210 if (vc4_state
->dlist_count
== vc4_state
->dlist_size
) {
211 u32 new_size
= max(4u, vc4_state
->dlist_count
* 2);
212 u32
*new_dlist
= kmalloc_array(new_size
, 4, GFP_KERNEL
);
216 memcpy(new_dlist
, vc4_state
->dlist
, vc4_state
->dlist_count
* 4);
218 kfree(vc4_state
->dlist
);
219 vc4_state
->dlist
= new_dlist
;
220 vc4_state
->dlist_size
= new_size
;
223 vc4_state
->dlist
[vc4_state
->dlist_count
++] = val
;
226 /* Returns the scl0/scl1 field based on whether the dimensions need to
227 * be up/down/non-scaled.
229 * This is a replication of a table from the spec.
231 static u32
vc4_get_scl_field(struct drm_plane_state
*state
, int plane
)
233 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
235 switch (vc4_state
->x_scaling
[plane
] << 2 | vc4_state
->y_scaling
[plane
]) {
236 case VC4_SCALING_PPF
<< 2 | VC4_SCALING_PPF
:
237 return SCALER_CTL0_SCL_H_PPF_V_PPF
;
238 case VC4_SCALING_TPZ
<< 2 | VC4_SCALING_PPF
:
239 return SCALER_CTL0_SCL_H_TPZ_V_PPF
;
240 case VC4_SCALING_PPF
<< 2 | VC4_SCALING_TPZ
:
241 return SCALER_CTL0_SCL_H_PPF_V_TPZ
;
242 case VC4_SCALING_TPZ
<< 2 | VC4_SCALING_TPZ
:
243 return SCALER_CTL0_SCL_H_TPZ_V_TPZ
;
244 case VC4_SCALING_PPF
<< 2 | VC4_SCALING_NONE
:
245 return SCALER_CTL0_SCL_H_PPF_V_NONE
;
246 case VC4_SCALING_NONE
<< 2 | VC4_SCALING_PPF
:
247 return SCALER_CTL0_SCL_H_NONE_V_PPF
;
248 case VC4_SCALING_NONE
<< 2 | VC4_SCALING_TPZ
:
249 return SCALER_CTL0_SCL_H_NONE_V_TPZ
;
250 case VC4_SCALING_TPZ
<< 2 | VC4_SCALING_NONE
:
251 return SCALER_CTL0_SCL_H_TPZ_V_NONE
;
253 case VC4_SCALING_NONE
<< 2 | VC4_SCALING_NONE
:
254 /* The unity case is independently handled by
261 static int vc4_plane_margins_adj(struct drm_plane_state
*pstate
)
263 struct vc4_plane_state
*vc4_pstate
= to_vc4_plane_state(pstate
);
264 unsigned int left
, right
, top
, bottom
, adjhdisplay
, adjvdisplay
;
265 struct drm_crtc_state
*crtc_state
;
267 crtc_state
= drm_atomic_get_new_crtc_state(pstate
->state
,
270 vc4_crtc_get_margins(crtc_state
, &left
, &right
, &top
, &bottom
);
271 if (!left
&& !right
&& !top
&& !bottom
)
274 if (left
+ right
>= crtc_state
->mode
.hdisplay
||
275 top
+ bottom
>= crtc_state
->mode
.vdisplay
)
278 adjhdisplay
= crtc_state
->mode
.hdisplay
- (left
+ right
);
279 vc4_pstate
->crtc_x
= DIV_ROUND_CLOSEST(vc4_pstate
->crtc_x
*
281 crtc_state
->mode
.hdisplay
);
282 vc4_pstate
->crtc_x
+= left
;
283 if (vc4_pstate
->crtc_x
> crtc_state
->mode
.hdisplay
- left
)
284 vc4_pstate
->crtc_x
= crtc_state
->mode
.hdisplay
- left
;
286 adjvdisplay
= crtc_state
->mode
.vdisplay
- (top
+ bottom
);
287 vc4_pstate
->crtc_y
= DIV_ROUND_CLOSEST(vc4_pstate
->crtc_y
*
289 crtc_state
->mode
.vdisplay
);
290 vc4_pstate
->crtc_y
+= top
;
291 if (vc4_pstate
->crtc_y
> crtc_state
->mode
.vdisplay
- top
)
292 vc4_pstate
->crtc_y
= crtc_state
->mode
.vdisplay
- top
;
294 vc4_pstate
->crtc_w
= DIV_ROUND_CLOSEST(vc4_pstate
->crtc_w
*
296 crtc_state
->mode
.hdisplay
);
297 vc4_pstate
->crtc_h
= DIV_ROUND_CLOSEST(vc4_pstate
->crtc_h
*
299 crtc_state
->mode
.vdisplay
);
301 if (!vc4_pstate
->crtc_w
|| !vc4_pstate
->crtc_h
)
307 static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state
*state
)
309 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
310 struct drm_framebuffer
*fb
= state
->fb
;
311 struct drm_gem_cma_object
*bo
= drm_fb_cma_get_gem_obj(fb
, 0);
312 u32 subpixel_src_mask
= (1 << 16) - 1;
313 u32 format
= fb
->format
->format
;
314 int num_planes
= fb
->format
->num_planes
;
315 struct drm_crtc_state
*crtc_state
;
316 u32 h_subsample
, v_subsample
;
319 crtc_state
= drm_atomic_get_existing_crtc_state(state
->state
,
322 DRM_DEBUG_KMS("Invalid crtc state\n");
326 ret
= drm_atomic_helper_check_plane_state(state
, crtc_state
, 1,
327 INT_MAX
, true, true);
331 h_subsample
= drm_format_horz_chroma_subsampling(format
);
332 v_subsample
= drm_format_vert_chroma_subsampling(format
);
334 for (i
= 0; i
< num_planes
; i
++)
335 vc4_state
->offsets
[i
] = bo
->paddr
+ fb
->offsets
[i
];
337 /* We don't support subpixel source positioning for scaling. */
338 if ((state
->src
.x1
& subpixel_src_mask
) ||
339 (state
->src
.x2
& subpixel_src_mask
) ||
340 (state
->src
.y1
& subpixel_src_mask
) ||
341 (state
->src
.y2
& subpixel_src_mask
)) {
345 vc4_state
->src_x
= state
->src
.x1
>> 16;
346 vc4_state
->src_y
= state
->src
.y1
>> 16;
347 vc4_state
->src_w
[0] = (state
->src
.x2
- state
->src
.x1
) >> 16;
348 vc4_state
->src_h
[0] = (state
->src
.y2
- state
->src
.y1
) >> 16;
350 vc4_state
->crtc_x
= state
->dst
.x1
;
351 vc4_state
->crtc_y
= state
->dst
.y1
;
352 vc4_state
->crtc_w
= state
->dst
.x2
- state
->dst
.x1
;
353 vc4_state
->crtc_h
= state
->dst
.y2
- state
->dst
.y1
;
355 ret
= vc4_plane_margins_adj(state
);
359 vc4_state
->x_scaling
[0] = vc4_get_scaling_mode(vc4_state
->src_w
[0],
361 vc4_state
->y_scaling
[0] = vc4_get_scaling_mode(vc4_state
->src_h
[0],
364 vc4_state
->is_unity
= (vc4_state
->x_scaling
[0] == VC4_SCALING_NONE
&&
365 vc4_state
->y_scaling
[0] == VC4_SCALING_NONE
);
367 if (num_planes
> 1) {
368 vc4_state
->is_yuv
= true;
370 vc4_state
->src_w
[1] = vc4_state
->src_w
[0] / h_subsample
;
371 vc4_state
->src_h
[1] = vc4_state
->src_h
[0] / v_subsample
;
373 vc4_state
->x_scaling
[1] =
374 vc4_get_scaling_mode(vc4_state
->src_w
[1],
376 vc4_state
->y_scaling
[1] =
377 vc4_get_scaling_mode(vc4_state
->src_h
[1],
380 /* YUV conversion requires that horizontal scaling be enabled
381 * on the UV plane even if vc4_get_scaling_mode() returned
382 * VC4_SCALING_NONE (which can happen when the down-scaling
383 * ratio is 0.5). Let's force it to VC4_SCALING_PPF in this
386 if (vc4_state
->x_scaling
[1] == VC4_SCALING_NONE
)
387 vc4_state
->x_scaling
[1] = VC4_SCALING_PPF
;
389 vc4_state
->is_yuv
= false;
390 vc4_state
->x_scaling
[1] = VC4_SCALING_NONE
;
391 vc4_state
->y_scaling
[1] = VC4_SCALING_NONE
;
397 static void vc4_write_tpz(struct vc4_plane_state
*vc4_state
, u32 src
, u32 dst
)
401 scale
= (1 << 16) * src
/ dst
;
403 /* The specs note that while the reciprocal would be defined
404 * as (1<<32)/scale, ~0 is close enough.
408 vc4_dlist_write(vc4_state
,
409 VC4_SET_FIELD(scale
, SCALER_TPZ0_SCALE
) |
410 VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE
));
411 vc4_dlist_write(vc4_state
,
412 VC4_SET_FIELD(recip
, SCALER_TPZ1_RECIP
));
415 static void vc4_write_ppf(struct vc4_plane_state
*vc4_state
, u32 src
, u32 dst
)
417 u32 scale
= (1 << 16) * src
/ dst
;
419 vc4_dlist_write(vc4_state
,
421 VC4_SET_FIELD(scale
, SCALER_PPF_SCALE
) |
422 VC4_SET_FIELD(0, SCALER_PPF_IPHASE
));
425 static u32
vc4_lbm_size(struct drm_plane_state
*state
)
427 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
428 /* This is the worst case number. One of the two sizes will
429 * be used depending on the scaling configuration.
431 u32 pix_per_line
= max(vc4_state
->src_w
[0], (u32
)vc4_state
->crtc_w
);
434 /* LBM is not needed when there's no vertical scaling. */
435 if (vc4_state
->y_scaling
[0] == VC4_SCALING_NONE
&&
436 vc4_state
->y_scaling
[1] == VC4_SCALING_NONE
)
439 if (!vc4_state
->is_yuv
) {
440 if (vc4_state
->y_scaling
[0] == VC4_SCALING_TPZ
)
441 lbm
= pix_per_line
* 8;
443 /* In special cases, this multiplier might be 12. */
444 lbm
= pix_per_line
* 16;
447 /* There are cases for this going down to a multiplier
448 * of 2, but according to the firmware source, the
449 * table in the docs is somewhat wrong.
451 lbm
= pix_per_line
* 16;
454 lbm
= roundup(lbm
, 32);
459 static void vc4_write_scaling_parameters(struct drm_plane_state
*state
,
462 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
464 /* Ch0 H-PPF Word 0: Scaling Parameters */
465 if (vc4_state
->x_scaling
[channel
] == VC4_SCALING_PPF
) {
466 vc4_write_ppf(vc4_state
,
467 vc4_state
->src_w
[channel
], vc4_state
->crtc_w
);
470 /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */
471 if (vc4_state
->y_scaling
[channel
] == VC4_SCALING_PPF
) {
472 vc4_write_ppf(vc4_state
,
473 vc4_state
->src_h
[channel
], vc4_state
->crtc_h
);
474 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
477 /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */
478 if (vc4_state
->x_scaling
[channel
] == VC4_SCALING_TPZ
) {
479 vc4_write_tpz(vc4_state
,
480 vc4_state
->src_w
[channel
], vc4_state
->crtc_w
);
483 /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */
484 if (vc4_state
->y_scaling
[channel
] == VC4_SCALING_TPZ
) {
485 vc4_write_tpz(vc4_state
,
486 vc4_state
->src_h
[channel
], vc4_state
->crtc_h
);
487 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
491 static int vc4_plane_allocate_lbm(struct drm_plane_state
*state
)
493 struct vc4_dev
*vc4
= to_vc4_dev(state
->plane
->dev
);
494 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
495 unsigned long irqflags
;
498 lbm_size
= vc4_lbm_size(state
);
502 if (WARN_ON(!vc4_state
->lbm_offset
))
505 /* Allocate the LBM memory that the HVS will use for temporary
506 * storage due to our scaling/format conversion.
508 if (!vc4_state
->lbm
.allocated
) {
511 spin_lock_irqsave(&vc4
->hvs
->mm_lock
, irqflags
);
512 ret
= drm_mm_insert_node_generic(&vc4
->hvs
->lbm_mm
,
515 spin_unlock_irqrestore(&vc4
->hvs
->mm_lock
, irqflags
);
520 WARN_ON_ONCE(lbm_size
!= vc4_state
->lbm
.size
);
523 vc4_state
->dlist
[vc4_state
->lbm_offset
] = vc4_state
->lbm
.start
;
528 /* Writes out a full display list for an active plane to the plane's
529 * private dlist state.
531 static int vc4_plane_mode_set(struct drm_plane
*plane
,
532 struct drm_plane_state
*state
)
534 struct vc4_dev
*vc4
= to_vc4_dev(plane
->dev
);
535 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
536 struct drm_framebuffer
*fb
= state
->fb
;
537 u32 ctl0_offset
= vc4_state
->dlist_count
;
538 const struct hvs_format
*format
= vc4_get_hvs_format(fb
->format
->format
);
539 u64 base_format_mod
= fourcc_mod_broadcom_mod(fb
->modifier
);
540 int num_planes
= drm_format_num_planes(format
->drm
);
541 u32 h_subsample
, v_subsample
;
542 bool mix_plane_alpha
;
544 u32 scl0
, scl1
, pitch0
;
546 u32 hvs_format
= format
->hvs
;
547 unsigned int rotation
;
550 if (vc4_state
->dlist_initialized
)
553 ret
= vc4_plane_setup_clipping_and_scaling(state
);
557 /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
558 * and 4:4:4, scl1 should be set to scl0 so both channels of
559 * the scaler do the same thing. For YUV, the Y plane needs
560 * to be put in channel 1 and Cb/Cr in channel 0, so we swap
561 * the scl fields here.
563 if (num_planes
== 1) {
564 scl0
= vc4_get_scl_field(state
, 0);
567 scl0
= vc4_get_scl_field(state
, 1);
568 scl1
= vc4_get_scl_field(state
, 0);
571 h_subsample
= drm_format_horz_chroma_subsampling(format
->drm
);
572 v_subsample
= drm_format_vert_chroma_subsampling(format
->drm
);
574 rotation
= drm_rotation_simplify(state
->rotation
,
579 /* We must point to the last line when Y reflection is enabled. */
580 src_y
= vc4_state
->src_y
;
581 if (rotation
& DRM_MODE_REFLECT_Y
)
582 src_y
+= vc4_state
->src_h
[0] - 1;
584 switch (base_format_mod
) {
585 case DRM_FORMAT_MOD_LINEAR
:
586 tiling
= SCALER_CTL0_TILING_LINEAR
;
587 pitch0
= VC4_SET_FIELD(fb
->pitches
[0], SCALER_SRC_PITCH
);
589 /* Adjust the base pointer to the first pixel to be scanned
592 for (i
= 0; i
< num_planes
; i
++) {
593 vc4_state
->offsets
[i
] += src_y
/
594 (i
? v_subsample
: 1) *
597 vc4_state
->offsets
[i
] += vc4_state
->src_x
/
598 (i
? h_subsample
: 1) *
604 case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED
: {
605 u32 tile_size_shift
= 12; /* T tiles are 4kb */
606 /* Whole-tile offsets, mostly for setting the pitch. */
607 u32 tile_w_shift
= fb
->format
->cpp
[0] == 2 ? 6 : 5;
608 u32 tile_h_shift
= 5; /* 16 and 32bpp are 32 pixels high */
609 u32 tile_w_mask
= (1 << tile_w_shift
) - 1;
610 /* The height mask on 32-bit-per-pixel tiles is 63, i.e. twice
611 * the height (in pixels) of a 4k tile.
613 u32 tile_h_mask
= (2 << tile_h_shift
) - 1;
614 /* For T-tiled, the FB pitch is "how many bytes from one row to
615 * the next, such that
617 * pitch * tile_h == tile_size * tiles_per_row
619 u32 tiles_w
= fb
->pitches
[0] >> (tile_size_shift
- tile_h_shift
);
620 u32 tiles_l
= vc4_state
->src_x
>> tile_w_shift
;
621 u32 tiles_r
= tiles_w
- tiles_l
;
622 u32 tiles_t
= src_y
>> tile_h_shift
;
623 /* Intra-tile offsets, which modify the base address (the
624 * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that
627 u32 tile_y
= (src_y
>> 4) & 1;
628 u32 subtile_y
= (src_y
>> 2) & 3;
629 u32 utile_y
= src_y
& 3;
630 u32 x_off
= vc4_state
->src_x
& tile_w_mask
;
631 u32 y_off
= src_y
& tile_h_mask
;
633 /* When Y reflection is requested we must set the
634 * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines
635 * after the initial one should be fetched in descending order,
636 * which makes sense since we start from the last line and go
638 * Don't know why we need y_off = max_y_off - y_off, but it's
639 * definitely required (I guess it's also related to the "going
640 * backward" situation).
642 if (rotation
& DRM_MODE_REFLECT_Y
) {
643 y_off
= tile_h_mask
- y_off
;
644 pitch0
= SCALER_PITCH0_TILE_LINE_DIR
;
649 tiling
= SCALER_CTL0_TILING_256B_OR_T
;
650 pitch0
|= (VC4_SET_FIELD(x_off
, SCALER_PITCH0_SINK_PIX
) |
651 VC4_SET_FIELD(y_off
, SCALER_PITCH0_TILE_Y_OFFSET
) |
652 VC4_SET_FIELD(tiles_l
, SCALER_PITCH0_TILE_WIDTH_L
) |
653 VC4_SET_FIELD(tiles_r
, SCALER_PITCH0_TILE_WIDTH_R
));
654 vc4_state
->offsets
[0] += tiles_t
* (tiles_w
<< tile_size_shift
);
655 vc4_state
->offsets
[0] += subtile_y
<< 8;
656 vc4_state
->offsets
[0] += utile_y
<< 4;
658 /* Rows of tiles alternate left-to-right and right-to-left. */
660 pitch0
|= SCALER_PITCH0_TILE_INITIAL_LINE_DIR
;
661 vc4_state
->offsets
[0] += (tiles_w
- tiles_l
) <<
663 vc4_state
->offsets
[0] -= (1 + !tile_y
) << 10;
665 vc4_state
->offsets
[0] += tiles_l
<< tile_size_shift
;
666 vc4_state
->offsets
[0] += tile_y
<< 10;
672 case DRM_FORMAT_MOD_BROADCOM_SAND64
:
673 case DRM_FORMAT_MOD_BROADCOM_SAND128
:
674 case DRM_FORMAT_MOD_BROADCOM_SAND256
: {
675 uint32_t param
= fourcc_mod_broadcom_param(fb
->modifier
);
676 u32 tile_w
, tile
, x_off
, pix_per_tile
;
678 hvs_format
= HVS_PIXEL_FORMAT_H264
;
680 switch (base_format_mod
) {
681 case DRM_FORMAT_MOD_BROADCOM_SAND64
:
682 tiling
= SCALER_CTL0_TILING_64B
;
685 case DRM_FORMAT_MOD_BROADCOM_SAND128
:
686 tiling
= SCALER_CTL0_TILING_128B
;
689 case DRM_FORMAT_MOD_BROADCOM_SAND256
:
690 tiling
= SCALER_CTL0_TILING_256B_OR_T
;
697 if (param
> SCALER_TILE_HEIGHT_MASK
) {
698 DRM_DEBUG_KMS("SAND height too large (%d)\n", param
);
702 pix_per_tile
= tile_w
/ fb
->format
->cpp
[0];
703 tile
= vc4_state
->src_x
/ pix_per_tile
;
704 x_off
= vc4_state
->src_x
% pix_per_tile
;
706 /* Adjust the base pointer to the first pixel to be scanned
709 for (i
= 0; i
< num_planes
; i
++) {
710 vc4_state
->offsets
[i
] += param
* tile_w
* tile
;
711 vc4_state
->offsets
[i
] += src_y
/
712 (i
? v_subsample
: 1) *
714 vc4_state
->offsets
[i
] += x_off
/
715 (i
? h_subsample
: 1) *
719 pitch0
= VC4_SET_FIELD(param
, SCALER_TILE_HEIGHT
);
724 DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
725 (long long)fb
->modifier
);
730 vc4_dlist_write(vc4_state
,
732 (rotation
& DRM_MODE_REFLECT_X
? SCALER_CTL0_HFLIP
: 0) |
733 (rotation
& DRM_MODE_REFLECT_Y
? SCALER_CTL0_VFLIP
: 0) |
734 VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND
, SCALER_CTL0_RGBA_EXPAND
) |
735 (format
->pixel_order
<< SCALER_CTL0_ORDER_SHIFT
) |
736 (hvs_format
<< SCALER_CTL0_PIXEL_FORMAT_SHIFT
) |
737 VC4_SET_FIELD(tiling
, SCALER_CTL0_TILING
) |
738 (vc4_state
->is_unity
? SCALER_CTL0_UNITY
: 0) |
739 VC4_SET_FIELD(scl0
, SCALER_CTL0_SCL0
) |
740 VC4_SET_FIELD(scl1
, SCALER_CTL0_SCL1
));
742 /* Position Word 0: Image Positions and Alpha Value */
743 vc4_state
->pos0_offset
= vc4_state
->dlist_count
;
744 vc4_dlist_write(vc4_state
,
745 VC4_SET_FIELD(state
->alpha
>> 8, SCALER_POS0_FIXED_ALPHA
) |
746 VC4_SET_FIELD(vc4_state
->crtc_x
, SCALER_POS0_START_X
) |
747 VC4_SET_FIELD(vc4_state
->crtc_y
, SCALER_POS0_START_Y
));
749 /* Position Word 1: Scaled Image Dimensions. */
750 if (!vc4_state
->is_unity
) {
751 vc4_dlist_write(vc4_state
,
752 VC4_SET_FIELD(vc4_state
->crtc_w
,
753 SCALER_POS1_SCL_WIDTH
) |
754 VC4_SET_FIELD(vc4_state
->crtc_h
,
755 SCALER_POS1_SCL_HEIGHT
));
758 /* Don't waste cycles mixing with plane alpha if the set alpha
759 * is opaque or there is no per-pixel alpha information.
760 * In any case we use the alpha property value as the fixed alpha.
762 mix_plane_alpha
= state
->alpha
!= DRM_BLEND_ALPHA_OPAQUE
&&
763 fb
->format
->has_alpha
;
765 /* Position Word 2: Source Image Size, Alpha */
766 vc4_state
->pos2_offset
= vc4_state
->dlist_count
;
767 vc4_dlist_write(vc4_state
,
768 VC4_SET_FIELD(fb
->format
->has_alpha
?
769 SCALER_POS2_ALPHA_MODE_PIPELINE
:
770 SCALER_POS2_ALPHA_MODE_FIXED
,
771 SCALER_POS2_ALPHA_MODE
) |
772 (mix_plane_alpha
? SCALER_POS2_ALPHA_MIX
: 0) |
773 (fb
->format
->has_alpha
? SCALER_POS2_ALPHA_PREMULT
: 0) |
774 VC4_SET_FIELD(vc4_state
->src_w
[0], SCALER_POS2_WIDTH
) |
775 VC4_SET_FIELD(vc4_state
->src_h
[0], SCALER_POS2_HEIGHT
));
777 /* Position Word 3: Context. Written by the HVS. */
778 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
781 /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
783 * The pointers may be any byte address.
785 vc4_state
->ptr0_offset
= vc4_state
->dlist_count
;
786 for (i
= 0; i
< num_planes
; i
++)
787 vc4_dlist_write(vc4_state
, vc4_state
->offsets
[i
]);
789 /* Pointer Context Word 0/1/2: Written by the HVS */
790 for (i
= 0; i
< num_planes
; i
++)
791 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
794 vc4_dlist_write(vc4_state
, pitch0
);
797 for (i
= 1; i
< num_planes
; i
++) {
798 if (hvs_format
!= HVS_PIXEL_FORMAT_H264
) {
799 vc4_dlist_write(vc4_state
,
800 VC4_SET_FIELD(fb
->pitches
[i
],
803 vc4_dlist_write(vc4_state
, pitch0
);
807 /* Colorspace conversion words */
808 if (vc4_state
->is_yuv
) {
809 vc4_dlist_write(vc4_state
, SCALER_CSC0_ITR_R_601_5
);
810 vc4_dlist_write(vc4_state
, SCALER_CSC1_ITR_R_601_5
);
811 vc4_dlist_write(vc4_state
, SCALER_CSC2_ITR_R_601_5
);
814 vc4_state
->lbm_offset
= 0;
816 if (vc4_state
->x_scaling
[0] != VC4_SCALING_NONE
||
817 vc4_state
->x_scaling
[1] != VC4_SCALING_NONE
||
818 vc4_state
->y_scaling
[0] != VC4_SCALING_NONE
||
819 vc4_state
->y_scaling
[1] != VC4_SCALING_NONE
) {
820 /* Reserve a slot for the LBM Base Address. The real value will
821 * be set when calling vc4_plane_allocate_lbm().
823 if (vc4_state
->y_scaling
[0] != VC4_SCALING_NONE
||
824 vc4_state
->y_scaling
[1] != VC4_SCALING_NONE
)
825 vc4_state
->lbm_offset
= vc4_state
->dlist_count
++;
827 if (num_planes
> 1) {
828 /* Emit Cb/Cr as channel 0 and Y as channel
829 * 1. This matches how we set up scl0/scl1
832 vc4_write_scaling_parameters(state
, 1);
834 vc4_write_scaling_parameters(state
, 0);
836 /* If any PPF setup was done, then all the kernel
837 * pointers get uploaded.
839 if (vc4_state
->x_scaling
[0] == VC4_SCALING_PPF
||
840 vc4_state
->y_scaling
[0] == VC4_SCALING_PPF
||
841 vc4_state
->x_scaling
[1] == VC4_SCALING_PPF
||
842 vc4_state
->y_scaling
[1] == VC4_SCALING_PPF
) {
843 u32 kernel
= VC4_SET_FIELD(vc4
->hvs
->mitchell_netravali_filter
.start
,
844 SCALER_PPF_KERNEL_OFFSET
);
847 vc4_dlist_write(vc4_state
, kernel
);
849 vc4_dlist_write(vc4_state
, kernel
);
851 vc4_dlist_write(vc4_state
, kernel
);
853 vc4_dlist_write(vc4_state
, kernel
);
857 vc4_state
->dlist
[ctl0_offset
] |=
858 VC4_SET_FIELD(vc4_state
->dlist_count
, SCALER_CTL0_SIZE
);
860 /* crtc_* are already clipped coordinates. */
861 covers_screen
= vc4_state
->crtc_x
== 0 && vc4_state
->crtc_y
== 0 &&
862 vc4_state
->crtc_w
== state
->crtc
->mode
.hdisplay
&&
863 vc4_state
->crtc_h
== state
->crtc
->mode
.vdisplay
;
864 /* Background fill might be necessary when the plane has per-pixel
865 * alpha content or a non-opaque plane alpha and could blend from the
866 * background or does not cover the entire screen.
868 vc4_state
->needs_bg_fill
= fb
->format
->has_alpha
|| !covers_screen
||
869 state
->alpha
!= DRM_BLEND_ALPHA_OPAQUE
;
871 /* Flag the dlist as initialized to avoid checking it twice in case
872 * the async update check already called vc4_plane_mode_set() and
873 * decided to fallback to sync update because async update was not
876 vc4_state
->dlist_initialized
= 1;
881 /* If a modeset involves changing the setup of a plane, the atomic
882 * infrastructure will call this to validate a proposed plane setup.
883 * However, if a plane isn't getting updated, this (and the
884 * corresponding vc4_plane_atomic_update) won't get called. Thus, we
885 * compute the dlist here and have all active plane dlists get updated
886 * in the CRTC's flush.
888 static int vc4_plane_atomic_check(struct drm_plane
*plane
,
889 struct drm_plane_state
*state
)
891 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
894 vc4_state
->dlist_count
= 0;
896 if (!plane_enabled(state
))
899 ret
= vc4_plane_mode_set(plane
, state
);
903 return vc4_plane_allocate_lbm(state
);
906 static void vc4_plane_atomic_update(struct drm_plane
*plane
,
907 struct drm_plane_state
*old_state
)
909 /* No contents here. Since we don't know where in the CRTC's
910 * dlist we should be stored, our dlist is uploaded to the
911 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
916 u32
vc4_plane_write_dlist(struct drm_plane
*plane
, u32 __iomem
*dlist
)
918 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(plane
->state
);
921 vc4_state
->hw_dlist
= dlist
;
923 /* Can't memcpy_toio() because it needs to be 32-bit writes. */
924 for (i
= 0; i
< vc4_state
->dlist_count
; i
++)
925 writel(vc4_state
->dlist
[i
], &dlist
[i
]);
927 return vc4_state
->dlist_count
;
930 u32
vc4_plane_dlist_size(const struct drm_plane_state
*state
)
932 const struct vc4_plane_state
*vc4_state
=
933 container_of(state
, typeof(*vc4_state
), base
);
935 return vc4_state
->dlist_count
;
938 /* Updates the plane to immediately (well, once the FIFO needs
939 * refilling) scan out from at a new framebuffer.
941 void vc4_plane_async_set_fb(struct drm_plane
*plane
, struct drm_framebuffer
*fb
)
943 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(plane
->state
);
944 struct drm_gem_cma_object
*bo
= drm_fb_cma_get_gem_obj(fb
, 0);
947 /* We're skipping the address adjustment for negative origin,
948 * because this is only called on the primary plane.
950 WARN_ON_ONCE(plane
->state
->crtc_x
< 0 || plane
->state
->crtc_y
< 0);
951 addr
= bo
->paddr
+ fb
->offsets
[0];
953 /* Write the new address into the hardware immediately. The
954 * scanout will start from this address as soon as the FIFO
955 * needs to refill with pixels.
957 writel(addr
, &vc4_state
->hw_dlist
[vc4_state
->ptr0_offset
]);
959 /* Also update the CPU-side dlist copy, so that any later
960 * atomic updates that don't do a new modeset on our plane
961 * also use our updated address.
963 vc4_state
->dlist
[vc4_state
->ptr0_offset
] = addr
;
966 static void vc4_plane_atomic_async_update(struct drm_plane
*plane
,
967 struct drm_plane_state
*state
)
969 struct vc4_plane_state
*vc4_state
, *new_vc4_state
;
971 drm_atomic_set_fb_for_plane(plane
->state
, state
->fb
);
972 plane
->state
->crtc_x
= state
->crtc_x
;
973 plane
->state
->crtc_y
= state
->crtc_y
;
974 plane
->state
->crtc_w
= state
->crtc_w
;
975 plane
->state
->crtc_h
= state
->crtc_h
;
976 plane
->state
->src_x
= state
->src_x
;
977 plane
->state
->src_y
= state
->src_y
;
978 plane
->state
->src_w
= state
->src_w
;
979 plane
->state
->src_h
= state
->src_h
;
980 plane
->state
->src_h
= state
->src_h
;
981 plane
->state
->alpha
= state
->alpha
;
982 plane
->state
->pixel_blend_mode
= state
->pixel_blend_mode
;
983 plane
->state
->rotation
= state
->rotation
;
984 plane
->state
->zpos
= state
->zpos
;
985 plane
->state
->normalized_zpos
= state
->normalized_zpos
;
986 plane
->state
->color_encoding
= state
->color_encoding
;
987 plane
->state
->color_range
= state
->color_range
;
988 plane
->state
->src
= state
->src
;
989 plane
->state
->dst
= state
->dst
;
990 plane
->state
->visible
= state
->visible
;
992 new_vc4_state
= to_vc4_plane_state(state
);
993 vc4_state
= to_vc4_plane_state(plane
->state
);
995 vc4_state
->crtc_x
= new_vc4_state
->crtc_x
;
996 vc4_state
->crtc_y
= new_vc4_state
->crtc_y
;
997 vc4_state
->crtc_h
= new_vc4_state
->crtc_h
;
998 vc4_state
->crtc_w
= new_vc4_state
->crtc_w
;
999 vc4_state
->src_x
= new_vc4_state
->src_x
;
1000 vc4_state
->src_y
= new_vc4_state
->src_y
;
1001 memcpy(vc4_state
->src_w
, new_vc4_state
->src_w
,
1002 sizeof(vc4_state
->src_w
));
1003 memcpy(vc4_state
->src_h
, new_vc4_state
->src_h
,
1004 sizeof(vc4_state
->src_h
));
1005 memcpy(vc4_state
->x_scaling
, new_vc4_state
->x_scaling
,
1006 sizeof(vc4_state
->x_scaling
));
1007 memcpy(vc4_state
->y_scaling
, new_vc4_state
->y_scaling
,
1008 sizeof(vc4_state
->y_scaling
));
1009 vc4_state
->is_unity
= new_vc4_state
->is_unity
;
1010 vc4_state
->is_yuv
= new_vc4_state
->is_yuv
;
1011 memcpy(vc4_state
->offsets
, new_vc4_state
->offsets
,
1012 sizeof(vc4_state
->offsets
));
1013 vc4_state
->needs_bg_fill
= new_vc4_state
->needs_bg_fill
;
1015 /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */
1016 vc4_state
->dlist
[vc4_state
->pos0_offset
] =
1017 new_vc4_state
->dlist
[vc4_state
->pos0_offset
];
1018 vc4_state
->dlist
[vc4_state
->pos2_offset
] =
1019 new_vc4_state
->dlist
[vc4_state
->pos2_offset
];
1020 vc4_state
->dlist
[vc4_state
->ptr0_offset
] =
1021 new_vc4_state
->dlist
[vc4_state
->ptr0_offset
];
1023 /* Note that we can't just call vc4_plane_write_dlist()
1024 * because that would smash the context data that the HVS is
1027 writel(vc4_state
->dlist
[vc4_state
->pos0_offset
],
1028 &vc4_state
->hw_dlist
[vc4_state
->pos0_offset
]);
1029 writel(vc4_state
->dlist
[vc4_state
->pos2_offset
],
1030 &vc4_state
->hw_dlist
[vc4_state
->pos2_offset
]);
1031 writel(vc4_state
->dlist
[vc4_state
->ptr0_offset
],
1032 &vc4_state
->hw_dlist
[vc4_state
->ptr0_offset
]);
1035 static int vc4_plane_atomic_async_check(struct drm_plane
*plane
,
1036 struct drm_plane_state
*state
)
1038 struct vc4_plane_state
*old_vc4_state
, *new_vc4_state
;
1042 ret
= vc4_plane_mode_set(plane
, state
);
1046 old_vc4_state
= to_vc4_plane_state(plane
->state
);
1047 new_vc4_state
= to_vc4_plane_state(state
);
1048 if (old_vc4_state
->dlist_count
!= new_vc4_state
->dlist_count
||
1049 old_vc4_state
->pos0_offset
!= new_vc4_state
->pos0_offset
||
1050 old_vc4_state
->pos2_offset
!= new_vc4_state
->pos2_offset
||
1051 old_vc4_state
->ptr0_offset
!= new_vc4_state
->ptr0_offset
||
1052 vc4_lbm_size(plane
->state
) != vc4_lbm_size(state
))
1055 /* Only pos0, pos2 and ptr0 DWORDS can be updated in an async update
1056 * if anything else has changed, fallback to a sync update.
1058 for (i
= 0; i
< new_vc4_state
->dlist_count
; i
++) {
1059 if (i
== new_vc4_state
->pos0_offset
||
1060 i
== new_vc4_state
->pos2_offset
||
1061 i
== new_vc4_state
->ptr0_offset
||
1062 (new_vc4_state
->lbm_offset
&&
1063 i
== new_vc4_state
->lbm_offset
))
1066 if (new_vc4_state
->dlist
[i
] != old_vc4_state
->dlist
[i
])
1073 static int vc4_prepare_fb(struct drm_plane
*plane
,
1074 struct drm_plane_state
*state
)
1077 struct dma_fence
*fence
;
1083 bo
= to_vc4_bo(&drm_fb_cma_get_gem_obj(state
->fb
, 0)->base
);
1085 fence
= reservation_object_get_excl_rcu(bo
->resv
);
1086 drm_atomic_set_fence_for_plane(state
, fence
);
1088 if (plane
->state
->fb
== state
->fb
)
1091 ret
= vc4_bo_inc_usecnt(bo
);
1098 static void vc4_cleanup_fb(struct drm_plane
*plane
,
1099 struct drm_plane_state
*state
)
1103 if (plane
->state
->fb
== state
->fb
|| !state
->fb
)
1106 bo
= to_vc4_bo(&drm_fb_cma_get_gem_obj(state
->fb
, 0)->base
);
1107 vc4_bo_dec_usecnt(bo
);
1110 static const struct drm_plane_helper_funcs vc4_plane_helper_funcs
= {
1111 .atomic_check
= vc4_plane_atomic_check
,
1112 .atomic_update
= vc4_plane_atomic_update
,
1113 .prepare_fb
= vc4_prepare_fb
,
1114 .cleanup_fb
= vc4_cleanup_fb
,
1115 .atomic_async_check
= vc4_plane_atomic_async_check
,
1116 .atomic_async_update
= vc4_plane_atomic_async_update
,
1119 static void vc4_plane_destroy(struct drm_plane
*plane
)
1121 drm_plane_cleanup(plane
);
1124 static bool vc4_format_mod_supported(struct drm_plane
*plane
,
1128 /* Support T_TILING for RGB formats only. */
1130 case DRM_FORMAT_XRGB8888
:
1131 case DRM_FORMAT_ARGB8888
:
1132 case DRM_FORMAT_ABGR8888
:
1133 case DRM_FORMAT_XBGR8888
:
1134 case DRM_FORMAT_RGB565
:
1135 case DRM_FORMAT_BGR565
:
1136 case DRM_FORMAT_ARGB1555
:
1137 case DRM_FORMAT_XRGB1555
:
1138 switch (fourcc_mod_broadcom_mod(modifier
)) {
1139 case DRM_FORMAT_MOD_LINEAR
:
1140 case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED
:
1145 case DRM_FORMAT_NV12
:
1146 case DRM_FORMAT_NV21
:
1147 switch (fourcc_mod_broadcom_mod(modifier
)) {
1148 case DRM_FORMAT_MOD_LINEAR
:
1149 case DRM_FORMAT_MOD_BROADCOM_SAND64
:
1150 case DRM_FORMAT_MOD_BROADCOM_SAND128
:
1151 case DRM_FORMAT_MOD_BROADCOM_SAND256
:
1156 case DRM_FORMAT_YUV422
:
1157 case DRM_FORMAT_YVU422
:
1158 case DRM_FORMAT_YUV420
:
1159 case DRM_FORMAT_YVU420
:
1160 case DRM_FORMAT_NV16
:
1161 case DRM_FORMAT_NV61
:
1163 return (modifier
== DRM_FORMAT_MOD_LINEAR
);
1167 static const struct drm_plane_funcs vc4_plane_funcs
= {
1168 .update_plane
= drm_atomic_helper_update_plane
,
1169 .disable_plane
= drm_atomic_helper_disable_plane
,
1170 .destroy
= vc4_plane_destroy
,
1171 .set_property
= NULL
,
1172 .reset
= vc4_plane_reset
,
1173 .atomic_duplicate_state
= vc4_plane_duplicate_state
,
1174 .atomic_destroy_state
= vc4_plane_destroy_state
,
1175 .format_mod_supported
= vc4_format_mod_supported
,
1178 struct drm_plane
*vc4_plane_init(struct drm_device
*dev
,
1179 enum drm_plane_type type
)
1181 struct drm_plane
*plane
= NULL
;
1182 struct vc4_plane
*vc4_plane
;
1183 u32 formats
[ARRAY_SIZE(hvs_formats
)];
1186 static const uint64_t modifiers
[] = {
1187 DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED
,
1188 DRM_FORMAT_MOD_BROADCOM_SAND128
,
1189 DRM_FORMAT_MOD_BROADCOM_SAND64
,
1190 DRM_FORMAT_MOD_BROADCOM_SAND256
,
1191 DRM_FORMAT_MOD_LINEAR
,
1192 DRM_FORMAT_MOD_INVALID
1195 vc4_plane
= devm_kzalloc(dev
->dev
, sizeof(*vc4_plane
),
1198 return ERR_PTR(-ENOMEM
);
1200 for (i
= 0; i
< ARRAY_SIZE(hvs_formats
); i
++)
1201 formats
[i
] = hvs_formats
[i
].drm
;
1203 plane
= &vc4_plane
->base
;
1204 ret
= drm_universal_plane_init(dev
, plane
, 0,
1206 formats
, ARRAY_SIZE(formats
),
1207 modifiers
, type
, NULL
);
1209 drm_plane_helper_add(plane
, &vc4_plane_helper_funcs
);
1211 drm_plane_create_alpha_property(plane
);
1212 drm_plane_create_rotation_property(plane
, DRM_MODE_ROTATE_0
,
1214 DRM_MODE_ROTATE_180
|
1215 DRM_MODE_REFLECT_X
|
1216 DRM_MODE_REFLECT_Y
);