xref: /linux/drivers/gpu/drm/sun4i/sun8i_vi_layer.c (revision 1aca5ce036e3499336d1a2ace3070f908381c055)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
4  */
5 
6 #include <drm/drm_atomic.h>
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_blend.h>
9 #include <drm/drm_crtc.h>
10 #include <drm/drm_fb_dma_helper.h>
11 #include <drm/drm_framebuffer.h>
12 #include <drm/drm_gem_atomic_helper.h>
13 #include <drm/drm_gem_dma_helper.h>
14 #include <drm/drm_probe_helper.h>
15 
16 #include "sun8i_csc.h"
17 #include "sun8i_mixer.h"
18 #include "sun8i_vi_layer.h"
19 #include "sun8i_vi_scaler.h"
20 
21 static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
22 				  int overlay, bool enable, unsigned int zpos,
23 				  unsigned int old_zpos)
24 {
25 	u32 val, bld_base, ch_base;
26 
27 	bld_base = sun8i_blender_base(mixer);
28 	ch_base = sun8i_channel_base(mixer, channel);
29 
30 	DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
31 			 enable ? "En" : "Dis", channel, overlay);
32 
33 	if (enable)
34 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
35 	else
36 		val = 0;
37 
38 	regmap_update_bits(mixer->engine.regs,
39 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
40 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
41 
42 	if (!enable || zpos != old_zpos) {
43 		regmap_update_bits(mixer->engine.regs,
44 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
45 				   SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
46 				   0);
47 
48 		regmap_update_bits(mixer->engine.regs,
49 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
50 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
51 				   0);
52 	}
53 
54 	if (enable) {
55 		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
56 
57 		regmap_update_bits(mixer->engine.regs,
58 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
59 				   val, val);
60 
61 		val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
62 
63 		regmap_update_bits(mixer->engine.regs,
64 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
65 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
66 				   val);
67 	}
68 }
69 
70 static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
71 					int overlay, struct drm_plane *plane)
72 {
73 	u32 mask, val, ch_base;
74 
75 	ch_base = sun8i_channel_base(mixer, channel);
76 
77 	if (mixer->cfg->is_de3) {
78 		mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
79 		       SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
80 		val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
81 			(plane->state->alpha >> 8);
82 
83 		val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
84 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
85 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
86 
87 		regmap_update_bits(mixer->engine.regs,
88 				   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
89 								  overlay),
90 				   mask, val);
91 	} else if (mixer->cfg->vi_num == 1) {
92 		regmap_update_bits(mixer->engine.regs,
93 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
94 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
95 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA
96 					(plane->state->alpha >> 8));
97 	}
98 }
99 
100 static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
101 				       int overlay, struct drm_plane *plane,
102 				       unsigned int zpos)
103 {
104 	struct drm_plane_state *state = plane->state;
105 	const struct drm_format_info *format = state->fb->format;
106 	u32 src_w, src_h, dst_w, dst_h;
107 	u32 bld_base, ch_base;
108 	u32 outsize, insize;
109 	u32 hphase, vphase;
110 	u32 hn = 0, hm = 0;
111 	u32 vn = 0, vm = 0;
112 	bool subsampled;
113 
114 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
115 			 channel, overlay);
116 
117 	bld_base = sun8i_blender_base(mixer);
118 	ch_base = sun8i_channel_base(mixer, channel);
119 
120 	src_w = drm_rect_width(&state->src) >> 16;
121 	src_h = drm_rect_height(&state->src) >> 16;
122 	dst_w = drm_rect_width(&state->dst);
123 	dst_h = drm_rect_height(&state->dst);
124 
125 	hphase = state->src.x1 & 0xffff;
126 	vphase = state->src.y1 & 0xffff;
127 
128 	/* make coordinates dividable by subsampling factor */
129 	if (format->hsub > 1) {
130 		int mask, remainder;
131 
132 		mask = format->hsub - 1;
133 		remainder = (state->src.x1 >> 16) & mask;
134 		src_w = (src_w + remainder) & ~mask;
135 		hphase += remainder << 16;
136 	}
137 
138 	if (format->vsub > 1) {
139 		int mask, remainder;
140 
141 		mask = format->vsub - 1;
142 		remainder = (state->src.y1 >> 16) & mask;
143 		src_h = (src_h + remainder) & ~mask;
144 		vphase += remainder << 16;
145 	}
146 
147 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
148 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
149 
150 	/* Set height and width */
151 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
152 			 (state->src.x1 >> 16) & ~(format->hsub - 1),
153 			 (state->src.y1 >> 16) & ~(format->vsub - 1));
154 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
155 	regmap_write(mixer->engine.regs,
156 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
157 		     insize);
158 	regmap_write(mixer->engine.regs,
159 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
160 		     insize);
161 
162 	/*
163 	 * Scaler must be enabled for subsampled formats, so it scales
164 	 * chroma to same size as luma.
165 	 */
166 	subsampled = format->hsub > 1 || format->vsub > 1;
167 
168 	if (insize != outsize || subsampled || hphase || vphase) {
169 		unsigned int scanline, required;
170 		struct drm_display_mode *mode;
171 		u32 hscale, vscale, fps;
172 		u64 ability;
173 
174 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
175 
176 		mode = &plane->state->crtc->state->mode;
177 		fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
178 		ability = clk_get_rate(mixer->mod_clk);
179 		/* BSP algorithm assumes 80% efficiency of VI scaler unit */
180 		ability *= 80;
181 		do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
182 
183 		required = src_h * 100 / dst_h;
184 
185 		if (ability < required) {
186 			DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
187 			vm = src_h;
188 			vn = (u32)ability * dst_h / 100;
189 			src_h = vn;
190 		}
191 
192 		/* it seems that every RGB scaler has buffer for 2048 pixels */
193 		scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
194 
195 		if (src_w > scanline) {
196 			DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
197 			hm = src_w;
198 			hn = scanline;
199 			src_w = hn;
200 		}
201 
202 		hscale = (src_w << 16) / dst_w;
203 		vscale = (src_h << 16) / dst_h;
204 
205 		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
206 				      dst_h, hscale, vscale, hphase, vphase,
207 				      format);
208 		sun8i_vi_scaler_enable(mixer, channel, true);
209 	} else {
210 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
211 		sun8i_vi_scaler_enable(mixer, channel, false);
212 	}
213 
214 	regmap_write(mixer->engine.regs,
215 		     SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
216 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
217 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
218 	regmap_write(mixer->engine.regs,
219 		     SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
220 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
221 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
222 	regmap_write(mixer->engine.regs,
223 		     SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
224 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
225 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
226 	regmap_write(mixer->engine.regs,
227 		     SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
228 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
229 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
230 
231 	/* Set base coordinates */
232 	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
233 			 state->dst.x1, state->dst.y1);
234 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
235 	regmap_write(mixer->engine.regs,
236 		     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
237 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
238 	regmap_write(mixer->engine.regs,
239 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
240 		     outsize);
241 
242 	return 0;
243 }
244 
245 static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
246 {
247 	if (!format->is_yuv)
248 		return SUN8I_CSC_MODE_OFF;
249 
250 	switch (format->format) {
251 	case DRM_FORMAT_YVU411:
252 	case DRM_FORMAT_YVU420:
253 	case DRM_FORMAT_YVU422:
254 	case DRM_FORMAT_YVU444:
255 		return SUN8I_CSC_MODE_YVU2RGB;
256 	default:
257 		return SUN8I_CSC_MODE_YUV2RGB;
258 	}
259 }
260 
261 static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
262 					 int overlay, struct drm_plane *plane)
263 {
264 	struct drm_plane_state *state = plane->state;
265 	u32 val, ch_base, csc_mode, hw_fmt;
266 	const struct drm_format_info *fmt;
267 	int ret;
268 
269 	ch_base = sun8i_channel_base(mixer, channel);
270 
271 	fmt = state->fb->format;
272 	ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
273 	if (ret) {
274 		DRM_DEBUG_DRIVER("Invalid format\n");
275 		return ret;
276 	}
277 
278 	val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
279 	regmap_update_bits(mixer->engine.regs,
280 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
281 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
282 
283 	csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
284 	if (csc_mode != SUN8I_CSC_MODE_OFF) {
285 		sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
286 						state->color_encoding,
287 						state->color_range);
288 		sun8i_csc_enable_ccsc(mixer, channel, true);
289 	} else {
290 		sun8i_csc_enable_ccsc(mixer, channel, false);
291 	}
292 
293 	if (!fmt->is_yuv)
294 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
295 	else
296 		val = 0;
297 
298 	regmap_update_bits(mixer->engine.regs,
299 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
300 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
301 
302 	return 0;
303 }
304 
305 static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
306 					int overlay, struct drm_plane *plane)
307 {
308 	struct drm_plane_state *state = plane->state;
309 	struct drm_framebuffer *fb = state->fb;
310 	const struct drm_format_info *format = fb->format;
311 	struct drm_gem_dma_object *gem;
312 	u32 dx, dy, src_x, src_y;
313 	dma_addr_t dma_addr;
314 	u32 ch_base;
315 	int i;
316 
317 	ch_base = sun8i_channel_base(mixer, channel);
318 
319 	/* Adjust x and y to be dividable by subsampling factor */
320 	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
321 	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
322 
323 	for (i = 0; i < format->num_planes; i++) {
324 		/* Get the physical address of the buffer in memory */
325 		gem = drm_fb_dma_get_gem_obj(fb, i);
326 
327 		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr);
328 
329 		/* Compute the start of the displayed memory */
330 		dma_addr = gem->dma_addr + fb->offsets[i];
331 
332 		dx = src_x;
333 		dy = src_y;
334 
335 		if (i > 0) {
336 			dx /= format->hsub;
337 			dy /= format->vsub;
338 		}
339 
340 		/* Fixup framebuffer address for src coordinates */
341 		dma_addr += dx * format->cpp[i];
342 		dma_addr += dy * fb->pitches[i];
343 
344 		/* Set the line width */
345 		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
346 				 i + 1, fb->pitches[i]);
347 		regmap_write(mixer->engine.regs,
348 			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
349 							     overlay, i),
350 			     fb->pitches[i]);
351 
352 		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
353 				 i + 1, &dma_addr);
354 
355 		regmap_write(mixer->engine.regs,
356 			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
357 								 overlay, i),
358 			     lower_32_bits(dma_addr));
359 	}
360 
361 	return 0;
362 }
363 
364 static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
365 				       struct drm_atomic_state *state)
366 {
367 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
368 										 plane);
369 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
370 	struct drm_crtc *crtc = new_plane_state->crtc;
371 	struct drm_crtc_state *crtc_state;
372 	int min_scale, max_scale;
373 
374 	if (!crtc)
375 		return 0;
376 
377 	crtc_state = drm_atomic_get_existing_crtc_state(state,
378 							crtc);
379 	if (WARN_ON(!crtc_state))
380 		return -EINVAL;
381 
382 	min_scale = DRM_PLANE_NO_SCALING;
383 	max_scale = DRM_PLANE_NO_SCALING;
384 
385 	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
386 		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
387 		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
388 	}
389 
390 	return drm_atomic_helper_check_plane_state(new_plane_state,
391 						   crtc_state,
392 						   min_scale, max_scale,
393 						   true, true);
394 }
395 
396 static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
397 					  struct drm_atomic_state *state)
398 {
399 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
400 									   plane);
401 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
402 	unsigned int old_zpos = old_state->normalized_zpos;
403 	struct sun8i_mixer *mixer = layer->mixer;
404 
405 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
406 			      old_zpos);
407 }
408 
409 static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
410 					 struct drm_atomic_state *state)
411 {
412 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
413 									   plane);
414 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
415 									   plane);
416 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
417 	unsigned int zpos = new_state->normalized_zpos;
418 	unsigned int old_zpos = old_state->normalized_zpos;
419 	struct sun8i_mixer *mixer = layer->mixer;
420 
421 	if (!new_state->visible) {
422 		sun8i_vi_layer_enable(mixer, layer->channel,
423 				      layer->overlay, false, 0, old_zpos);
424 		return;
425 	}
426 
427 	sun8i_vi_layer_update_coord(mixer, layer->channel,
428 				    layer->overlay, plane, zpos);
429 	sun8i_vi_layer_update_alpha(mixer, layer->channel,
430 				    layer->overlay, plane);
431 	sun8i_vi_layer_update_formats(mixer, layer->channel,
432 				      layer->overlay, plane);
433 	sun8i_vi_layer_update_buffer(mixer, layer->channel,
434 				     layer->overlay, plane);
435 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
436 			      true, zpos, old_zpos);
437 }
438 
439 static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
440 	.atomic_check	= sun8i_vi_layer_atomic_check,
441 	.atomic_disable	= sun8i_vi_layer_atomic_disable,
442 	.atomic_update	= sun8i_vi_layer_atomic_update,
443 };
444 
445 static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
446 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
447 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
448 	.destroy		= drm_plane_cleanup,
449 	.disable_plane		= drm_atomic_helper_disable_plane,
450 	.reset			= drm_atomic_helper_plane_reset,
451 	.update_plane		= drm_atomic_helper_update_plane,
452 };
453 
454 /*
455  * While DE2 VI layer supports same RGB formats as UI layer, alpha
456  * channel is ignored. This structure lists all unique variants
457  * where alpha channel is replaced with "don't care" (X) channel.
458  */
459 static const u32 sun8i_vi_layer_formats[] = {
460 	DRM_FORMAT_BGR565,
461 	DRM_FORMAT_BGR888,
462 	DRM_FORMAT_BGRX4444,
463 	DRM_FORMAT_BGRX5551,
464 	DRM_FORMAT_BGRX8888,
465 	DRM_FORMAT_RGB565,
466 	DRM_FORMAT_RGB888,
467 	DRM_FORMAT_RGBX4444,
468 	DRM_FORMAT_RGBX5551,
469 	DRM_FORMAT_RGBX8888,
470 	DRM_FORMAT_XBGR1555,
471 	DRM_FORMAT_XBGR4444,
472 	DRM_FORMAT_XBGR8888,
473 	DRM_FORMAT_XRGB1555,
474 	DRM_FORMAT_XRGB4444,
475 	DRM_FORMAT_XRGB8888,
476 
477 	DRM_FORMAT_NV16,
478 	DRM_FORMAT_NV12,
479 	DRM_FORMAT_NV21,
480 	DRM_FORMAT_NV61,
481 	DRM_FORMAT_UYVY,
482 	DRM_FORMAT_VYUY,
483 	DRM_FORMAT_YUYV,
484 	DRM_FORMAT_YVYU,
485 	DRM_FORMAT_YUV411,
486 	DRM_FORMAT_YUV420,
487 	DRM_FORMAT_YUV422,
488 	DRM_FORMAT_YVU411,
489 	DRM_FORMAT_YVU420,
490 	DRM_FORMAT_YVU422,
491 };
492 
493 static const u32 sun8i_vi_layer_de3_formats[] = {
494 	DRM_FORMAT_ABGR1555,
495 	DRM_FORMAT_ABGR2101010,
496 	DRM_FORMAT_ABGR4444,
497 	DRM_FORMAT_ABGR8888,
498 	DRM_FORMAT_ARGB1555,
499 	DRM_FORMAT_ARGB2101010,
500 	DRM_FORMAT_ARGB4444,
501 	DRM_FORMAT_ARGB8888,
502 	DRM_FORMAT_BGR565,
503 	DRM_FORMAT_BGR888,
504 	DRM_FORMAT_BGRA1010102,
505 	DRM_FORMAT_BGRA5551,
506 	DRM_FORMAT_BGRA4444,
507 	DRM_FORMAT_BGRA8888,
508 	DRM_FORMAT_BGRX8888,
509 	DRM_FORMAT_RGB565,
510 	DRM_FORMAT_RGB888,
511 	DRM_FORMAT_RGBA1010102,
512 	DRM_FORMAT_RGBA4444,
513 	DRM_FORMAT_RGBA5551,
514 	DRM_FORMAT_RGBA8888,
515 	DRM_FORMAT_RGBX8888,
516 	DRM_FORMAT_XBGR8888,
517 	DRM_FORMAT_XRGB8888,
518 
519 	DRM_FORMAT_NV16,
520 	DRM_FORMAT_NV12,
521 	DRM_FORMAT_NV21,
522 	DRM_FORMAT_NV61,
523 	DRM_FORMAT_P010,
524 	DRM_FORMAT_P210,
525 	DRM_FORMAT_UYVY,
526 	DRM_FORMAT_VYUY,
527 	DRM_FORMAT_YUYV,
528 	DRM_FORMAT_YVYU,
529 	DRM_FORMAT_YUV411,
530 	DRM_FORMAT_YUV420,
531 	DRM_FORMAT_YUV422,
532 	DRM_FORMAT_YVU411,
533 	DRM_FORMAT_YVU420,
534 	DRM_FORMAT_YVU422,
535 };
536 
537 static const uint64_t sun8i_layer_modifiers[] = {
538 	DRM_FORMAT_MOD_LINEAR,
539 	DRM_FORMAT_MOD_INVALID
540 };
541 
542 struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
543 					       struct sun8i_mixer *mixer,
544 					       int index)
545 {
546 	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
547 	u32 supported_encodings, supported_ranges;
548 	unsigned int plane_cnt, format_count;
549 	struct sun8i_vi_layer *layer;
550 	const u32 *formats;
551 	int ret;
552 
553 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
554 	if (!layer)
555 		return ERR_PTR(-ENOMEM);
556 
557 	if (mixer->cfg->is_de3) {
558 		formats = sun8i_vi_layer_de3_formats;
559 		format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
560 	} else {
561 		formats = sun8i_vi_layer_formats;
562 		format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
563 	}
564 
565 	if (!mixer->cfg->ui_num && index == 0)
566 		type = DRM_PLANE_TYPE_PRIMARY;
567 
568 	/* possible crtcs are set later */
569 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
570 				       &sun8i_vi_layer_funcs,
571 				       formats, format_count,
572 				       sun8i_layer_modifiers,
573 				       type, NULL);
574 	if (ret) {
575 		dev_err(drm->dev, "Couldn't initialize layer\n");
576 		return ERR_PTR(ret);
577 	}
578 
579 	plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
580 
581 	if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) {
582 		ret = drm_plane_create_alpha_property(&layer->plane);
583 		if (ret) {
584 			dev_err(drm->dev, "Couldn't add alpha property\n");
585 			return ERR_PTR(ret);
586 		}
587 	}
588 
589 	ret = drm_plane_create_zpos_property(&layer->plane, index,
590 					     0, plane_cnt - 1);
591 	if (ret) {
592 		dev_err(drm->dev, "Couldn't add zpos property\n");
593 		return ERR_PTR(ret);
594 	}
595 
596 	supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
597 			      BIT(DRM_COLOR_YCBCR_BT709);
598 	if (mixer->cfg->is_de3)
599 		supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
600 
601 	supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
602 			   BIT(DRM_COLOR_YCBCR_FULL_RANGE);
603 
604 	ret = drm_plane_create_color_properties(&layer->plane,
605 						supported_encodings,
606 						supported_ranges,
607 						DRM_COLOR_YCBCR_BT709,
608 						DRM_COLOR_YCBCR_LIMITED_RANGE);
609 	if (ret) {
610 		dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
611 		return ERR_PTR(ret);
612 	}
613 
614 	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
615 	layer->mixer = mixer;
616 	layer->channel = index;
617 	layer->overlay = 0;
618 
619 	return layer;
620 }
621