Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cris-mirror.git] / drivers / gpu / drm / vc4 / vc4_plane.c
blob515f979976240bd03bcb58f24f20cd5d7d596c5e
1 /*
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.
7 */
9 /**
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>
26 #include "uapi/drm/vc4_drm.h"
27 #include "vc4_drv.h"
28 #include "vc4_regs.h"
30 enum vc4_scaling_mode {
31 VC4_SCALING_NONE,
32 VC4_SCALING_TPZ,
33 VC4_SCALING_PPF,
36 struct vc4_plane_state {
37 struct drm_plane_state base;
38 /* System memory copy of the display list for this element, computed
39 * at atomic_check time.
41 u32 *dlist;
42 u32 dlist_size; /* Number of dwords allocated for the display list */
43 u32 dlist_count; /* Number of used dwords in the display list. */
45 /* Offset in the dlist to various words, for pageflip or
46 * cursor updates.
48 u32 pos0_offset;
49 u32 pos2_offset;
50 u32 ptr0_offset;
52 /* Offset where the plane's dlist was last stored in the
53 * hardware at vc4_crtc_atomic_flush() time.
55 u32 __iomem *hw_dlist;
57 /* Clipped coordinates of the plane on the display. */
58 int crtc_x, crtc_y, crtc_w, crtc_h;
59 /* Clipped area being scanned from in the FB. */
60 u32 src_x, src_y;
62 u32 src_w[2], src_h[2];
64 /* Scaling selection for the RGB/Y plane and the Cb/Cr planes. */
65 enum vc4_scaling_mode x_scaling[2], y_scaling[2];
66 bool is_unity;
67 bool is_yuv;
69 /* Offset to start scanning out from the start of the plane's
70 * BO.
72 u32 offsets[3];
74 /* Our allocation in LBM for temporary storage during scaling. */
75 struct drm_mm_node lbm;
78 static inline struct vc4_plane_state *
79 to_vc4_plane_state(struct drm_plane_state *state)
81 return (struct vc4_plane_state *)state;
84 static const struct hvs_format {
85 u32 drm; /* DRM_FORMAT_* */
86 u32 hvs; /* HVS_FORMAT_* */
87 u32 pixel_order;
88 bool has_alpha;
89 } hvs_formats[] = {
91 .drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
92 .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
95 .drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
96 .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
99 .drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
100 .pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = true,
103 .drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
104 .pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = false,
107 .drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
108 .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
111 .drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565,
112 .pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false,
115 .drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
116 .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
119 .drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
120 .pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
123 .drm = DRM_FORMAT_RGB888, .hvs = HVS_PIXEL_FORMAT_RGB888,
124 .pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
127 .drm = DRM_FORMAT_BGR888, .hvs = HVS_PIXEL_FORMAT_RGB888,
128 .pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false,
131 .drm = DRM_FORMAT_YUV422,
132 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
133 .pixel_order = HVS_PIXEL_ORDER_XYCBCR,
136 .drm = DRM_FORMAT_YVU422,
137 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
138 .pixel_order = HVS_PIXEL_ORDER_XYCRCB,
141 .drm = DRM_FORMAT_YUV420,
142 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
143 .pixel_order = HVS_PIXEL_ORDER_XYCBCR,
146 .drm = DRM_FORMAT_YVU420,
147 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
148 .pixel_order = HVS_PIXEL_ORDER_XYCRCB,
151 .drm = DRM_FORMAT_NV12,
152 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
153 .pixel_order = HVS_PIXEL_ORDER_XYCBCR,
156 .drm = DRM_FORMAT_NV21,
157 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
158 .pixel_order = HVS_PIXEL_ORDER_XYCRCB,
161 .drm = DRM_FORMAT_NV16,
162 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
163 .pixel_order = HVS_PIXEL_ORDER_XYCBCR,
166 .drm = DRM_FORMAT_NV61,
167 .hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
168 .pixel_order = HVS_PIXEL_ORDER_XYCRCB,
172 static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
174 unsigned i;
176 for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
177 if (hvs_formats[i].drm == drm_format)
178 return &hvs_formats[i];
181 return NULL;
184 static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst)
186 if (dst > src)
187 return VC4_SCALING_PPF;
188 else if (dst < src)
189 return VC4_SCALING_TPZ;
190 else
191 return VC4_SCALING_NONE;
194 static bool plane_enabled(struct drm_plane_state *state)
196 return state->fb && state->crtc;
199 static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
201 struct vc4_plane_state *vc4_state;
203 if (WARN_ON(!plane->state))
204 return NULL;
206 vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
207 if (!vc4_state)
208 return NULL;
210 memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
212 __drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
214 if (vc4_state->dlist) {
215 vc4_state->dlist = kmemdup(vc4_state->dlist,
216 vc4_state->dlist_count * 4,
217 GFP_KERNEL);
218 if (!vc4_state->dlist) {
219 kfree(vc4_state);
220 return NULL;
222 vc4_state->dlist_size = vc4_state->dlist_count;
225 return &vc4_state->base;
228 static void vc4_plane_destroy_state(struct drm_plane *plane,
229 struct drm_plane_state *state)
231 struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
232 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
234 if (vc4_state->lbm.allocated) {
235 unsigned long irqflags;
237 spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
238 drm_mm_remove_node(&vc4_state->lbm);
239 spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
242 kfree(vc4_state->dlist);
243 __drm_atomic_helper_plane_destroy_state(&vc4_state->base);
244 kfree(state);
247 /* Called during init to allocate the plane's atomic state. */
248 static void vc4_plane_reset(struct drm_plane *plane)
250 struct vc4_plane_state *vc4_state;
252 WARN_ON(plane->state);
254 vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
255 if (!vc4_state)
256 return;
258 plane->state = &vc4_state->base;
259 vc4_state->base.plane = plane;
262 static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
264 if (vc4_state->dlist_count == vc4_state->dlist_size) {
265 u32 new_size = max(4u, vc4_state->dlist_count * 2);
266 u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
268 if (!new_dlist)
269 return;
270 memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
272 kfree(vc4_state->dlist);
273 vc4_state->dlist = new_dlist;
274 vc4_state->dlist_size = new_size;
277 vc4_state->dlist[vc4_state->dlist_count++] = val;
280 /* Returns the scl0/scl1 field based on whether the dimensions need to
281 * be up/down/non-scaled.
283 * This is a replication of a table from the spec.
285 static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane)
287 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
289 switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) {
290 case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF:
291 return SCALER_CTL0_SCL_H_PPF_V_PPF;
292 case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF:
293 return SCALER_CTL0_SCL_H_TPZ_V_PPF;
294 case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ:
295 return SCALER_CTL0_SCL_H_PPF_V_TPZ;
296 case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ:
297 return SCALER_CTL0_SCL_H_TPZ_V_TPZ;
298 case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE:
299 return SCALER_CTL0_SCL_H_PPF_V_NONE;
300 case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF:
301 return SCALER_CTL0_SCL_H_NONE_V_PPF;
302 case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ:
303 return SCALER_CTL0_SCL_H_NONE_V_TPZ;
304 case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE:
305 return SCALER_CTL0_SCL_H_TPZ_V_NONE;
306 default:
307 case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE:
308 /* The unity case is independently handled by
309 * SCALER_CTL0_UNITY.
311 return 0;
315 static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
317 struct drm_plane *plane = state->plane;
318 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
319 struct drm_framebuffer *fb = state->fb;
320 struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
321 u32 subpixel_src_mask = (1 << 16) - 1;
322 u32 format = fb->format->format;
323 int num_planes = fb->format->num_planes;
324 u32 h_subsample = 1;
325 u32 v_subsample = 1;
326 int i;
328 for (i = 0; i < num_planes; i++)
329 vc4_state->offsets[i] = bo->paddr + fb->offsets[i];
331 /* We don't support subpixel source positioning for scaling. */
332 if ((state->src_x & subpixel_src_mask) ||
333 (state->src_y & subpixel_src_mask) ||
334 (state->src_w & subpixel_src_mask) ||
335 (state->src_h & subpixel_src_mask)) {
336 return -EINVAL;
339 vc4_state->src_x = state->src_x >> 16;
340 vc4_state->src_y = state->src_y >> 16;
341 vc4_state->src_w[0] = state->src_w >> 16;
342 vc4_state->src_h[0] = state->src_h >> 16;
344 vc4_state->crtc_x = state->crtc_x;
345 vc4_state->crtc_y = state->crtc_y;
346 vc4_state->crtc_w = state->crtc_w;
347 vc4_state->crtc_h = state->crtc_h;
349 vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],
350 vc4_state->crtc_w);
351 vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0],
352 vc4_state->crtc_h);
354 if (num_planes > 1) {
355 vc4_state->is_yuv = true;
357 h_subsample = drm_format_horz_chroma_subsampling(format);
358 v_subsample = drm_format_vert_chroma_subsampling(format);
359 vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample;
360 vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample;
362 vc4_state->x_scaling[1] =
363 vc4_get_scaling_mode(vc4_state->src_w[1],
364 vc4_state->crtc_w);
365 vc4_state->y_scaling[1] =
366 vc4_get_scaling_mode(vc4_state->src_h[1],
367 vc4_state->crtc_h);
369 /* YUV conversion requires that scaling be enabled,
370 * even on a plane that's otherwise 1:1. Choose TPZ
371 * for simplicity.
373 if (vc4_state->x_scaling[0] == VC4_SCALING_NONE)
374 vc4_state->x_scaling[0] = VC4_SCALING_TPZ;
375 if (vc4_state->y_scaling[0] == VC4_SCALING_NONE)
376 vc4_state->y_scaling[0] = VC4_SCALING_TPZ;
379 vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE &&
380 vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
381 vc4_state->x_scaling[1] == VC4_SCALING_NONE &&
382 vc4_state->y_scaling[1] == VC4_SCALING_NONE);
384 /* No configuring scaling on the cursor plane, since it gets
385 non-vblank-synced updates, and scaling requires requires
386 LBM changes which have to be vblank-synced.
388 if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity)
389 return -EINVAL;
391 /* Clamp the on-screen start x/y to 0. The hardware doesn't
392 * support negative y, and negative x wastes bandwidth.
394 if (vc4_state->crtc_x < 0) {
395 for (i = 0; i < num_planes; i++) {
396 u32 cpp = fb->format->cpp[i];
397 u32 subs = ((i == 0) ? 1 : h_subsample);
399 vc4_state->offsets[i] += (cpp *
400 (-vc4_state->crtc_x) / subs);
402 vc4_state->src_w[0] += vc4_state->crtc_x;
403 vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample;
404 vc4_state->crtc_x = 0;
407 if (vc4_state->crtc_y < 0) {
408 for (i = 0; i < num_planes; i++) {
409 u32 subs = ((i == 0) ? 1 : v_subsample);
411 vc4_state->offsets[i] += (fb->pitches[i] *
412 (-vc4_state->crtc_y) / subs);
414 vc4_state->src_h[0] += vc4_state->crtc_y;
415 vc4_state->src_h[1] += vc4_state->crtc_y / v_subsample;
416 vc4_state->crtc_y = 0;
419 return 0;
422 static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
424 u32 scale, recip;
426 scale = (1 << 16) * src / dst;
428 /* The specs note that while the reciprocal would be defined
429 * as (1<<32)/scale, ~0 is close enough.
431 recip = ~0 / scale;
433 vc4_dlist_write(vc4_state,
434 VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) |
435 VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE));
436 vc4_dlist_write(vc4_state,
437 VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP));
440 static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
442 u32 scale = (1 << 16) * src / dst;
444 vc4_dlist_write(vc4_state,
445 SCALER_PPF_AGC |
446 VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
447 VC4_SET_FIELD(0, SCALER_PPF_IPHASE));
450 static u32 vc4_lbm_size(struct drm_plane_state *state)
452 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
453 /* This is the worst case number. One of the two sizes will
454 * be used depending on the scaling configuration.
456 u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w);
457 u32 lbm;
459 if (!vc4_state->is_yuv) {
460 if (vc4_state->is_unity)
461 return 0;
462 else if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ)
463 lbm = pix_per_line * 8;
464 else {
465 /* In special cases, this multiplier might be 12. */
466 lbm = pix_per_line * 16;
468 } else {
469 /* There are cases for this going down to a multiplier
470 * of 2, but according to the firmware source, the
471 * table in the docs is somewhat wrong.
473 lbm = pix_per_line * 16;
476 lbm = roundup(lbm, 32);
478 return lbm;
481 static void vc4_write_scaling_parameters(struct drm_plane_state *state,
482 int channel)
484 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
486 /* Ch0 H-PPF Word 0: Scaling Parameters */
487 if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) {
488 vc4_write_ppf(vc4_state,
489 vc4_state->src_w[channel], vc4_state->crtc_w);
492 /* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */
493 if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) {
494 vc4_write_ppf(vc4_state,
495 vc4_state->src_h[channel], vc4_state->crtc_h);
496 vc4_dlist_write(vc4_state, 0xc0c0c0c0);
499 /* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */
500 if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) {
501 vc4_write_tpz(vc4_state,
502 vc4_state->src_w[channel], vc4_state->crtc_w);
505 /* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */
506 if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) {
507 vc4_write_tpz(vc4_state,
508 vc4_state->src_h[channel], vc4_state->crtc_h);
509 vc4_dlist_write(vc4_state, 0xc0c0c0c0);
513 /* Writes out a full display list for an active plane to the plane's
514 * private dlist state.
516 static int vc4_plane_mode_set(struct drm_plane *plane,
517 struct drm_plane_state *state)
519 struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
520 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
521 struct drm_framebuffer *fb = state->fb;
522 u32 ctl0_offset = vc4_state->dlist_count;
523 const struct hvs_format *format = vc4_get_hvs_format(fb->format->format);
524 int num_planes = drm_format_num_planes(format->drm);
525 u32 scl0, scl1, pitch0;
526 u32 lbm_size, tiling;
527 unsigned long irqflags;
528 int ret, i;
530 ret = vc4_plane_setup_clipping_and_scaling(state);
531 if (ret)
532 return ret;
534 /* Allocate the LBM memory that the HVS will use for temporary
535 * storage due to our scaling/format conversion.
537 lbm_size = vc4_lbm_size(state);
538 if (lbm_size) {
539 if (!vc4_state->lbm.allocated) {
540 spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
541 ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
542 &vc4_state->lbm,
543 lbm_size, 32, 0, 0);
544 spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
545 } else {
546 WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
550 if (ret)
551 return ret;
553 /* SCL1 is used for Cb/Cr scaling of planar formats. For RGB
554 * and 4:4:4, scl1 should be set to scl0 so both channels of
555 * the scaler do the same thing. For YUV, the Y plane needs
556 * to be put in channel 1 and Cb/Cr in channel 0, so we swap
557 * the scl fields here.
559 if (num_planes == 1) {
560 scl0 = vc4_get_scl_field(state, 1);
561 scl1 = scl0;
562 } else {
563 scl0 = vc4_get_scl_field(state, 1);
564 scl1 = vc4_get_scl_field(state, 0);
567 switch (fb->modifier) {
568 case DRM_FORMAT_MOD_LINEAR:
569 tiling = SCALER_CTL0_TILING_LINEAR;
570 pitch0 = VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH);
571 break;
573 case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: {
574 /* For T-tiled, the FB pitch is "how many bytes from
575 * one row to the next, such that pitch * tile_h ==
576 * tile_size * tiles_per_row."
578 u32 tile_size_shift = 12; /* T tiles are 4kb */
579 u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */
580 u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift);
582 tiling = SCALER_CTL0_TILING_256B_OR_T;
584 pitch0 = (VC4_SET_FIELD(0, SCALER_PITCH0_TILE_Y_OFFSET) |
585 VC4_SET_FIELD(0, SCALER_PITCH0_TILE_WIDTH_L) |
586 VC4_SET_FIELD(tiles_w, SCALER_PITCH0_TILE_WIDTH_R));
587 break;
590 default:
591 DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
592 (long long)fb->modifier);
593 return -EINVAL;
596 /* Control word */
597 vc4_dlist_write(vc4_state,
598 SCALER_CTL0_VALID |
599 (format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
600 (format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
601 VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
602 (vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
603 VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
604 VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
606 /* Position Word 0: Image Positions and Alpha Value */
607 vc4_state->pos0_offset = vc4_state->dlist_count;
608 vc4_dlist_write(vc4_state,
609 VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
610 VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
611 VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
613 /* Position Word 1: Scaled Image Dimensions. */
614 if (!vc4_state->is_unity) {
615 vc4_dlist_write(vc4_state,
616 VC4_SET_FIELD(vc4_state->crtc_w,
617 SCALER_POS1_SCL_WIDTH) |
618 VC4_SET_FIELD(vc4_state->crtc_h,
619 SCALER_POS1_SCL_HEIGHT));
622 /* Position Word 2: Source Image Size, Alpha Mode */
623 vc4_state->pos2_offset = vc4_state->dlist_count;
624 vc4_dlist_write(vc4_state,
625 VC4_SET_FIELD(format->has_alpha ?
626 SCALER_POS2_ALPHA_MODE_PIPELINE :
627 SCALER_POS2_ALPHA_MODE_FIXED,
628 SCALER_POS2_ALPHA_MODE) |
629 VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) |
630 VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT));
632 /* Position Word 3: Context. Written by the HVS. */
633 vc4_dlist_write(vc4_state, 0xc0c0c0c0);
636 /* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
638 * The pointers may be any byte address.
640 vc4_state->ptr0_offset = vc4_state->dlist_count;
641 for (i = 0; i < num_planes; i++)
642 vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
644 /* Pointer Context Word 0/1/2: Written by the HVS */
645 for (i = 0; i < num_planes; i++)
646 vc4_dlist_write(vc4_state, 0xc0c0c0c0);
648 /* Pitch word 0 */
649 vc4_dlist_write(vc4_state, pitch0);
651 /* Pitch word 1/2 */
652 for (i = 1; i < num_planes; i++) {
653 vc4_dlist_write(vc4_state,
654 VC4_SET_FIELD(fb->pitches[i], SCALER_SRC_PITCH));
657 /* Colorspace conversion words */
658 if (vc4_state->is_yuv) {
659 vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5);
660 vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5);
661 vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5);
664 if (!vc4_state->is_unity) {
665 /* LBM Base Address. */
666 if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
667 vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
668 vc4_dlist_write(vc4_state, vc4_state->lbm.start);
671 if (num_planes > 1) {
672 /* Emit Cb/Cr as channel 0 and Y as channel
673 * 1. This matches how we set up scl0/scl1
674 * above.
676 vc4_write_scaling_parameters(state, 1);
678 vc4_write_scaling_parameters(state, 0);
680 /* If any PPF setup was done, then all the kernel
681 * pointers get uploaded.
683 if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
684 vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
685 vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
686 vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
687 u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
688 SCALER_PPF_KERNEL_OFFSET);
690 /* HPPF plane 0 */
691 vc4_dlist_write(vc4_state, kernel);
692 /* VPPF plane 0 */
693 vc4_dlist_write(vc4_state, kernel);
694 /* HPPF plane 1 */
695 vc4_dlist_write(vc4_state, kernel);
696 /* VPPF plane 1 */
697 vc4_dlist_write(vc4_state, kernel);
701 vc4_state->dlist[ctl0_offset] |=
702 VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
704 return 0;
707 /* If a modeset involves changing the setup of a plane, the atomic
708 * infrastructure will call this to validate a proposed plane setup.
709 * However, if a plane isn't getting updated, this (and the
710 * corresponding vc4_plane_atomic_update) won't get called. Thus, we
711 * compute the dlist here and have all active plane dlists get updated
712 * in the CRTC's flush.
714 static int vc4_plane_atomic_check(struct drm_plane *plane,
715 struct drm_plane_state *state)
717 struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
719 vc4_state->dlist_count = 0;
721 if (plane_enabled(state))
722 return vc4_plane_mode_set(plane, state);
723 else
724 return 0;
727 static void vc4_plane_atomic_update(struct drm_plane *plane,
728 struct drm_plane_state *old_state)
730 /* No contents here. Since we don't know where in the CRTC's
731 * dlist we should be stored, our dlist is uploaded to the
732 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
733 * time.
737 u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
739 struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
740 int i;
742 vc4_state->hw_dlist = dlist;
744 /* Can't memcpy_toio() because it needs to be 32-bit writes. */
745 for (i = 0; i < vc4_state->dlist_count; i++)
746 writel(vc4_state->dlist[i], &dlist[i]);
748 return vc4_state->dlist_count;
751 u32 vc4_plane_dlist_size(const struct drm_plane_state *state)
753 const struct vc4_plane_state *vc4_state =
754 container_of(state, typeof(*vc4_state), base);
756 return vc4_state->dlist_count;
759 /* Updates the plane to immediately (well, once the FIFO needs
760 * refilling) scan out from at a new framebuffer.
762 void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
764 struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
765 struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
766 uint32_t addr;
768 /* We're skipping the address adjustment for negative origin,
769 * because this is only called on the primary plane.
771 WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
772 addr = bo->paddr + fb->offsets[0];
774 /* Write the new address into the hardware immediately. The
775 * scanout will start from this address as soon as the FIFO
776 * needs to refill with pixels.
778 writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
780 /* Also update the CPU-side dlist copy, so that any later
781 * atomic updates that don't do a new modeset on our plane
782 * also use our updated address.
784 vc4_state->dlist[vc4_state->ptr0_offset] = addr;
787 static int vc4_prepare_fb(struct drm_plane *plane,
788 struct drm_plane_state *state)
790 struct vc4_bo *bo;
791 struct dma_fence *fence;
792 int ret;
794 if ((plane->state->fb == state->fb) || !state->fb)
795 return 0;
797 bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base);
799 ret = vc4_bo_inc_usecnt(bo);
800 if (ret)
801 return ret;
803 fence = reservation_object_get_excl_rcu(bo->resv);
804 drm_atomic_set_fence_for_plane(state, fence);
806 return 0;
809 static void vc4_cleanup_fb(struct drm_plane *plane,
810 struct drm_plane_state *state)
812 struct vc4_bo *bo;
814 if (plane->state->fb == state->fb || !state->fb)
815 return;
817 bo = to_vc4_bo(&drm_fb_cma_get_gem_obj(state->fb, 0)->base);
818 vc4_bo_dec_usecnt(bo);
821 static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
822 .atomic_check = vc4_plane_atomic_check,
823 .atomic_update = vc4_plane_atomic_update,
824 .prepare_fb = vc4_prepare_fb,
825 .cleanup_fb = vc4_cleanup_fb,
828 static void vc4_plane_destroy(struct drm_plane *plane)
830 drm_plane_helper_disable(plane);
831 drm_plane_cleanup(plane);
834 /* Implements immediate (non-vblank-synced) updates of the cursor
835 * position, or falls back to the atomic helper otherwise.
837 static int
838 vc4_update_plane(struct drm_plane *plane,
839 struct drm_crtc *crtc,
840 struct drm_framebuffer *fb,
841 int crtc_x, int crtc_y,
842 unsigned int crtc_w, unsigned int crtc_h,
843 uint32_t src_x, uint32_t src_y,
844 uint32_t src_w, uint32_t src_h,
845 struct drm_modeset_acquire_ctx *ctx)
847 struct drm_plane_state *plane_state;
848 struct vc4_plane_state *vc4_state;
850 if (plane != crtc->cursor)
851 goto out;
853 plane_state = plane->state;
854 vc4_state = to_vc4_plane_state(plane_state);
856 if (!plane_state)
857 goto out;
859 /* No configuring new scaling in the fast path. */
860 if (crtc_w != plane_state->crtc_w ||
861 crtc_h != plane_state->crtc_h ||
862 src_w != plane_state->src_w ||
863 src_h != plane_state->src_h) {
864 goto out;
867 if (fb != plane_state->fb) {
868 drm_atomic_set_fb_for_plane(plane->state, fb);
869 vc4_plane_async_set_fb(plane, fb);
872 /* Set the cursor's position on the screen. This is the
873 * expected change from the drm_mode_cursor_universal()
874 * helper.
876 plane_state->crtc_x = crtc_x;
877 plane_state->crtc_y = crtc_y;
879 /* Allow changing the start position within the cursor BO, if
880 * that matters.
882 plane_state->src_x = src_x;
883 plane_state->src_y = src_y;
885 /* Update the display list based on the new crtc_x/y. */
886 vc4_plane_atomic_check(plane, plane_state);
888 /* Note that we can't just call vc4_plane_write_dlist()
889 * because that would smash the context data that the HVS is
890 * currently using.
892 writel(vc4_state->dlist[vc4_state->pos0_offset],
893 &vc4_state->hw_dlist[vc4_state->pos0_offset]);
894 writel(vc4_state->dlist[vc4_state->pos2_offset],
895 &vc4_state->hw_dlist[vc4_state->pos2_offset]);
896 writel(vc4_state->dlist[vc4_state->ptr0_offset],
897 &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
899 return 0;
901 out:
902 return drm_atomic_helper_update_plane(plane, crtc, fb,
903 crtc_x, crtc_y,
904 crtc_w, crtc_h,
905 src_x, src_y,
906 src_w, src_h,
907 ctx);
910 static const struct drm_plane_funcs vc4_plane_funcs = {
911 .update_plane = vc4_update_plane,
912 .disable_plane = drm_atomic_helper_disable_plane,
913 .destroy = vc4_plane_destroy,
914 .set_property = NULL,
915 .reset = vc4_plane_reset,
916 .atomic_duplicate_state = vc4_plane_duplicate_state,
917 .atomic_destroy_state = vc4_plane_destroy_state,
920 struct drm_plane *vc4_plane_init(struct drm_device *dev,
921 enum drm_plane_type type)
923 struct drm_plane *plane = NULL;
924 struct vc4_plane *vc4_plane;
925 u32 formats[ARRAY_SIZE(hvs_formats)];
926 u32 num_formats = 0;
927 int ret = 0;
928 unsigned i;
930 vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
931 GFP_KERNEL);
932 if (!vc4_plane)
933 return ERR_PTR(-ENOMEM);
935 for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
936 /* Don't allow YUV in cursor planes, since that means
937 * tuning on the scaler, which we don't allow for the
938 * cursor.
940 if (type != DRM_PLANE_TYPE_CURSOR ||
941 hvs_formats[i].hvs < HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE) {
942 formats[num_formats++] = hvs_formats[i].drm;
945 plane = &vc4_plane->base;
946 ret = drm_universal_plane_init(dev, plane, 0,
947 &vc4_plane_funcs,
948 formats, num_formats,
949 NULL, type, NULL);
951 drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
953 return plane;