xref: /openbmc/linux/drivers/gpu/drm/vc4/vc4_plane.c (revision 82003e04)
1 /*
2  * Copyright (C) 2015 Broadcom
3  *
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  */
8 
9 /**
10  * DOC: VC4 plane module
11  *
12  * Each DRM plane is a layer of pixels being scanned out by the HVS.
13  *
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.
19  */
20 
21 #include "vc4_drv.h"
22 #include "vc4_regs.h"
23 #include "drm_atomic_helper.h"
24 #include "drm_fb_cma_helper.h"
25 #include "drm_plane_helper.h"
26 
27 enum vc4_scaling_mode {
28 	VC4_SCALING_NONE,
29 	VC4_SCALING_TPZ,
30 	VC4_SCALING_PPF,
31 };
32 
33 struct vc4_plane_state {
34 	struct drm_plane_state base;
35 	/* System memory copy of the display list for this element, computed
36 	 * at atomic_check time.
37 	 */
38 	u32 *dlist;
39 	u32 dlist_size; /* Number of dwords allocated for the display list */
40 	u32 dlist_count; /* Number of used dwords in the display list. */
41 
42 	/* Offset in the dlist to various words, for pageflip or
43 	 * cursor updates.
44 	 */
45 	u32 pos0_offset;
46 	u32 pos2_offset;
47 	u32 ptr0_offset;
48 
49 	/* Offset where the plane's dlist was last stored in the
50 	 * hardware at vc4_crtc_atomic_flush() time.
51 	 */
52 	u32 __iomem *hw_dlist;
53 
54 	/* Clipped coordinates of the plane on the display. */
55 	int crtc_x, crtc_y, crtc_w, crtc_h;
56 	/* Clipped area being scanned from in the FB. */
57 	u32 src_x, src_y;
58 
59 	u32 src_w[2], src_h[2];
60 
61 	/* Scaling selection for the RGB/Y plane and the Cb/Cr planes. */
62 	enum vc4_scaling_mode x_scaling[2], y_scaling[2];
63 	bool is_unity;
64 	bool is_yuv;
65 
66 	/* Offset to start scanning out from the start of the plane's
67 	 * BO.
68 	 */
69 	u32 offsets[3];
70 
71 	/* Our allocation in LBM for temporary storage during scaling. */
72 	struct drm_mm_node lbm;
73 };
74 
75 static inline struct vc4_plane_state *
76 to_vc4_plane_state(struct drm_plane_state *state)
77 {
78 	return (struct vc4_plane_state *)state;
79 }
80 
81 static const struct hvs_format {
82 	u32 drm; /* DRM_FORMAT_* */
83 	u32 hvs; /* HVS_FORMAT_* */
84 	u32 pixel_order;
85 	bool has_alpha;
86 	bool flip_cbcr;
87 } hvs_formats[] = {
88 	{
89 		.drm = DRM_FORMAT_XRGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
90 		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
91 	},
92 	{
93 		.drm = DRM_FORMAT_ARGB8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
94 		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
95 	},
96 	{
97 		.drm = DRM_FORMAT_ABGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
98 		.pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = true,
99 	},
100 	{
101 		.drm = DRM_FORMAT_XBGR8888, .hvs = HVS_PIXEL_FORMAT_RGBA8888,
102 		.pixel_order = HVS_PIXEL_ORDER_ARGB, .has_alpha = false,
103 	},
104 	{
105 		.drm = DRM_FORMAT_RGB565, .hvs = HVS_PIXEL_FORMAT_RGB565,
106 		.pixel_order = HVS_PIXEL_ORDER_XRGB, .has_alpha = false,
107 	},
108 	{
109 		.drm = DRM_FORMAT_BGR565, .hvs = HVS_PIXEL_FORMAT_RGB565,
110 		.pixel_order = HVS_PIXEL_ORDER_XBGR, .has_alpha = false,
111 	},
112 	{
113 		.drm = DRM_FORMAT_ARGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
114 		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = true,
115 	},
116 	{
117 		.drm = DRM_FORMAT_XRGB1555, .hvs = HVS_PIXEL_FORMAT_RGBA5551,
118 		.pixel_order = HVS_PIXEL_ORDER_ABGR, .has_alpha = false,
119 	},
120 	{
121 		.drm = DRM_FORMAT_YUV422,
122 		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
123 	},
124 	{
125 		.drm = DRM_FORMAT_YVU422,
126 		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
127 		.flip_cbcr = true,
128 	},
129 	{
130 		.drm = DRM_FORMAT_YUV420,
131 		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
132 	},
133 	{
134 		.drm = DRM_FORMAT_YVU420,
135 		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
136 		.flip_cbcr = true,
137 	},
138 	{
139 		.drm = DRM_FORMAT_NV12,
140 		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
141 	},
142 	{
143 		.drm = DRM_FORMAT_NV16,
144 		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
145 	},
146 };
147 
148 static const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
149 {
150 	unsigned i;
151 
152 	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
153 		if (hvs_formats[i].drm == drm_format)
154 			return &hvs_formats[i];
155 	}
156 
157 	return NULL;
158 }
159 
160 static enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst)
161 {
162 	if (dst > src)
163 		return VC4_SCALING_PPF;
164 	else if (dst < src)
165 		return VC4_SCALING_TPZ;
166 	else
167 		return VC4_SCALING_NONE;
168 }
169 
170 static bool plane_enabled(struct drm_plane_state *state)
171 {
172 	return state->fb && state->crtc;
173 }
174 
175 static struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
176 {
177 	struct vc4_plane_state *vc4_state;
178 
179 	if (WARN_ON(!plane->state))
180 		return NULL;
181 
182 	vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
183 	if (!vc4_state)
184 		return NULL;
185 
186 	memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
187 
188 	__drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
189 
190 	if (vc4_state->dlist) {
191 		vc4_state->dlist = kmemdup(vc4_state->dlist,
192 					   vc4_state->dlist_count * 4,
193 					   GFP_KERNEL);
194 		if (!vc4_state->dlist) {
195 			kfree(vc4_state);
196 			return NULL;
197 		}
198 		vc4_state->dlist_size = vc4_state->dlist_count;
199 	}
200 
201 	return &vc4_state->base;
202 }
203 
204 static void vc4_plane_destroy_state(struct drm_plane *plane,
205 				    struct drm_plane_state *state)
206 {
207 	struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
208 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
209 
210 	if (vc4_state->lbm.allocated) {
211 		unsigned long irqflags;
212 
213 		spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
214 		drm_mm_remove_node(&vc4_state->lbm);
215 		spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
216 	}
217 
218 	kfree(vc4_state->dlist);
219 	__drm_atomic_helper_plane_destroy_state(&vc4_state->base);
220 	kfree(state);
221 }
222 
223 /* Called during init to allocate the plane's atomic state. */
224 static void vc4_plane_reset(struct drm_plane *plane)
225 {
226 	struct vc4_plane_state *vc4_state;
227 
228 	WARN_ON(plane->state);
229 
230 	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
231 	if (!vc4_state)
232 		return;
233 
234 	plane->state = &vc4_state->base;
235 	vc4_state->base.plane = plane;
236 }
237 
238 static void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
239 {
240 	if (vc4_state->dlist_count == vc4_state->dlist_size) {
241 		u32 new_size = max(4u, vc4_state->dlist_count * 2);
242 		u32 *new_dlist = kmalloc(new_size * 4, GFP_KERNEL);
243 
244 		if (!new_dlist)
245 			return;
246 		memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
247 
248 		kfree(vc4_state->dlist);
249 		vc4_state->dlist = new_dlist;
250 		vc4_state->dlist_size = new_size;
251 	}
252 
253 	vc4_state->dlist[vc4_state->dlist_count++] = val;
254 }
255 
256 /* Returns the scl0/scl1 field based on whether the dimensions need to
257  * be up/down/non-scaled.
258  *
259  * This is a replication of a table from the spec.
260  */
261 static u32 vc4_get_scl_field(struct drm_plane_state *state, int plane)
262 {
263 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
264 
265 	switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) {
266 	case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF:
267 		return SCALER_CTL0_SCL_H_PPF_V_PPF;
268 	case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF:
269 		return SCALER_CTL0_SCL_H_TPZ_V_PPF;
270 	case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ:
271 		return SCALER_CTL0_SCL_H_PPF_V_TPZ;
272 	case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ:
273 		return SCALER_CTL0_SCL_H_TPZ_V_TPZ;
274 	case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE:
275 		return SCALER_CTL0_SCL_H_PPF_V_NONE;
276 	case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF:
277 		return SCALER_CTL0_SCL_H_NONE_V_PPF;
278 	case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ:
279 		return SCALER_CTL0_SCL_H_NONE_V_TPZ;
280 	case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE:
281 		return SCALER_CTL0_SCL_H_TPZ_V_NONE;
282 	default:
283 	case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE:
284 		/* The unity case is independently handled by
285 		 * SCALER_CTL0_UNITY.
286 		 */
287 		return 0;
288 	}
289 }
290 
291 static int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
292 {
293 	struct drm_plane *plane = state->plane;
294 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
295 	struct drm_framebuffer *fb = state->fb;
296 	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
297 	u32 subpixel_src_mask = (1 << 16) - 1;
298 	u32 format = fb->pixel_format;
299 	int num_planes = drm_format_num_planes(format);
300 	u32 h_subsample = 1;
301 	u32 v_subsample = 1;
302 	int i;
303 
304 	for (i = 0; i < num_planes; i++)
305 		vc4_state->offsets[i] = bo->paddr + fb->offsets[i];
306 
307 	/* We don't support subpixel source positioning for scaling. */
308 	if ((state->src_x & subpixel_src_mask) ||
309 	    (state->src_y & subpixel_src_mask) ||
310 	    (state->src_w & subpixel_src_mask) ||
311 	    (state->src_h & subpixel_src_mask)) {
312 		return -EINVAL;
313 	}
314 
315 	vc4_state->src_x = state->src_x >> 16;
316 	vc4_state->src_y = state->src_y >> 16;
317 	vc4_state->src_w[0] = state->src_w >> 16;
318 	vc4_state->src_h[0] = state->src_h >> 16;
319 
320 	vc4_state->crtc_x = state->crtc_x;
321 	vc4_state->crtc_y = state->crtc_y;
322 	vc4_state->crtc_w = state->crtc_w;
323 	vc4_state->crtc_h = state->crtc_h;
324 
325 	vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],
326 						       vc4_state->crtc_w);
327 	vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0],
328 						       vc4_state->crtc_h);
329 
330 	if (num_planes > 1) {
331 		vc4_state->is_yuv = true;
332 
333 		h_subsample = drm_format_horz_chroma_subsampling(format);
334 		v_subsample = drm_format_vert_chroma_subsampling(format);
335 		vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample;
336 		vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample;
337 
338 		vc4_state->x_scaling[1] =
339 			vc4_get_scaling_mode(vc4_state->src_w[1],
340 					     vc4_state->crtc_w);
341 		vc4_state->y_scaling[1] =
342 			vc4_get_scaling_mode(vc4_state->src_h[1],
343 					     vc4_state->crtc_h);
344 
345 		/* YUV conversion requires that scaling be enabled,
346 		 * even on a plane that's otherwise 1:1.  Choose TPZ
347 		 * for simplicity.
348 		 */
349 		if (vc4_state->x_scaling[0] == VC4_SCALING_NONE)
350 			vc4_state->x_scaling[0] = VC4_SCALING_TPZ;
351 		if (vc4_state->y_scaling[0] == VC4_SCALING_NONE)
352 			vc4_state->y_scaling[0] = VC4_SCALING_TPZ;
353 	}
354 
355 	vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE &&
356 			       vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
357 			       vc4_state->x_scaling[1] == VC4_SCALING_NONE &&
358 			       vc4_state->y_scaling[1] == VC4_SCALING_NONE);
359 
360 	/* No configuring scaling on the cursor plane, since it gets
361 	   non-vblank-synced updates, and scaling requires requires
362 	   LBM changes which have to be vblank-synced.
363 	 */
364 	if (plane->type == DRM_PLANE_TYPE_CURSOR && !vc4_state->is_unity)
365 		return -EINVAL;
366 
367 	/* Clamp the on-screen start x/y to 0.  The hardware doesn't
368 	 * support negative y, and negative x wastes bandwidth.
369 	 */
370 	if (vc4_state->crtc_x < 0) {
371 		for (i = 0; i < num_planes; i++) {
372 			u32 cpp = drm_format_plane_cpp(fb->pixel_format, i);
373 			u32 subs = ((i == 0) ? 1 : h_subsample);
374 
375 			vc4_state->offsets[i] += (cpp *
376 						  (-vc4_state->crtc_x) / subs);
377 		}
378 		vc4_state->src_w[0] += vc4_state->crtc_x;
379 		vc4_state->src_w[1] += vc4_state->crtc_x / h_subsample;
380 		vc4_state->crtc_x = 0;
381 	}
382 
383 	if (vc4_state->crtc_y < 0) {
384 		for (i = 0; i < num_planes; i++) {
385 			u32 subs = ((i == 0) ? 1 : v_subsample);
386 
387 			vc4_state->offsets[i] += (fb->pitches[i] *
388 						  (-vc4_state->crtc_y) / subs);
389 		}
390 		vc4_state->src_h[0] += vc4_state->crtc_y;
391 		vc4_state->src_h[1] += vc4_state->crtc_y / v_subsample;
392 		vc4_state->crtc_y = 0;
393 	}
394 
395 	return 0;
396 }
397 
398 static void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
399 {
400 	u32 scale, recip;
401 
402 	scale = (1 << 16) * src / dst;
403 
404 	/* The specs note that while the reciprocal would be defined
405 	 * as (1<<32)/scale, ~0 is close enough.
406 	 */
407 	recip = ~0 / scale;
408 
409 	vc4_dlist_write(vc4_state,
410 			VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) |
411 			VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE));
412 	vc4_dlist_write(vc4_state,
413 			VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP));
414 }
415 
416 static void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
417 {
418 	u32 scale = (1 << 16) * src / dst;
419 
420 	vc4_dlist_write(vc4_state,
421 			SCALER_PPF_AGC |
422 			VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
423 			VC4_SET_FIELD(0, SCALER_PPF_IPHASE));
424 }
425 
426 static u32 vc4_lbm_size(struct drm_plane_state *state)
427 {
428 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
429 	/* This is the worst case number.  One of the two sizes will
430 	 * be used depending on the scaling configuration.
431 	 */
432 	u32 pix_per_line = max(vc4_state->src_w[0], (u32)vc4_state->crtc_w);
433 	u32 lbm;
434 
435 	if (!vc4_state->is_yuv) {
436 		if (vc4_state->is_unity)
437 			return 0;
438 		else if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ)
439 			lbm = pix_per_line * 8;
440 		else {
441 			/* In special cases, this multiplier might be 12. */
442 			lbm = pix_per_line * 16;
443 		}
444 	} else {
445 		/* There are cases for this going down to a multiplier
446 		 * of 2, but according to the firmware source, the
447 		 * table in the docs is somewhat wrong.
448 		 */
449 		lbm = pix_per_line * 16;
450 	}
451 
452 	lbm = roundup(lbm, 32);
453 
454 	return lbm;
455 }
456 
457 static void vc4_write_scaling_parameters(struct drm_plane_state *state,
458 					 int channel)
459 {
460 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
461 
462 	/* Ch0 H-PPF Word 0: Scaling Parameters */
463 	if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) {
464 		vc4_write_ppf(vc4_state,
465 			      vc4_state->src_w[channel], vc4_state->crtc_w);
466 	}
467 
468 	/* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */
469 	if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) {
470 		vc4_write_ppf(vc4_state,
471 			      vc4_state->src_h[channel], vc4_state->crtc_h);
472 		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
473 	}
474 
475 	/* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */
476 	if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) {
477 		vc4_write_tpz(vc4_state,
478 			      vc4_state->src_w[channel], vc4_state->crtc_w);
479 	}
480 
481 	/* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */
482 	if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) {
483 		vc4_write_tpz(vc4_state,
484 			      vc4_state->src_h[channel], vc4_state->crtc_h);
485 		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
486 	}
487 }
488 
489 /* Writes out a full display list for an active plane to the plane's
490  * private dlist state.
491  */
492 static int vc4_plane_mode_set(struct drm_plane *plane,
493 			      struct drm_plane_state *state)
494 {
495 	struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
496 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
497 	struct drm_framebuffer *fb = state->fb;
498 	u32 ctl0_offset = vc4_state->dlist_count;
499 	const struct hvs_format *format = vc4_get_hvs_format(fb->pixel_format);
500 	int num_planes = drm_format_num_planes(format->drm);
501 	u32 scl0, scl1;
502 	u32 lbm_size;
503 	unsigned long irqflags;
504 	int ret, i;
505 
506 	ret = vc4_plane_setup_clipping_and_scaling(state);
507 	if (ret)
508 		return ret;
509 
510 	/* Allocate the LBM memory that the HVS will use for temporary
511 	 * storage due to our scaling/format conversion.
512 	 */
513 	lbm_size = vc4_lbm_size(state);
514 	if (lbm_size) {
515 		if (!vc4_state->lbm.allocated) {
516 			spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
517 			ret = drm_mm_insert_node(&vc4->hvs->lbm_mm,
518 						 &vc4_state->lbm,
519 						 lbm_size, 32, 0);
520 			spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
521 		} else {
522 			WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
523 		}
524 	}
525 
526 	if (ret)
527 		return ret;
528 
529 	/* SCL1 is used for Cb/Cr scaling of planar formats.  For RGB
530 	 * and 4:4:4, scl1 should be set to scl0 so both channels of
531 	 * the scaler do the same thing.  For YUV, the Y plane needs
532 	 * to be put in channel 1 and Cb/Cr in channel 0, so we swap
533 	 * the scl fields here.
534 	 */
535 	if (num_planes == 1) {
536 		scl0 = vc4_get_scl_field(state, 1);
537 		scl1 = scl0;
538 	} else {
539 		scl0 = vc4_get_scl_field(state, 1);
540 		scl1 = vc4_get_scl_field(state, 0);
541 	}
542 
543 	/* Control word */
544 	vc4_dlist_write(vc4_state,
545 			SCALER_CTL0_VALID |
546 			(format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
547 			(format->hvs << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
548 			(vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
549 			VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
550 			VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
551 
552 	/* Position Word 0: Image Positions and Alpha Value */
553 	vc4_state->pos0_offset = vc4_state->dlist_count;
554 	vc4_dlist_write(vc4_state,
555 			VC4_SET_FIELD(0xff, SCALER_POS0_FIXED_ALPHA) |
556 			VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
557 			VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
558 
559 	/* Position Word 1: Scaled Image Dimensions. */
560 	if (!vc4_state->is_unity) {
561 		vc4_dlist_write(vc4_state,
562 				VC4_SET_FIELD(vc4_state->crtc_w,
563 					      SCALER_POS1_SCL_WIDTH) |
564 				VC4_SET_FIELD(vc4_state->crtc_h,
565 					      SCALER_POS1_SCL_HEIGHT));
566 	}
567 
568 	/* Position Word 2: Source Image Size, Alpha Mode */
569 	vc4_state->pos2_offset = vc4_state->dlist_count;
570 	vc4_dlist_write(vc4_state,
571 			VC4_SET_FIELD(format->has_alpha ?
572 				      SCALER_POS2_ALPHA_MODE_PIPELINE :
573 				      SCALER_POS2_ALPHA_MODE_FIXED,
574 				      SCALER_POS2_ALPHA_MODE) |
575 			VC4_SET_FIELD(vc4_state->src_w[0], SCALER_POS2_WIDTH) |
576 			VC4_SET_FIELD(vc4_state->src_h[0], SCALER_POS2_HEIGHT));
577 
578 	/* Position Word 3: Context.  Written by the HVS. */
579 	vc4_dlist_write(vc4_state, 0xc0c0c0c0);
580 
581 
582 	/* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
583 	 *
584 	 * The pointers may be any byte address.
585 	 */
586 	vc4_state->ptr0_offset = vc4_state->dlist_count;
587 	if (!format->flip_cbcr) {
588 		for (i = 0; i < num_planes; i++)
589 			vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
590 	} else {
591 		WARN_ON_ONCE(num_planes != 3);
592 		vc4_dlist_write(vc4_state, vc4_state->offsets[0]);
593 		vc4_dlist_write(vc4_state, vc4_state->offsets[2]);
594 		vc4_dlist_write(vc4_state, vc4_state->offsets[1]);
595 	}
596 
597 	/* Pointer Context Word 0/1/2: Written by the HVS */
598 	for (i = 0; i < num_planes; i++)
599 		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
600 
601 	/* Pitch word 0/1/2 */
602 	for (i = 0; i < num_planes; i++) {
603 		vc4_dlist_write(vc4_state,
604 				VC4_SET_FIELD(fb->pitches[i], SCALER_SRC_PITCH));
605 	}
606 
607 	/* Colorspace conversion words */
608 	if (vc4_state->is_yuv) {
609 		vc4_dlist_write(vc4_state, SCALER_CSC0_ITR_R_601_5);
610 		vc4_dlist_write(vc4_state, SCALER_CSC1_ITR_R_601_5);
611 		vc4_dlist_write(vc4_state, SCALER_CSC2_ITR_R_601_5);
612 	}
613 
614 	if (!vc4_state->is_unity) {
615 		/* LBM Base Address. */
616 		if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
617 		    vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
618 			vc4_dlist_write(vc4_state, vc4_state->lbm.start);
619 		}
620 
621 		if (num_planes > 1) {
622 			/* Emit Cb/Cr as channel 0 and Y as channel
623 			 * 1. This matches how we set up scl0/scl1
624 			 * above.
625 			 */
626 			vc4_write_scaling_parameters(state, 1);
627 		}
628 		vc4_write_scaling_parameters(state, 0);
629 
630 		/* If any PPF setup was done, then all the kernel
631 		 * pointers get uploaded.
632 		 */
633 		if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
634 		    vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
635 		    vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
636 		    vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
637 			u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
638 						   SCALER_PPF_KERNEL_OFFSET);
639 
640 			/* HPPF plane 0 */
641 			vc4_dlist_write(vc4_state, kernel);
642 			/* VPPF plane 0 */
643 			vc4_dlist_write(vc4_state, kernel);
644 			/* HPPF plane 1 */
645 			vc4_dlist_write(vc4_state, kernel);
646 			/* VPPF plane 1 */
647 			vc4_dlist_write(vc4_state, kernel);
648 		}
649 	}
650 
651 	vc4_state->dlist[ctl0_offset] |=
652 		VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
653 
654 	return 0;
655 }
656 
657 /* If a modeset involves changing the setup of a plane, the atomic
658  * infrastructure will call this to validate a proposed plane setup.
659  * However, if a plane isn't getting updated, this (and the
660  * corresponding vc4_plane_atomic_update) won't get called.  Thus, we
661  * compute the dlist here and have all active plane dlists get updated
662  * in the CRTC's flush.
663  */
664 static int vc4_plane_atomic_check(struct drm_plane *plane,
665 				  struct drm_plane_state *state)
666 {
667 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
668 
669 	vc4_state->dlist_count = 0;
670 
671 	if (plane_enabled(state))
672 		return vc4_plane_mode_set(plane, state);
673 	else
674 		return 0;
675 }
676 
677 static void vc4_plane_atomic_update(struct drm_plane *plane,
678 				    struct drm_plane_state *old_state)
679 {
680 	/* No contents here.  Since we don't know where in the CRTC's
681 	 * dlist we should be stored, our dlist is uploaded to the
682 	 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
683 	 * time.
684 	 */
685 }
686 
687 u32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
688 {
689 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
690 	int i;
691 
692 	vc4_state->hw_dlist = dlist;
693 
694 	/* Can't memcpy_toio() because it needs to be 32-bit writes. */
695 	for (i = 0; i < vc4_state->dlist_count; i++)
696 		writel(vc4_state->dlist[i], &dlist[i]);
697 
698 	return vc4_state->dlist_count;
699 }
700 
701 u32 vc4_plane_dlist_size(const struct drm_plane_state *state)
702 {
703 	const struct vc4_plane_state *vc4_state =
704 		container_of(state, typeof(*vc4_state), base);
705 
706 	return vc4_state->dlist_count;
707 }
708 
709 /* Updates the plane to immediately (well, once the FIFO needs
710  * refilling) scan out from at a new framebuffer.
711  */
712 void vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
713 {
714 	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
715 	struct drm_gem_cma_object *bo = drm_fb_cma_get_gem_obj(fb, 0);
716 	uint32_t addr;
717 
718 	/* We're skipping the address adjustment for negative origin,
719 	 * because this is only called on the primary plane.
720 	 */
721 	WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
722 	addr = bo->paddr + fb->offsets[0];
723 
724 	/* Write the new address into the hardware immediately.  The
725 	 * scanout will start from this address as soon as the FIFO
726 	 * needs to refill with pixels.
727 	 */
728 	writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
729 
730 	/* Also update the CPU-side dlist copy, so that any later
731 	 * atomic updates that don't do a new modeset on our plane
732 	 * also use our updated address.
733 	 */
734 	vc4_state->dlist[vc4_state->ptr0_offset] = addr;
735 }
736 
737 static const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
738 	.atomic_check = vc4_plane_atomic_check,
739 	.atomic_update = vc4_plane_atomic_update,
740 };
741 
742 static void vc4_plane_destroy(struct drm_plane *plane)
743 {
744 	drm_plane_helper_disable(plane);
745 	drm_plane_cleanup(plane);
746 }
747 
748 /* Implements immediate (non-vblank-synced) updates of the cursor
749  * position, or falls back to the atomic helper otherwise.
750  */
751 static int
752 vc4_update_plane(struct drm_plane *plane,
753 		 struct drm_crtc *crtc,
754 		 struct drm_framebuffer *fb,
755 		 int crtc_x, int crtc_y,
756 		 unsigned int crtc_w, unsigned int crtc_h,
757 		 uint32_t src_x, uint32_t src_y,
758 		 uint32_t src_w, uint32_t src_h)
759 {
760 	struct drm_plane_state *plane_state;
761 	struct vc4_plane_state *vc4_state;
762 
763 	if (plane != crtc->cursor)
764 		goto out;
765 
766 	plane_state = plane->state;
767 	vc4_state = to_vc4_plane_state(plane_state);
768 
769 	if (!plane_state)
770 		goto out;
771 
772 	/* If we're changing the cursor contents, do that in the
773 	 * normal vblank-synced atomic path.
774 	 */
775 	if (fb != plane_state->fb)
776 		goto out;
777 
778 	/* No configuring new scaling in the fast path. */
779 	if (crtc_w != plane_state->crtc_w ||
780 	    crtc_h != plane_state->crtc_h ||
781 	    src_w != plane_state->src_w ||
782 	    src_h != plane_state->src_h) {
783 		goto out;
784 	}
785 
786 	/* Set the cursor's position on the screen.  This is the
787 	 * expected change from the drm_mode_cursor_universal()
788 	 * helper.
789 	 */
790 	plane_state->crtc_x = crtc_x;
791 	plane_state->crtc_y = crtc_y;
792 
793 	/* Allow changing the start position within the cursor BO, if
794 	 * that matters.
795 	 */
796 	plane_state->src_x = src_x;
797 	plane_state->src_y = src_y;
798 
799 	/* Update the display list based on the new crtc_x/y. */
800 	vc4_plane_atomic_check(plane, plane_state);
801 
802 	/* Note that we can't just call vc4_plane_write_dlist()
803 	 * because that would smash the context data that the HVS is
804 	 * currently using.
805 	 */
806 	writel(vc4_state->dlist[vc4_state->pos0_offset],
807 	       &vc4_state->hw_dlist[vc4_state->pos0_offset]);
808 	writel(vc4_state->dlist[vc4_state->pos2_offset],
809 	       &vc4_state->hw_dlist[vc4_state->pos2_offset]);
810 	writel(vc4_state->dlist[vc4_state->ptr0_offset],
811 	       &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
812 
813 	return 0;
814 
815 out:
816 	return drm_atomic_helper_update_plane(plane, crtc, fb,
817 					      crtc_x, crtc_y,
818 					      crtc_w, crtc_h,
819 					      src_x, src_y,
820 					      src_w, src_h);
821 }
822 
823 static const struct drm_plane_funcs vc4_plane_funcs = {
824 	.update_plane = vc4_update_plane,
825 	.disable_plane = drm_atomic_helper_disable_plane,
826 	.destroy = vc4_plane_destroy,
827 	.set_property = NULL,
828 	.reset = vc4_plane_reset,
829 	.atomic_duplicate_state = vc4_plane_duplicate_state,
830 	.atomic_destroy_state = vc4_plane_destroy_state,
831 };
832 
833 struct drm_plane *vc4_plane_init(struct drm_device *dev,
834 				 enum drm_plane_type type)
835 {
836 	struct drm_plane *plane = NULL;
837 	struct vc4_plane *vc4_plane;
838 	u32 formats[ARRAY_SIZE(hvs_formats)];
839 	u32 num_formats = 0;
840 	int ret = 0;
841 	unsigned i;
842 
843 	vc4_plane = devm_kzalloc(dev->dev, sizeof(*vc4_plane),
844 				 GFP_KERNEL);
845 	if (!vc4_plane) {
846 		ret = -ENOMEM;
847 		goto fail;
848 	}
849 
850 	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
851 		/* Don't allow YUV in cursor planes, since that means
852 		 * tuning on the scaler, which we don't allow for the
853 		 * cursor.
854 		 */
855 		if (type != DRM_PLANE_TYPE_CURSOR ||
856 		    hvs_formats[i].hvs < HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE) {
857 			formats[num_formats++] = hvs_formats[i].drm;
858 		}
859 	}
860 	plane = &vc4_plane->base;
861 	ret = drm_universal_plane_init(dev, plane, 0xff,
862 				       &vc4_plane_funcs,
863 				       formats, num_formats,
864 				       type, NULL);
865 
866 	drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
867 
868 	return plane;
869 fail:
870 	if (plane)
871 		vc4_plane_destroy(plane);
872 
873 	return ERR_PTR(ret);
874 }
875