Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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
 27enum vc4_scaling_mode {
 28	VC4_SCALING_NONE,
 29	VC4_SCALING_TPZ,
 30	VC4_SCALING_PPF,
 31};
 32
 33struct 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
 75static inline struct vc4_plane_state *
 76to_vc4_plane_state(struct drm_plane_state *state)
 77{
 78	return (struct vc4_plane_state *)state;
 79}
 80
 81static 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
148static 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
160static 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
170static bool plane_enabled(struct drm_plane_state *state)
171{
172	return state->fb && state->crtc;
173}
174
175static 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
204static 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. */
224static 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
238static 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 */
261static 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
291static 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
398static 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
416static 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
426static 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
457static 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 */
492static 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 */
664static 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
677static 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
687u32 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
701u32 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 */
712void 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
737static 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
742static 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 */
751static int
752vc4_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
815out:
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
823static 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
833struct 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, 0,
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;
869fail:
870	if (plane)
871		vc4_plane_destroy(plane);
872
873	return ERR_PTR(ret);
874}