1 // SPDX-License-Identifier: GPL-2.0-only
3 * Copyright (C) 2015 Broadcom
7 * DOC: VC4 plane module
9 * Each DRM plane is a layer of pixels being scanned out by the HVS.
11 * At atomic modeset check time, we compute the HVS display element
12 * state that would be necessary for displaying the plane (giving us a
13 * chance to figure out if a plane configuration is invalid), then at
14 * atomic flush time the CRTC will ask us to write our element state
15 * into the region of the HVS that it has allocated for us.
18 #include <drm/drm_atomic.h>
19 #include <drm/drm_atomic_helper.h>
20 #include <drm/drm_atomic_uapi.h>
21 #include <drm/drm_fb_cma_helper.h>
22 #include <drm/drm_fourcc.h>
23 #include <drm/drm_gem_framebuffer_helper.h>
24 #include <drm/drm_plane_helper.h>
26 #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 (drm_mm_node_allocated(&vc4_state
->lbm
)) {
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 int num_planes
= fb
->format
->num_planes
;
314 struct drm_crtc_state
*crtc_state
;
315 u32 h_subsample
= fb
->format
->hsub
;
316 u32 v_subsample
= fb
->format
->vsub
;
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 for (i
= 0; i
< num_planes
; i
++)
332 vc4_state
->offsets
[i
] = bo
->paddr
+ fb
->offsets
[i
];
334 /* We don't support subpixel source positioning for scaling. */
335 if ((state
->src
.x1
& subpixel_src_mask
) ||
336 (state
->src
.x2
& subpixel_src_mask
) ||
337 (state
->src
.y1
& subpixel_src_mask
) ||
338 (state
->src
.y2
& subpixel_src_mask
)) {
342 vc4_state
->src_x
= state
->src
.x1
>> 16;
343 vc4_state
->src_y
= state
->src
.y1
>> 16;
344 vc4_state
->src_w
[0] = (state
->src
.x2
- state
->src
.x1
) >> 16;
345 vc4_state
->src_h
[0] = (state
->src
.y2
- state
->src
.y1
) >> 16;
347 vc4_state
->crtc_x
= state
->dst
.x1
;
348 vc4_state
->crtc_y
= state
->dst
.y1
;
349 vc4_state
->crtc_w
= state
->dst
.x2
- state
->dst
.x1
;
350 vc4_state
->crtc_h
= state
->dst
.y2
- state
->dst
.y1
;
352 ret
= vc4_plane_margins_adj(state
);
356 vc4_state
->x_scaling
[0] = vc4_get_scaling_mode(vc4_state
->src_w
[0],
358 vc4_state
->y_scaling
[0] = vc4_get_scaling_mode(vc4_state
->src_h
[0],
361 vc4_state
->is_unity
= (vc4_state
->x_scaling
[0] == VC4_SCALING_NONE
&&
362 vc4_state
->y_scaling
[0] == VC4_SCALING_NONE
);
364 if (num_planes
> 1) {
365 vc4_state
->is_yuv
= true;
367 vc4_state
->src_w
[1] = vc4_state
->src_w
[0] / h_subsample
;
368 vc4_state
->src_h
[1] = vc4_state
->src_h
[0] / v_subsample
;
370 vc4_state
->x_scaling
[1] =
371 vc4_get_scaling_mode(vc4_state
->src_w
[1],
373 vc4_state
->y_scaling
[1] =
374 vc4_get_scaling_mode(vc4_state
->src_h
[1],
377 /* YUV conversion requires that horizontal scaling be enabled
378 * on the UV plane even if vc4_get_scaling_mode() returned
379 * VC4_SCALING_NONE (which can happen when the down-scaling
380 * ratio is 0.5). Let's force it to VC4_SCALING_PPF in this
383 if (vc4_state
->x_scaling
[1] == VC4_SCALING_NONE
)
384 vc4_state
->x_scaling
[1] = VC4_SCALING_PPF
;
386 vc4_state
->is_yuv
= false;
387 vc4_state
->x_scaling
[1] = VC4_SCALING_NONE
;
388 vc4_state
->y_scaling
[1] = VC4_SCALING_NONE
;
394 static void vc4_write_tpz(struct vc4_plane_state
*vc4_state
, u32 src
, u32 dst
)
398 scale
= (1 << 16) * src
/ dst
;
400 /* The specs note that while the reciprocal would be defined
401 * as (1<<32)/scale, ~0 is close enough.
405 vc4_dlist_write(vc4_state
,
406 VC4_SET_FIELD(scale
, SCALER_TPZ0_SCALE
) |
407 VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE
));
408 vc4_dlist_write(vc4_state
,
409 VC4_SET_FIELD(recip
, SCALER_TPZ1_RECIP
));
412 static void vc4_write_ppf(struct vc4_plane_state
*vc4_state
, u32 src
, u32 dst
)
414 u32 scale
= (1 << 16) * src
/ dst
;
416 vc4_dlist_write(vc4_state
,
418 VC4_SET_FIELD(scale
, SCALER_PPF_SCALE
) |
419 VC4_SET_FIELD(0, SCALER_PPF_IPHASE
));
422 static u32
vc4_lbm_size(struct drm_plane_state
*state
)
424 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
425 /* This is the worst case number. One of the two sizes will
426 * be used depending on the scaling configuration.
428 u32 pix_per_line
= max(vc4_state
->src_w
[0], (u32
)vc4_state
->crtc_w
);
431 /* LBM is not needed when there's no vertical scaling. */
432 if (vc4_state
->y_scaling
[0] == VC4_SCALING_NONE
&&
433 vc4_state
->y_scaling
[1] == VC4_SCALING_NONE
)
436 if (!vc4_state
->is_yuv
) {
437 if (vc4_state
->y_scaling
[0] == VC4_SCALING_TPZ
)
438 lbm
= pix_per_line
* 8;
440 /* In special cases, this multiplier might be 12. */
441 lbm
= pix_per_line
* 16;
444 /* There are cases for this going down to a multiplier
445 * of 2, but according to the firmware source, the
446 * table in the docs is somewhat wrong.
448 lbm
= pix_per_line
* 16;
451 lbm
= roundup(lbm
, 32);
456 static void vc4_write_scaling_parameters(struct drm_plane_state
*state
,
459 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
461 /* Ch0 H-PPF Word 0: Scaling Parameters */
462 if (vc4_state
->x_scaling
[channel
] == VC4_SCALING_PPF
) {
463 vc4_write_ppf(vc4_state
,
464 vc4_state
->src_w
[channel
], vc4_state
->crtc_w
);
467 /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */
468 if (vc4_state
->y_scaling
[channel
] == VC4_SCALING_PPF
) {
469 vc4_write_ppf(vc4_state
,
470 vc4_state
->src_h
[channel
], vc4_state
->crtc_h
);
471 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
474 /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */
475 if (vc4_state
->x_scaling
[channel
] == VC4_SCALING_TPZ
) {
476 vc4_write_tpz(vc4_state
,
477 vc4_state
->src_w
[channel
], vc4_state
->crtc_w
);
480 /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */
481 if (vc4_state
->y_scaling
[channel
] == VC4_SCALING_TPZ
) {
482 vc4_write_tpz(vc4_state
,
483 vc4_state
->src_h
[channel
], vc4_state
->crtc_h
);
484 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
488 static void vc4_plane_calc_load(struct drm_plane_state
*state
)
490 unsigned int hvs_load_shift
, vrefresh
, i
;
491 struct drm_framebuffer
*fb
= state
->fb
;
492 struct vc4_plane_state
*vc4_state
;
493 struct drm_crtc_state
*crtc_state
;
494 unsigned int vscale_factor
;
496 vc4_state
= to_vc4_plane_state(state
);
497 crtc_state
= drm_atomic_get_existing_crtc_state(state
->state
,
499 vrefresh
= drm_mode_vrefresh(&crtc_state
->adjusted_mode
);
501 /* The HVS is able to process 2 pixels/cycle when scaling the source,
502 * 4 pixels/cycle otherwise.
503 * Alpha blending step seems to be pipelined and it's always operating
504 * at 4 pixels/cycle, so the limiting aspect here seems to be the
506 * HVS load is expressed in clk-cycles/sec (AKA Hz).
508 if (vc4_state
->x_scaling
[0] != VC4_SCALING_NONE
||
509 vc4_state
->x_scaling
[1] != VC4_SCALING_NONE
||
510 vc4_state
->y_scaling
[0] != VC4_SCALING_NONE
||
511 vc4_state
->y_scaling
[1] != VC4_SCALING_NONE
)
516 vc4_state
->membus_load
= 0;
517 vc4_state
->hvs_load
= 0;
518 for (i
= 0; i
< fb
->format
->num_planes
; i
++) {
519 /* Even if the bandwidth/plane required for a single frame is
521 * vc4_state->src_w[i] * vc4_state->src_h[i] * cpp * vrefresh
523 * when downscaling, we have to read more pixels per line in
524 * the time frame reserved for a single line, so the bandwidth
525 * demand can be punctually higher. To account for that, we
526 * calculate the down-scaling factor and multiply the plane
527 * load by this number. We're likely over-estimating the read
528 * demand, but that's better than under-estimating it.
530 vscale_factor
= DIV_ROUND_UP(vc4_state
->src_h
[i
],
532 vc4_state
->membus_load
+= vc4_state
->src_w
[i
] *
533 vc4_state
->src_h
[i
] * vscale_factor
*
535 vc4_state
->hvs_load
+= vc4_state
->crtc_h
* vc4_state
->crtc_w
;
538 vc4_state
->hvs_load
*= vrefresh
;
539 vc4_state
->hvs_load
>>= hvs_load_shift
;
540 vc4_state
->membus_load
*= vrefresh
;
543 static int vc4_plane_allocate_lbm(struct drm_plane_state
*state
)
545 struct vc4_dev
*vc4
= to_vc4_dev(state
->plane
->dev
);
546 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
547 unsigned long irqflags
;
550 lbm_size
= vc4_lbm_size(state
);
554 if (WARN_ON(!vc4_state
->lbm_offset
))
557 /* Allocate the LBM memory that the HVS will use for temporary
558 * storage due to our scaling/format conversion.
560 if (!drm_mm_node_allocated(&vc4_state
->lbm
)) {
563 spin_lock_irqsave(&vc4
->hvs
->mm_lock
, irqflags
);
564 ret
= drm_mm_insert_node_generic(&vc4
->hvs
->lbm_mm
,
567 spin_unlock_irqrestore(&vc4
->hvs
->mm_lock
, irqflags
);
572 WARN_ON_ONCE(lbm_size
!= vc4_state
->lbm
.size
);
575 vc4_state
->dlist
[vc4_state
->lbm_offset
] = vc4_state
->lbm
.start
;
580 /* Writes out a full display list for an active plane to the plane's
581 * private dlist state.
583 static int vc4_plane_mode_set(struct drm_plane
*plane
,
584 struct drm_plane_state
*state
)
586 struct vc4_dev
*vc4
= to_vc4_dev(plane
->dev
);
587 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
588 struct drm_framebuffer
*fb
= state
->fb
;
589 u32 ctl0_offset
= vc4_state
->dlist_count
;
590 const struct hvs_format
*format
= vc4_get_hvs_format(fb
->format
->format
);
591 u64 base_format_mod
= fourcc_mod_broadcom_mod(fb
->modifier
);
592 int num_planes
= fb
->format
->num_planes
;
593 u32 h_subsample
= fb
->format
->hsub
;
594 u32 v_subsample
= fb
->format
->vsub
;
595 bool mix_plane_alpha
;
597 u32 scl0
, scl1
, pitch0
;
599 u32 hvs_format
= format
->hvs
;
600 unsigned int rotation
;
603 if (vc4_state
->dlist_initialized
)
606 ret
= vc4_plane_setup_clipping_and_scaling(state
);
610 /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
611 * and 4:4:4, scl1 should be set to scl0 so both channels of
612 * the scaler do the same thing. For YUV, the Y plane needs
613 * to be put in channel 1 and Cb/Cr in channel 0, so we swap
614 * the scl fields here.
616 if (num_planes
== 1) {
617 scl0
= vc4_get_scl_field(state
, 0);
620 scl0
= vc4_get_scl_field(state
, 1);
621 scl1
= vc4_get_scl_field(state
, 0);
624 rotation
= drm_rotation_simplify(state
->rotation
,
629 /* We must point to the last line when Y reflection is enabled. */
630 src_y
= vc4_state
->src_y
;
631 if (rotation
& DRM_MODE_REFLECT_Y
)
632 src_y
+= vc4_state
->src_h
[0] - 1;
634 switch (base_format_mod
) {
635 case DRM_FORMAT_MOD_LINEAR
:
636 tiling
= SCALER_CTL0_TILING_LINEAR
;
637 pitch0
= VC4_SET_FIELD(fb
->pitches
[0], SCALER_SRC_PITCH
);
639 /* Adjust the base pointer to the first pixel to be scanned
642 for (i
= 0; i
< num_planes
; i
++) {
643 vc4_state
->offsets
[i
] += src_y
/
644 (i
? v_subsample
: 1) *
647 vc4_state
->offsets
[i
] += vc4_state
->src_x
/
648 (i
? h_subsample
: 1) *
654 case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED
: {
655 u32 tile_size_shift
= 12; /* T tiles are 4kb */
656 /* Whole-tile offsets, mostly for setting the pitch. */
657 u32 tile_w_shift
= fb
->format
->cpp
[0] == 2 ? 6 : 5;
658 u32 tile_h_shift
= 5; /* 16 and 32bpp are 32 pixels high */
659 u32 tile_w_mask
= (1 << tile_w_shift
) - 1;
660 /* The height mask on 32-bit-per-pixel tiles is 63, i.e. twice
661 * the height (in pixels) of a 4k tile.
663 u32 tile_h_mask
= (2 << tile_h_shift
) - 1;
664 /* For T-tiled, the FB pitch is "how many bytes from one row to
665 * the next, such that
667 * pitch * tile_h == tile_size * tiles_per_row
669 u32 tiles_w
= fb
->pitches
[0] >> (tile_size_shift
- tile_h_shift
);
670 u32 tiles_l
= vc4_state
->src_x
>> tile_w_shift
;
671 u32 tiles_r
= tiles_w
- tiles_l
;
672 u32 tiles_t
= src_y
>> tile_h_shift
;
673 /* Intra-tile offsets, which modify the base address (the
674 * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that
677 u32 tile_y
= (src_y
>> 4) & 1;
678 u32 subtile_y
= (src_y
>> 2) & 3;
679 u32 utile_y
= src_y
& 3;
680 u32 x_off
= vc4_state
->src_x
& tile_w_mask
;
681 u32 y_off
= src_y
& tile_h_mask
;
683 /* When Y reflection is requested we must set the
684 * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines
685 * after the initial one should be fetched in descending order,
686 * which makes sense since we start from the last line and go
688 * Don't know why we need y_off = max_y_off - y_off, but it's
689 * definitely required (I guess it's also related to the "going
690 * backward" situation).
692 if (rotation
& DRM_MODE_REFLECT_Y
) {
693 y_off
= tile_h_mask
- y_off
;
694 pitch0
= SCALER_PITCH0_TILE_LINE_DIR
;
699 tiling
= SCALER_CTL0_TILING_256B_OR_T
;
700 pitch0
|= (VC4_SET_FIELD(x_off
, SCALER_PITCH0_SINK_PIX
) |
701 VC4_SET_FIELD(y_off
, SCALER_PITCH0_TILE_Y_OFFSET
) |
702 VC4_SET_FIELD(tiles_l
, SCALER_PITCH0_TILE_WIDTH_L
) |
703 VC4_SET_FIELD(tiles_r
, SCALER_PITCH0_TILE_WIDTH_R
));
704 vc4_state
->offsets
[0] += tiles_t
* (tiles_w
<< tile_size_shift
);
705 vc4_state
->offsets
[0] += subtile_y
<< 8;
706 vc4_state
->offsets
[0] += utile_y
<< 4;
708 /* Rows of tiles alternate left-to-right and right-to-left. */
710 pitch0
|= SCALER_PITCH0_TILE_INITIAL_LINE_DIR
;
711 vc4_state
->offsets
[0] += (tiles_w
- tiles_l
) <<
713 vc4_state
->offsets
[0] -= (1 + !tile_y
) << 10;
715 vc4_state
->offsets
[0] += tiles_l
<< tile_size_shift
;
716 vc4_state
->offsets
[0] += tile_y
<< 10;
722 case DRM_FORMAT_MOD_BROADCOM_SAND64
:
723 case DRM_FORMAT_MOD_BROADCOM_SAND128
:
724 case DRM_FORMAT_MOD_BROADCOM_SAND256
: {
725 uint32_t param
= fourcc_mod_broadcom_param(fb
->modifier
);
726 u32 tile_w
, tile
, x_off
, pix_per_tile
;
728 hvs_format
= HVS_PIXEL_FORMAT_H264
;
730 switch (base_format_mod
) {
731 case DRM_FORMAT_MOD_BROADCOM_SAND64
:
732 tiling
= SCALER_CTL0_TILING_64B
;
735 case DRM_FORMAT_MOD_BROADCOM_SAND128
:
736 tiling
= SCALER_CTL0_TILING_128B
;
739 case DRM_FORMAT_MOD_BROADCOM_SAND256
:
740 tiling
= SCALER_CTL0_TILING_256B_OR_T
;
747 if (param
> SCALER_TILE_HEIGHT_MASK
) {
748 DRM_DEBUG_KMS("SAND height too large (%d)\n", param
);
752 pix_per_tile
= tile_w
/ fb
->format
->cpp
[0];
753 tile
= vc4_state
->src_x
/ pix_per_tile
;
754 x_off
= vc4_state
->src_x
% pix_per_tile
;
756 /* Adjust the base pointer to the first pixel to be scanned
759 for (i
= 0; i
< num_planes
; i
++) {
760 vc4_state
->offsets
[i
] += param
* tile_w
* tile
;
761 vc4_state
->offsets
[i
] += src_y
/
762 (i
? v_subsample
: 1) *
764 vc4_state
->offsets
[i
] += x_off
/
765 (i
? h_subsample
: 1) *
769 pitch0
= VC4_SET_FIELD(param
, SCALER_TILE_HEIGHT
);
774 DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
775 (long long)fb
->modifier
);
780 vc4_dlist_write(vc4_state
,
782 (rotation
& DRM_MODE_REFLECT_X
? SCALER_CTL0_HFLIP
: 0) |
783 (rotation
& DRM_MODE_REFLECT_Y
? SCALER_CTL0_VFLIP
: 0) |
784 VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND
, SCALER_CTL0_RGBA_EXPAND
) |
785 (format
->pixel_order
<< SCALER_CTL0_ORDER_SHIFT
) |
786 (hvs_format
<< SCALER_CTL0_PIXEL_FORMAT_SHIFT
) |
787 VC4_SET_FIELD(tiling
, SCALER_CTL0_TILING
) |
788 (vc4_state
->is_unity
? SCALER_CTL0_UNITY
: 0) |
789 VC4_SET_FIELD(scl0
, SCALER_CTL0_SCL0
) |
790 VC4_SET_FIELD(scl1
, SCALER_CTL0_SCL1
));
792 /* Position Word 0: Image Positions and Alpha Value */
793 vc4_state
->pos0_offset
= vc4_state
->dlist_count
;
794 vc4_dlist_write(vc4_state
,
795 VC4_SET_FIELD(state
->alpha
>> 8, SCALER_POS0_FIXED_ALPHA
) |
796 VC4_SET_FIELD(vc4_state
->crtc_x
, SCALER_POS0_START_X
) |
797 VC4_SET_FIELD(vc4_state
->crtc_y
, SCALER_POS0_START_Y
));
799 /* Position Word 1: Scaled Image Dimensions. */
800 if (!vc4_state
->is_unity
) {
801 vc4_dlist_write(vc4_state
,
802 VC4_SET_FIELD(vc4_state
->crtc_w
,
803 SCALER_POS1_SCL_WIDTH
) |
804 VC4_SET_FIELD(vc4_state
->crtc_h
,
805 SCALER_POS1_SCL_HEIGHT
));
808 /* Don't waste cycles mixing with plane alpha if the set alpha
809 * is opaque or there is no per-pixel alpha information.
810 * In any case we use the alpha property value as the fixed alpha.
812 mix_plane_alpha
= state
->alpha
!= DRM_BLEND_ALPHA_OPAQUE
&&
813 fb
->format
->has_alpha
;
815 /* Position Word 2: Source Image Size, Alpha */
816 vc4_state
->pos2_offset
= vc4_state
->dlist_count
;
817 vc4_dlist_write(vc4_state
,
818 VC4_SET_FIELD(fb
->format
->has_alpha
?
819 SCALER_POS2_ALPHA_MODE_PIPELINE
:
820 SCALER_POS2_ALPHA_MODE_FIXED
,
821 SCALER_POS2_ALPHA_MODE
) |
822 (mix_plane_alpha
? SCALER_POS2_ALPHA_MIX
: 0) |
823 (fb
->format
->has_alpha
? SCALER_POS2_ALPHA_PREMULT
: 0) |
824 VC4_SET_FIELD(vc4_state
->src_w
[0], SCALER_POS2_WIDTH
) |
825 VC4_SET_FIELD(vc4_state
->src_h
[0], SCALER_POS2_HEIGHT
));
827 /* Position Word 3: Context. Written by the HVS. */
828 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
831 /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
833 * The pointers may be any byte address.
835 vc4_state
->ptr0_offset
= vc4_state
->dlist_count
;
836 for (i
= 0; i
< num_planes
; i
++)
837 vc4_dlist_write(vc4_state
, vc4_state
->offsets
[i
]);
839 /* Pointer Context Word 0/1/2: Written by the HVS */
840 for (i
= 0; i
< num_planes
; i
++)
841 vc4_dlist_write(vc4_state
, 0xc0c0c0c0);
844 vc4_dlist_write(vc4_state
, pitch0
);
847 for (i
= 1; i
< num_planes
; i
++) {
848 if (hvs_format
!= HVS_PIXEL_FORMAT_H264
) {
849 vc4_dlist_write(vc4_state
,
850 VC4_SET_FIELD(fb
->pitches
[i
],
853 vc4_dlist_write(vc4_state
, pitch0
);
857 /* Colorspace conversion words */
858 if (vc4_state
->is_yuv
) {
859 vc4_dlist_write(vc4_state
, SCALER_CSC0_ITR_R_601_5
);
860 vc4_dlist_write(vc4_state
, SCALER_CSC1_ITR_R_601_5
);
861 vc4_dlist_write(vc4_state
, SCALER_CSC2_ITR_R_601_5
);
864 vc4_state
->lbm_offset
= 0;
866 if (vc4_state
->x_scaling
[0] != VC4_SCALING_NONE
||
867 vc4_state
->x_scaling
[1] != VC4_SCALING_NONE
||
868 vc4_state
->y_scaling
[0] != VC4_SCALING_NONE
||
869 vc4_state
->y_scaling
[1] != VC4_SCALING_NONE
) {
870 /* Reserve a slot for the LBM Base Address. The real value will
871 * be set when calling vc4_plane_allocate_lbm().
873 if (vc4_state
->y_scaling
[0] != VC4_SCALING_NONE
||
874 vc4_state
->y_scaling
[1] != VC4_SCALING_NONE
)
875 vc4_state
->lbm_offset
= vc4_state
->dlist_count
++;
877 if (num_planes
> 1) {
878 /* Emit Cb/Cr as channel 0 and Y as channel
879 * 1. This matches how we set up scl0/scl1
882 vc4_write_scaling_parameters(state
, 1);
884 vc4_write_scaling_parameters(state
, 0);
886 /* If any PPF setup was done, then all the kernel
887 * pointers get uploaded.
889 if (vc4_state
->x_scaling
[0] == VC4_SCALING_PPF
||
890 vc4_state
->y_scaling
[0] == VC4_SCALING_PPF
||
891 vc4_state
->x_scaling
[1] == VC4_SCALING_PPF
||
892 vc4_state
->y_scaling
[1] == VC4_SCALING_PPF
) {
893 u32 kernel
= VC4_SET_FIELD(vc4
->hvs
->mitchell_netravali_filter
.start
,
894 SCALER_PPF_KERNEL_OFFSET
);
897 vc4_dlist_write(vc4_state
, kernel
);
899 vc4_dlist_write(vc4_state
, kernel
);
901 vc4_dlist_write(vc4_state
, kernel
);
903 vc4_dlist_write(vc4_state
, kernel
);
907 vc4_state
->dlist
[ctl0_offset
] |=
908 VC4_SET_FIELD(vc4_state
->dlist_count
, SCALER_CTL0_SIZE
);
910 /* crtc_* are already clipped coordinates. */
911 covers_screen
= vc4_state
->crtc_x
== 0 && vc4_state
->crtc_y
== 0 &&
912 vc4_state
->crtc_w
== state
->crtc
->mode
.hdisplay
&&
913 vc4_state
->crtc_h
== state
->crtc
->mode
.vdisplay
;
914 /* Background fill might be necessary when the plane has per-pixel
915 * alpha content or a non-opaque plane alpha and could blend from the
916 * background or does not cover the entire screen.
918 vc4_state
->needs_bg_fill
= fb
->format
->has_alpha
|| !covers_screen
||
919 state
->alpha
!= DRM_BLEND_ALPHA_OPAQUE
;
921 /* Flag the dlist as initialized to avoid checking it twice in case
922 * the async update check already called vc4_plane_mode_set() and
923 * decided to fallback to sync update because async update was not
926 vc4_state
->dlist_initialized
= 1;
928 vc4_plane_calc_load(state
);
933 /* If a modeset involves changing the setup of a plane, the atomic
934 * infrastructure will call this to validate a proposed plane setup.
935 * However, if a plane isn't getting updated, this (and the
936 * corresponding vc4_plane_atomic_update) won't get called. Thus, we
937 * compute the dlist here and have all active plane dlists get updated
938 * in the CRTC's flush.
940 static int vc4_plane_atomic_check(struct drm_plane
*plane
,
941 struct drm_plane_state
*state
)
943 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(state
);
946 vc4_state
->dlist_count
= 0;
948 if (!plane_enabled(state
))
951 ret
= vc4_plane_mode_set(plane
, state
);
955 return vc4_plane_allocate_lbm(state
);
958 static void vc4_plane_atomic_update(struct drm_plane
*plane
,
959 struct drm_plane_state
*old_state
)
961 /* No contents here. Since we don't know where in the CRTC's
962 * dlist we should be stored, our dlist is uploaded to the
963 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
968 u32
vc4_plane_write_dlist(struct drm_plane
*plane
, u32 __iomem
*dlist
)
970 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(plane
->state
);
973 vc4_state
->hw_dlist
= dlist
;
975 /* Can't memcpy_toio() because it needs to be 32-bit writes. */
976 for (i
= 0; i
< vc4_state
->dlist_count
; i
++)
977 writel(vc4_state
->dlist
[i
], &dlist
[i
]);
979 return vc4_state
->dlist_count
;
982 u32
vc4_plane_dlist_size(const struct drm_plane_state
*state
)
984 const struct vc4_plane_state
*vc4_state
=
985 container_of(state
, typeof(*vc4_state
), base
);
987 return vc4_state
->dlist_count
;
990 /* Updates the plane to immediately (well, once the FIFO needs
991 * refilling) scan out from at a new framebuffer.
993 void vc4_plane_async_set_fb(struct drm_plane
*plane
, struct drm_framebuffer
*fb
)
995 struct vc4_plane_state
*vc4_state
= to_vc4_plane_state(plane
->state
);
996 struct drm_gem_cma_object
*bo
= drm_fb_cma_get_gem_obj(fb
, 0);
999 /* We're skipping the address adjustment for negative origin,
1000 * because this is only called on the primary plane.
1002 WARN_ON_ONCE(plane
->state
->crtc_x
< 0 || plane
->state
->crtc_y
< 0);
1003 addr
= bo
->paddr
+ fb
->offsets
[0];
1005 /* Write the new address into the hardware immediately. The
1006 * scanout will start from this address as soon as the FIFO
1007 * needs to refill with pixels.
1009 writel(addr
, &vc4_state
->hw_dlist
[vc4_state
->ptr0_offset
]);
1011 /* Also update the CPU-side dlist copy, so that any later
1012 * atomic updates that don't do a new modeset on our plane
1013 * also use our updated address.
1015 vc4_state
->dlist
[vc4_state
->ptr0_offset
] = addr
;
1018 static void vc4_plane_atomic_async_update(struct drm_plane
*plane
,
1019 struct drm_plane_state
*state
)
1021 struct vc4_plane_state
*vc4_state
, *new_vc4_state
;
1023 swap(plane
->state
->fb
, state
->fb
);
1024 plane
->state
->crtc_x
= state
->crtc_x
;
1025 plane
->state
->crtc_y
= state
->crtc_y
;
1026 plane
->state
->crtc_w
= state
->crtc_w
;
1027 plane
->state
->crtc_h
= state
->crtc_h
;
1028 plane
->state
->src_x
= state
->src_x
;
1029 plane
->state
->src_y
= state
->src_y
;
1030 plane
->state
->src_w
= state
->src_w
;
1031 plane
->state
->src_h
= state
->src_h
;
1032 plane
->state
->src_h
= state
->src_h
;
1033 plane
->state
->alpha
= state
->alpha
;
1034 plane
->state
->pixel_blend_mode
= state
->pixel_blend_mode
;
1035 plane
->state
->rotation
= state
->rotation
;
1036 plane
->state
->zpos
= state
->zpos
;
1037 plane
->state
->normalized_zpos
= state
->normalized_zpos
;
1038 plane
->state
->color_encoding
= state
->color_encoding
;
1039 plane
->state
->color_range
= state
->color_range
;
1040 plane
->state
->src
= state
->src
;
1041 plane
->state
->dst
= state
->dst
;
1042 plane
->state
->visible
= state
->visible
;
1044 new_vc4_state
= to_vc4_plane_state(state
);
1045 vc4_state
= to_vc4_plane_state(plane
->state
);
1047 vc4_state
->crtc_x
= new_vc4_state
->crtc_x
;
1048 vc4_state
->crtc_y
= new_vc4_state
->crtc_y
;
1049 vc4_state
->crtc_h
= new_vc4_state
->crtc_h
;
1050 vc4_state
->crtc_w
= new_vc4_state
->crtc_w
;
1051 vc4_state
->src_x
= new_vc4_state
->src_x
;
1052 vc4_state
->src_y
= new_vc4_state
->src_y
;
1053 memcpy(vc4_state
->src_w
, new_vc4_state
->src_w
,
1054 sizeof(vc4_state
->src_w
));
1055 memcpy(vc4_state
->src_h
, new_vc4_state
->src_h
,
1056 sizeof(vc4_state
->src_h
));
1057 memcpy(vc4_state
->x_scaling
, new_vc4_state
->x_scaling
,
1058 sizeof(vc4_state
->x_scaling
));
1059 memcpy(vc4_state
->y_scaling
, new_vc4_state
->y_scaling
,
1060 sizeof(vc4_state
->y_scaling
));
1061 vc4_state
->is_unity
= new_vc4_state
->is_unity
;
1062 vc4_state
->is_yuv
= new_vc4_state
->is_yuv
;
1063 memcpy(vc4_state
->offsets
, new_vc4_state
->offsets
,
1064 sizeof(vc4_state
->offsets
));
1065 vc4_state
->needs_bg_fill
= new_vc4_state
->needs_bg_fill
;
1067 /* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */
1068 vc4_state
->dlist
[vc4_state
->pos0_offset
] =
1069 new_vc4_state
->dlist
[vc4_state
->pos0_offset
];
1070 vc4_state
->dlist
[vc4_state
->pos2_offset
] =
1071 new_vc4_state
->dlist
[vc4_state
->pos2_offset
];
1072 vc4_state
->dlist
[vc4_state
->ptr0_offset
] =
1073 new_vc4_state
->dlist
[vc4_state
->ptr0_offset
];
1075 /* Note that we can't just call vc4_plane_write_dlist()
1076 * because that would smash the context data that the HVS is
1079 writel(vc4_state
->dlist
[vc4_state
->pos0_offset
],
1080 &vc4_state
->hw_dlist
[vc4_state
->pos0_offset
]);
1081 writel(vc4_state
->dlist
[vc4_state
->pos2_offset
],
1082 &vc4_state
->hw_dlist
[vc4_state
->pos2_offset
]);
1083 writel(vc4_state
->dlist
[vc4_state
->ptr0_offset
],
1084 &vc4_state
->hw_dlist
[vc4_state
->ptr0_offset
]);
1087 static int vc4_plane_atomic_async_check(struct drm_plane
*plane
,
1088 struct drm_plane_state
*state
)
1090 struct vc4_plane_state
*old_vc4_state
, *new_vc4_state
;
1094 ret
= vc4_plane_mode_set(plane
, state
);
1098 old_vc4_state
= to_vc4_plane_state(plane
->state
);
1099 new_vc4_state
= to_vc4_plane_state(state
);
1100 if (old_vc4_state
->dlist_count
!= new_vc4_state
->dlist_count
||
1101 old_vc4_state
->pos0_offset
!= new_vc4_state
->pos0_offset
||
1102 old_vc4_state
->pos2_offset
!= new_vc4_state
->pos2_offset
||
1103 old_vc4_state
->ptr0_offset
!= new_vc4_state
->ptr0_offset
||
1104 vc4_lbm_size(plane
->state
) != vc4_lbm_size(state
))
1107 /* Only pos0, pos2 and ptr0 DWORDS can be updated in an async update
1108 * if anything else has changed, fallback to a sync update.
1110 for (i
= 0; i
< new_vc4_state
->dlist_count
; i
++) {
1111 if (i
== new_vc4_state
->pos0_offset
||
1112 i
== new_vc4_state
->pos2_offset
||
1113 i
== new_vc4_state
->ptr0_offset
||
1114 (new_vc4_state
->lbm_offset
&&
1115 i
== new_vc4_state
->lbm_offset
))
1118 if (new_vc4_state
->dlist
[i
] != old_vc4_state
->dlist
[i
])
1125 static int vc4_prepare_fb(struct drm_plane
*plane
,
1126 struct drm_plane_state
*state
)
1134 bo
= to_vc4_bo(&drm_fb_cma_get_gem_obj(state
->fb
, 0)->base
);
1136 drm_gem_fb_prepare_fb(plane
, state
);
1138 if (plane
->state
->fb
== state
->fb
)
1141 ret
= vc4_bo_inc_usecnt(bo
);
1148 static void vc4_cleanup_fb(struct drm_plane
*plane
,
1149 struct drm_plane_state
*state
)
1153 if (plane
->state
->fb
== state
->fb
|| !state
->fb
)
1156 bo
= to_vc4_bo(&drm_fb_cma_get_gem_obj(state
->fb
, 0)->base
);
1157 vc4_bo_dec_usecnt(bo
);
1160 static const struct drm_plane_helper_funcs vc4_plane_helper_funcs
= {
1161 .atomic_check
= vc4_plane_atomic_check
,
1162 .atomic_update
= vc4_plane_atomic_update
,
1163 .prepare_fb
= vc4_prepare_fb
,
1164 .cleanup_fb
= vc4_cleanup_fb
,
1165 .atomic_async_check
= vc4_plane_atomic_async_check
,
1166 .atomic_async_update
= vc4_plane_atomic_async_update
,
1169 static void vc4_plane_destroy(struct drm_plane
*plane
)
1171 drm_plane_cleanup(plane
);
1174 static bool vc4_format_mod_supported(struct drm_plane
*plane
,
1178 /* Support T_TILING for RGB formats only. */
1180 case DRM_FORMAT_XRGB8888
:
1181 case DRM_FORMAT_ARGB8888
:
1182 case DRM_FORMAT_ABGR8888
:
1183 case DRM_FORMAT_XBGR8888
:
1184 case DRM_FORMAT_RGB565
:
1185 case DRM_FORMAT_BGR565
:
1186 case DRM_FORMAT_ARGB1555
:
1187 case DRM_FORMAT_XRGB1555
:
1188 switch (fourcc_mod_broadcom_mod(modifier
)) {
1189 case DRM_FORMAT_MOD_LINEAR
:
1190 case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED
:
1195 case DRM_FORMAT_NV12
:
1196 case DRM_FORMAT_NV21
:
1197 switch (fourcc_mod_broadcom_mod(modifier
)) {
1198 case DRM_FORMAT_MOD_LINEAR
:
1199 case DRM_FORMAT_MOD_BROADCOM_SAND64
:
1200 case DRM_FORMAT_MOD_BROADCOM_SAND128
:
1201 case DRM_FORMAT_MOD_BROADCOM_SAND256
:
1206 case DRM_FORMAT_YUV422
:
1207 case DRM_FORMAT_YVU422
:
1208 case DRM_FORMAT_YUV420
:
1209 case DRM_FORMAT_YVU420
:
1210 case DRM_FORMAT_NV16
:
1211 case DRM_FORMAT_NV61
:
1213 return (modifier
== DRM_FORMAT_MOD_LINEAR
);
1217 static const struct drm_plane_funcs vc4_plane_funcs
= {
1218 .update_plane
= drm_atomic_helper_update_plane
,
1219 .disable_plane
= drm_atomic_helper_disable_plane
,
1220 .destroy
= vc4_plane_destroy
,
1221 .set_property
= NULL
,
1222 .reset
= vc4_plane_reset
,
1223 .atomic_duplicate_state
= vc4_plane_duplicate_state
,
1224 .atomic_destroy_state
= vc4_plane_destroy_state
,
1225 .format_mod_supported
= vc4_format_mod_supported
,
1228 struct drm_plane
*vc4_plane_init(struct drm_device
*dev
,
1229 enum drm_plane_type type
)
1231 struct drm_plane
*plane
= NULL
;
1232 struct vc4_plane
*vc4_plane
;
1233 u32 formats
[ARRAY_SIZE(hvs_formats
)];
1236 static const uint64_t modifiers
[] = {
1237 DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED
,
1238 DRM_FORMAT_MOD_BROADCOM_SAND128
,
1239 DRM_FORMAT_MOD_BROADCOM_SAND64
,
1240 DRM_FORMAT_MOD_BROADCOM_SAND256
,
1241 DRM_FORMAT_MOD_LINEAR
,
1242 DRM_FORMAT_MOD_INVALID
1245 vc4_plane
= devm_kzalloc(dev
->dev
, sizeof(*vc4_plane
),
1248 return ERR_PTR(-ENOMEM
);
1250 for (i
= 0; i
< ARRAY_SIZE(hvs_formats
); i
++)
1251 formats
[i
] = hvs_formats
[i
].drm
;
1253 plane
= &vc4_plane
->base
;
1254 ret
= drm_universal_plane_init(dev
, plane
, 0,
1256 formats
, ARRAY_SIZE(formats
),
1257 modifiers
, type
, NULL
);
1259 drm_plane_helper_add(plane
, &vc4_plane_helper_funcs
);
1261 drm_plane_create_alpha_property(plane
);
1262 drm_plane_create_rotation_property(plane
, DRM_MODE_ROTATE_0
,
1264 DRM_MODE_ROTATE_180
|
1265 DRM_MODE_REFLECT_X
|
1266 DRM_MODE_REFLECT_Y
);