xref: /linux/drivers/gpu/drm/sun4i/sun8i_vi_layer.c (revision a1ff5a7d78a036d6c2178ee5acd6ba4946243800)
12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
27480ba4dSJernej Skrabec /*
37480ba4dSJernej Skrabec  * Copyright (C) Jernej Skrabec <jernej.skrabec@siol.net>
47480ba4dSJernej Skrabec  */
57480ba4dSJernej Skrabec 
67480ba4dSJernej Skrabec #include <drm/drm_atomic.h>
77480ba4dSJernej Skrabec #include <drm/drm_atomic_helper.h>
890bb087fSVille Syrjälä #include <drm/drm_blend.h>
97480ba4dSJernej Skrabec #include <drm/drm_crtc.h>
106bcfe8eaSDanilo Krummrich #include <drm/drm_fb_dma_helper.h>
11720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
12820c1707SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
134a83c26aSDanilo Krummrich #include <drm/drm_gem_dma_helper.h>
14fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
157480ba4dSJernej Skrabec 
16daab3d0eSJernej Skrabec #include "sun8i_csc.h"
177480ba4dSJernej Skrabec #include "sun8i_mixer.h"
181ef1380cSJernej Skrabec #include "sun8i_vi_layer.h"
19b862a648SJernej Skrabec #include "sun8i_vi_scaler.h"
207480ba4dSJernej Skrabec 
sun8i_vi_layer_update_alpha(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane)21c0f6f0c4SRoman Stratiienko static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
22c0f6f0c4SRoman Stratiienko 					int overlay, struct drm_plane *plane)
23c0f6f0c4SRoman Stratiienko {
24c0f6f0c4SRoman Stratiienko 	u32 mask, val, ch_base;
25c0f6f0c4SRoman Stratiienko 
26c0f6f0c4SRoman Stratiienko 	ch_base = sun8i_channel_base(mixer, channel);
27c0f6f0c4SRoman Stratiienko 
28c0f6f0c4SRoman Stratiienko 	if (mixer->cfg->is_de3) {
29c0f6f0c4SRoman Stratiienko 		mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
30c0f6f0c4SRoman Stratiienko 		       SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
31c0f6f0c4SRoman Stratiienko 		val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
32c0f6f0c4SRoman Stratiienko 			(plane->state->alpha >> 8);
33c0f6f0c4SRoman Stratiienko 
34c0f6f0c4SRoman Stratiienko 		val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
35c0f6f0c4SRoman Stratiienko 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
36c0f6f0c4SRoman Stratiienko 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
37c0f6f0c4SRoman Stratiienko 
38c0f6f0c4SRoman Stratiienko 		regmap_update_bits(mixer->engine.regs,
39c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
40c0f6f0c4SRoman Stratiienko 								  overlay),
41c0f6f0c4SRoman Stratiienko 				   mask, val);
42c0f6f0c4SRoman Stratiienko 	} else if (mixer->cfg->vi_num == 1) {
43c0f6f0c4SRoman Stratiienko 		regmap_update_bits(mixer->engine.regs,
44c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
45c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
46c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA
47c0f6f0c4SRoman Stratiienko 					(plane->state->alpha >> 8));
48c0f6f0c4SRoman Stratiienko 	}
49c0f6f0c4SRoman Stratiienko }
50c0f6f0c4SRoman Stratiienko 
sun8i_vi_layer_update_coord(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane,unsigned int zpos)517480ba4dSJernej Skrabec static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
52f88c5ee7SJernej Skrabec 				       int overlay, struct drm_plane *plane,
53f88c5ee7SJernej Skrabec 				       unsigned int zpos)
547480ba4dSJernej Skrabec {
557480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
561343bd6cSJernej Skrabec 	const struct drm_format_info *format = state->fb->format;
57b862a648SJernej Skrabec 	u32 src_w, src_h, dst_w, dst_h;
584b09c073SJernej Skrabec 	u32 bld_base, ch_base;
59b862a648SJernej Skrabec 	u32 outsize, insize;
60b862a648SJernej Skrabec 	u32 hphase, vphase;
61a7db690cSJernej Skrabec 	u32 hn = 0, hm = 0;
62a7db690cSJernej Skrabec 	u32 vn = 0, vm = 0;
63e1ef9006SJernej Skrabec 	bool subsampled;
647480ba4dSJernej Skrabec 
657480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
667480ba4dSJernej Skrabec 			 channel, overlay);
67b862a648SJernej Skrabec 
684b09c073SJernej Skrabec 	bld_base = sun8i_blender_base(mixer);
694b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
704b09c073SJernej Skrabec 
71b862a648SJernej Skrabec 	src_w = drm_rect_width(&state->src) >> 16;
72b862a648SJernej Skrabec 	src_h = drm_rect_height(&state->src) >> 16;
73b862a648SJernej Skrabec 	dst_w = drm_rect_width(&state->dst);
74b862a648SJernej Skrabec 	dst_h = drm_rect_height(&state->dst);
75b862a648SJernej Skrabec 
76b862a648SJernej Skrabec 	hphase = state->src.x1 & 0xffff;
77b862a648SJernej Skrabec 	vphase = state->src.y1 & 0xffff;
78b862a648SJernej Skrabec 
79e1ef9006SJernej Skrabec 	/* make coordinates dividable by subsampling factor */
80e1ef9006SJernej Skrabec 	if (format->hsub > 1) {
81e1ef9006SJernej Skrabec 		int mask, remainder;
82e1ef9006SJernej Skrabec 
83e1ef9006SJernej Skrabec 		mask = format->hsub - 1;
84e1ef9006SJernej Skrabec 		remainder = (state->src.x1 >> 16) & mask;
85e1ef9006SJernej Skrabec 		src_w = (src_w + remainder) & ~mask;
86e1ef9006SJernej Skrabec 		hphase += remainder << 16;
87e1ef9006SJernej Skrabec 	}
88e1ef9006SJernej Skrabec 
89e1ef9006SJernej Skrabec 	if (format->vsub > 1) {
90e1ef9006SJernej Skrabec 		int mask, remainder;
91e1ef9006SJernej Skrabec 
92e1ef9006SJernej Skrabec 		mask = format->vsub - 1;
93e1ef9006SJernej Skrabec 		remainder = (state->src.y1 >> 16) & mask;
94e1ef9006SJernej Skrabec 		src_h = (src_h + remainder) & ~mask;
95e1ef9006SJernej Skrabec 		vphase += remainder << 16;
96e1ef9006SJernej Skrabec 	}
97e1ef9006SJernej Skrabec 
98b862a648SJernej Skrabec 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
99b862a648SJernej Skrabec 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
1007480ba4dSJernej Skrabec 
1017480ba4dSJernej Skrabec 	/* Set height and width */
102b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
103e1ef9006SJernej Skrabec 			 (state->src.x1 >> 16) & ~(format->hsub - 1),
104e1ef9006SJernej Skrabec 			 (state->src.y1 >> 16) & ~(format->vsub - 1));
105b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
1067480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1074b09c073SJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
108b862a648SJernej Skrabec 		     insize);
1097480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1104b09c073SJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
111b862a648SJernej Skrabec 		     insize);
112b862a648SJernej Skrabec 
113e1ef9006SJernej Skrabec 	/*
114e1ef9006SJernej Skrabec 	 * Scaler must be enabled for subsampled formats, so it scales
115e1ef9006SJernej Skrabec 	 * chroma to same size as luma.
116e1ef9006SJernej Skrabec 	 */
117e1ef9006SJernej Skrabec 	subsampled = format->hsub > 1 || format->vsub > 1;
118e1ef9006SJernej Skrabec 
119e1ef9006SJernej Skrabec 	if (insize != outsize || subsampled || hphase || vphase) {
120a7db690cSJernej Skrabec 		unsigned int scanline, required;
121a7db690cSJernej Skrabec 		struct drm_display_mode *mode;
122a7db690cSJernej Skrabec 		u32 hscale, vscale, fps;
123a7db690cSJernej Skrabec 		u64 ability;
124b862a648SJernej Skrabec 
125b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
126b862a648SJernej Skrabec 
127a7db690cSJernej Skrabec 		mode = &plane->state->crtc->state->mode;
128a7db690cSJernej Skrabec 		fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
129a7db690cSJernej Skrabec 		ability = clk_get_rate(mixer->mod_clk);
130a7db690cSJernej Skrabec 		/* BSP algorithm assumes 80% efficiency of VI scaler unit */
131a7db690cSJernej Skrabec 		ability *= 80;
132a7db690cSJernej Skrabec 		do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
133a7db690cSJernej Skrabec 
134a7db690cSJernej Skrabec 		required = src_h * 100 / dst_h;
135a7db690cSJernej Skrabec 
136a7db690cSJernej Skrabec 		if (ability < required) {
137a7db690cSJernej Skrabec 			DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
138a7db690cSJernej Skrabec 			vm = src_h;
139a7db690cSJernej Skrabec 			vn = (u32)ability * dst_h / 100;
140a7db690cSJernej Skrabec 			src_h = vn;
141a7db690cSJernej Skrabec 		}
142a7db690cSJernej Skrabec 
143a7db690cSJernej Skrabec 		/* it seems that every RGB scaler has buffer for 2048 pixels */
144a7db690cSJernej Skrabec 		scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
145a7db690cSJernej Skrabec 
146a7db690cSJernej Skrabec 		if (src_w > scanline) {
147a7db690cSJernej Skrabec 			DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
148a7db690cSJernej Skrabec 			hm = src_w;
149a7db690cSJernej Skrabec 			hn = scanline;
150a7db690cSJernej Skrabec 			src_w = hn;
151a7db690cSJernej Skrabec 		}
152a7db690cSJernej Skrabec 
153a7db690cSJernej Skrabec 		hscale = (src_w << 16) / dst_w;
154a7db690cSJernej Skrabec 		vscale = (src_h << 16) / dst_h;
155b862a648SJernej Skrabec 
156b862a648SJernej Skrabec 		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
1571343bd6cSJernej Skrabec 				      dst_h, hscale, vscale, hphase, vphase,
1581343bd6cSJernej Skrabec 				      format);
159b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, true);
160b862a648SJernej Skrabec 	} else {
161b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
162b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, false);
163b862a648SJernej Skrabec 	}
1647480ba4dSJernej Skrabec 
165a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
166a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
167a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
168a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
169a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
170a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
171a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
172a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
173a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
174a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
175a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
176a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
177a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
178a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
179a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
180a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
181a7db690cSJernej Skrabec 
1827480ba4dSJernej Skrabec 	/* Set base coordinates */
183b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
1847480ba4dSJernej Skrabec 			 state->dst.x1, state->dst.y1);
185b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
1867480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1874b09c073SJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
1887480ba4dSJernej Skrabec 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
1897480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1904b09c073SJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
191b862a648SJernej Skrabec 		     outsize);
1927480ba4dSJernej Skrabec 
1937480ba4dSJernej Skrabec 	return 0;
1947480ba4dSJernej Skrabec }
1957480ba4dSJernej Skrabec 
sun8i_vi_layer_get_csc_mode(const struct drm_format_info * format)1960ee9f600SJernej Skrabec static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
197daab3d0eSJernej Skrabec {
198daab3d0eSJernej Skrabec 	if (!format->is_yuv)
199daab3d0eSJernej Skrabec 		return SUN8I_CSC_MODE_OFF;
200daab3d0eSJernej Skrabec 
201daab3d0eSJernej Skrabec 	switch (format->format) {
202daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU411:
203daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU420:
204daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU422:
205daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU444:
206daab3d0eSJernej Skrabec 		return SUN8I_CSC_MODE_YVU2RGB;
207daab3d0eSJernej Skrabec 	default:
208daab3d0eSJernej Skrabec 		return SUN8I_CSC_MODE_YUV2RGB;
209daab3d0eSJernej Skrabec 	}
210daab3d0eSJernej Skrabec }
211daab3d0eSJernej Skrabec 
sun8i_vi_layer_update_formats(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane)2127480ba4dSJernej Skrabec static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
2137480ba4dSJernej Skrabec 					 int overlay, struct drm_plane *plane)
2147480ba4dSJernej Skrabec {
2157480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
2167831112eSJernej Skrabec 	u32 val, ch_base, csc_mode, hw_fmt;
217a9a75359SJernej Skrabec 	const struct drm_format_info *fmt;
2187831112eSJernej Skrabec 	int ret;
2194b09c073SJernej Skrabec 
2204b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
2217480ba4dSJernej Skrabec 
222a9a75359SJernej Skrabec 	fmt = state->fb->format;
2237831112eSJernej Skrabec 	ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
2247831112eSJernej Skrabec 	if (ret) {
2257480ba4dSJernej Skrabec 		DRM_DEBUG_DRIVER("Invalid format\n");
2267831112eSJernej Skrabec 		return ret;
2277480ba4dSJernej Skrabec 	}
2287480ba4dSJernej Skrabec 
2297831112eSJernej Skrabec 	val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
2307480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
2314b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
232e1ef9006SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
233e1ef9006SJernej Skrabec 
234daab3d0eSJernej Skrabec 	csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
235daab3d0eSJernej Skrabec 	if (csc_mode != SUN8I_CSC_MODE_OFF) {
236daab3d0eSJernej Skrabec 		sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
237b72cb0dcSJernej Skrabec 						state->color_encoding,
238b72cb0dcSJernej Skrabec 						state->color_range);
239e1ef9006SJernej Skrabec 		sun8i_csc_enable_ccsc(mixer, channel, true);
240e1ef9006SJernej Skrabec 	} else {
241e1ef9006SJernej Skrabec 		sun8i_csc_enable_ccsc(mixer, channel, false);
242e1ef9006SJernej Skrabec 	}
243e1ef9006SJernej Skrabec 
244a9a75359SJernej Skrabec 	if (!fmt->is_yuv)
245e1ef9006SJernej Skrabec 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
246e1ef9006SJernej Skrabec 	else
247e1ef9006SJernej Skrabec 		val = 0;
248e1ef9006SJernej Skrabec 
249e1ef9006SJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
2504b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
251e1ef9006SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
2527480ba4dSJernej Skrabec 
2537480ba4dSJernej Skrabec 	return 0;
2547480ba4dSJernej Skrabec }
2557480ba4dSJernej Skrabec 
sun8i_vi_layer_update_buffer(struct sun8i_mixer * mixer,int channel,int overlay,struct drm_plane * plane)2567480ba4dSJernej Skrabec static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
2577480ba4dSJernej Skrabec 					int overlay, struct drm_plane *plane)
2587480ba4dSJernej Skrabec {
2597480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
2607480ba4dSJernej Skrabec 	struct drm_framebuffer *fb = state->fb;
261e1ef9006SJernej Skrabec 	const struct drm_format_info *format = fb->format;
2624a83c26aSDanilo Krummrich 	struct drm_gem_dma_object *gem;
263e1ef9006SJernej Skrabec 	u32 dx, dy, src_x, src_y;
2648c30eeccSDanilo Krummrich 	dma_addr_t dma_addr;
2654b09c073SJernej Skrabec 	u32 ch_base;
266e1ef9006SJernej Skrabec 	int i;
2677480ba4dSJernej Skrabec 
2684b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
2694b09c073SJernej Skrabec 
270e1ef9006SJernej Skrabec 	/* Adjust x and y to be dividable by subsampling factor */
271e1ef9006SJernej Skrabec 	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
272e1ef9006SJernej Skrabec 	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
273e1ef9006SJernej Skrabec 
274e1ef9006SJernej Skrabec 	for (i = 0; i < format->num_planes; i++) {
2757480ba4dSJernej Skrabec 		/* Get the physical address of the buffer in memory */
2766bcfe8eaSDanilo Krummrich 		gem = drm_fb_dma_get_gem_obj(fb, i);
2777480ba4dSJernej Skrabec 
2788c30eeccSDanilo Krummrich 		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->dma_addr);
2797480ba4dSJernej Skrabec 
2807480ba4dSJernej Skrabec 		/* Compute the start of the displayed memory */
2818c30eeccSDanilo Krummrich 		dma_addr = gem->dma_addr + fb->offsets[i];
282e1ef9006SJernej Skrabec 
283e1ef9006SJernej Skrabec 		dx = src_x;
284e1ef9006SJernej Skrabec 		dy = src_y;
285e1ef9006SJernej Skrabec 
286e1ef9006SJernej Skrabec 		if (i > 0) {
287e1ef9006SJernej Skrabec 			dx /= format->hsub;
288e1ef9006SJernej Skrabec 			dy /= format->vsub;
289e1ef9006SJernej Skrabec 		}
2907480ba4dSJernej Skrabec 
2917480ba4dSJernej Skrabec 		/* Fixup framebuffer address for src coordinates */
2928c30eeccSDanilo Krummrich 		dma_addr += dx * format->cpp[i];
2938c30eeccSDanilo Krummrich 		dma_addr += dy * fb->pitches[i];
2947480ba4dSJernej Skrabec 
2957480ba4dSJernej Skrabec 		/* Set the line width */
296e1ef9006SJernej Skrabec 		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
297e1ef9006SJernej Skrabec 				 i + 1, fb->pitches[i]);
2987480ba4dSJernej Skrabec 		regmap_write(mixer->engine.regs,
2994b09c073SJernej Skrabec 			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
300e1ef9006SJernej Skrabec 							     overlay, i),
301e1ef9006SJernej Skrabec 			     fb->pitches[i]);
3027480ba4dSJernej Skrabec 
303e1ef9006SJernej Skrabec 		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
3048c30eeccSDanilo Krummrich 				 i + 1, &dma_addr);
3057480ba4dSJernej Skrabec 
3067480ba4dSJernej Skrabec 		regmap_write(mixer->engine.regs,
3074b09c073SJernej Skrabec 			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
308e1ef9006SJernej Skrabec 								 overlay, i),
3098c30eeccSDanilo Krummrich 			     lower_32_bits(dma_addr));
310e1ef9006SJernej Skrabec 	}
3117480ba4dSJernej Skrabec 
3127480ba4dSJernej Skrabec 	return 0;
3137480ba4dSJernej Skrabec }
3147480ba4dSJernej Skrabec 
sun8i_vi_layer_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)3157480ba4dSJernej Skrabec static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
3167c11b99aSMaxime Ripard 				       struct drm_atomic_state *state)
3177480ba4dSJernej Skrabec {
3187c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
3197c11b99aSMaxime Ripard 										 plane);
320134155a5SOndrej Jirman 	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
321ba5c1649SMaxime Ripard 	struct drm_crtc *crtc = new_plane_state->crtc;
3227480ba4dSJernej Skrabec 	struct drm_crtc_state *crtc_state;
323b862a648SJernej Skrabec 	int min_scale, max_scale;
3247480ba4dSJernej Skrabec 
3257480ba4dSJernej Skrabec 	if (!crtc)
3267480ba4dSJernej Skrabec 		return 0;
3277480ba4dSJernej Skrabec 
328dec92020SMaxime Ripard 	crtc_state = drm_atomic_get_existing_crtc_state(state,
329ba5c1649SMaxime Ripard 							crtc);
3307480ba4dSJernej Skrabec 	if (WARN_ON(!crtc_state))
3317480ba4dSJernej Skrabec 		return -EINVAL;
3327480ba4dSJernej Skrabec 
333cce32e4eSThomas Zimmermann 	min_scale = DRM_PLANE_NO_SCALING;
334cce32e4eSThomas Zimmermann 	max_scale = DRM_PLANE_NO_SCALING;
335bc29489fSJernej Skrabec 
336b862a648SJernej Skrabec 	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
337b862a648SJernej Skrabec 		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
338b862a648SJernej Skrabec 		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
339b862a648SJernej Skrabec 	}
340b862a648SJernej Skrabec 
341ba5c1649SMaxime Ripard 	return drm_atomic_helper_check_plane_state(new_plane_state,
342ba5c1649SMaxime Ripard 						   crtc_state,
343b862a648SJernej Skrabec 						   min_scale, max_scale,
3447480ba4dSJernej Skrabec 						   true, true);
3457480ba4dSJernej Skrabec }
3467480ba4dSJernej Skrabec 
sun8i_vi_layer_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)3477480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
348977697e2SMaxime Ripard 					 struct drm_atomic_state *state)
3497480ba4dSJernej Skrabec {
35037418bf1SMaxime Ripard 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
35137418bf1SMaxime Ripard 									   plane);
352134155a5SOndrej Jirman 	struct sun8i_layer *layer = plane_to_sun8i_layer(plane);
353e05162c0SMaxime Ripard 	unsigned int zpos = new_state->normalized_zpos;
3547480ba4dSJernej Skrabec 	struct sun8i_mixer *mixer = layer->mixer;
3557480ba4dSJernej Skrabec 
356*f8d59facSOndrej Jirman 	if (!new_state->crtc || !new_state->visible)
3577480ba4dSJernej Skrabec 		return;
3587480ba4dSJernej Skrabec 
3597480ba4dSJernej Skrabec 	sun8i_vi_layer_update_coord(mixer, layer->channel,
360f88c5ee7SJernej Skrabec 				    layer->overlay, plane, zpos);
361c0f6f0c4SRoman Stratiienko 	sun8i_vi_layer_update_alpha(mixer, layer->channel,
362c0f6f0c4SRoman Stratiienko 				    layer->overlay, plane);
3637480ba4dSJernej Skrabec 	sun8i_vi_layer_update_formats(mixer, layer->channel,
3647480ba4dSJernej Skrabec 				      layer->overlay, plane);
3657480ba4dSJernej Skrabec 	sun8i_vi_layer_update_buffer(mixer, layer->channel,
3667480ba4dSJernej Skrabec 				     layer->overlay, plane);
3677480ba4dSJernej Skrabec }
3687480ba4dSJernej Skrabec 
369f13478c9SRikard Falkeborn static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
3707480ba4dSJernej Skrabec 	.atomic_check	= sun8i_vi_layer_atomic_check,
3717480ba4dSJernej Skrabec 	.atomic_update	= sun8i_vi_layer_atomic_update,
3727480ba4dSJernej Skrabec };
3737480ba4dSJernej Skrabec 
3747480ba4dSJernej Skrabec static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
3757480ba4dSJernej Skrabec 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
3767480ba4dSJernej Skrabec 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
3777480ba4dSJernej Skrabec 	.destroy		= drm_plane_cleanup,
3787480ba4dSJernej Skrabec 	.disable_plane		= drm_atomic_helper_disable_plane,
3797480ba4dSJernej Skrabec 	.reset			= drm_atomic_helper_plane_reset,
3807480ba4dSJernej Skrabec 	.update_plane		= drm_atomic_helper_update_plane,
3817480ba4dSJernej Skrabec };
3827480ba4dSJernej Skrabec 
3837480ba4dSJernej Skrabec /*
38420896ef1SJernej Skrabec  * While DE2 VI layer supports same RGB formats as UI layer, alpha
38520896ef1SJernej Skrabec  * channel is ignored. This structure lists all unique variants
38620896ef1SJernej Skrabec  * where alpha channel is replaced with "don't care" (X) channel.
3877480ba4dSJernej Skrabec  */
3887480ba4dSJernej Skrabec static const u32 sun8i_vi_layer_formats[] = {
3897480ba4dSJernej Skrabec 	DRM_FORMAT_BGR565,
3907480ba4dSJernej Skrabec 	DRM_FORMAT_BGR888,
39120896ef1SJernej Skrabec 	DRM_FORMAT_BGRX4444,
39220896ef1SJernej Skrabec 	DRM_FORMAT_BGRX5551,
3937480ba4dSJernej Skrabec 	DRM_FORMAT_BGRX8888,
3947480ba4dSJernej Skrabec 	DRM_FORMAT_RGB565,
3957480ba4dSJernej Skrabec 	DRM_FORMAT_RGB888,
39620896ef1SJernej Skrabec 	DRM_FORMAT_RGBX4444,
39720896ef1SJernej Skrabec 	DRM_FORMAT_RGBX5551,
3987480ba4dSJernej Skrabec 	DRM_FORMAT_RGBX8888,
39920896ef1SJernej Skrabec 	DRM_FORMAT_XBGR1555,
40020896ef1SJernej Skrabec 	DRM_FORMAT_XBGR4444,
4017480ba4dSJernej Skrabec 	DRM_FORMAT_XBGR8888,
40220896ef1SJernej Skrabec 	DRM_FORMAT_XRGB1555,
40320896ef1SJernej Skrabec 	DRM_FORMAT_XRGB4444,
4047480ba4dSJernej Skrabec 	DRM_FORMAT_XRGB8888,
405e1ef9006SJernej Skrabec 
406e1ef9006SJernej Skrabec 	DRM_FORMAT_NV16,
407e1ef9006SJernej Skrabec 	DRM_FORMAT_NV12,
408e1ef9006SJernej Skrabec 	DRM_FORMAT_NV21,
409e1ef9006SJernej Skrabec 	DRM_FORMAT_NV61,
410e1ef9006SJernej Skrabec 	DRM_FORMAT_UYVY,
411e1ef9006SJernej Skrabec 	DRM_FORMAT_VYUY,
412e1ef9006SJernej Skrabec 	DRM_FORMAT_YUYV,
413e1ef9006SJernej Skrabec 	DRM_FORMAT_YVYU,
414e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV411,
415e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV420,
416e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV422,
417e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU411,
418e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU420,
419e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU422,
4207480ba4dSJernej Skrabec };
4217480ba4dSJernej Skrabec 
422169ca4b3SJernej Skrabec static const u32 sun8i_vi_layer_de3_formats[] = {
423169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR1555,
424169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR2101010,
425169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR4444,
426169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR8888,
427169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB1555,
428169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB2101010,
429169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB4444,
430169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB8888,
431169ca4b3SJernej Skrabec 	DRM_FORMAT_BGR565,
432169ca4b3SJernej Skrabec 	DRM_FORMAT_BGR888,
433169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA1010102,
434169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA5551,
435169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA4444,
436169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA8888,
437169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRX8888,
438169ca4b3SJernej Skrabec 	DRM_FORMAT_RGB565,
439169ca4b3SJernej Skrabec 	DRM_FORMAT_RGB888,
440169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA1010102,
441169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA4444,
442169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA5551,
443169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA8888,
444169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBX8888,
445169ca4b3SJernej Skrabec 	DRM_FORMAT_XBGR8888,
446169ca4b3SJernej Skrabec 	DRM_FORMAT_XRGB8888,
447169ca4b3SJernej Skrabec 
448169ca4b3SJernej Skrabec 	DRM_FORMAT_NV16,
449169ca4b3SJernej Skrabec 	DRM_FORMAT_NV12,
450169ca4b3SJernej Skrabec 	DRM_FORMAT_NV21,
451169ca4b3SJernej Skrabec 	DRM_FORMAT_NV61,
452169ca4b3SJernej Skrabec 	DRM_FORMAT_P010,
453169ca4b3SJernej Skrabec 	DRM_FORMAT_P210,
454169ca4b3SJernej Skrabec 	DRM_FORMAT_UYVY,
455169ca4b3SJernej Skrabec 	DRM_FORMAT_VYUY,
456169ca4b3SJernej Skrabec 	DRM_FORMAT_YUYV,
457169ca4b3SJernej Skrabec 	DRM_FORMAT_YVYU,
458169ca4b3SJernej Skrabec 	DRM_FORMAT_YUV411,
459169ca4b3SJernej Skrabec 	DRM_FORMAT_YUV420,
460169ca4b3SJernej Skrabec 	DRM_FORMAT_YUV422,
461169ca4b3SJernej Skrabec 	DRM_FORMAT_YVU411,
462169ca4b3SJernej Skrabec 	DRM_FORMAT_YVU420,
463169ca4b3SJernej Skrabec 	DRM_FORMAT_YVU422,
464169ca4b3SJernej Skrabec };
465169ca4b3SJernej Skrabec 
466b36a6bb8SPiotr Oniszczuk static const uint64_t sun8i_layer_modifiers[] = {
467b36a6bb8SPiotr Oniszczuk 	DRM_FORMAT_MOD_LINEAR,
468b36a6bb8SPiotr Oniszczuk 	DRM_FORMAT_MOD_INVALID
469b36a6bb8SPiotr Oniszczuk };
470b36a6bb8SPiotr Oniszczuk 
sun8i_vi_layer_init_one(struct drm_device * drm,struct sun8i_mixer * mixer,int index)471134155a5SOndrej Jirman struct sun8i_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
4727480ba4dSJernej Skrabec 					    struct sun8i_mixer *mixer,
4737480ba4dSJernej Skrabec 					    int index)
4747480ba4dSJernej Skrabec {
475c3779dabSSamuel Holland 	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
4765917e0bdSJernej Skrabec 	u32 supported_encodings, supported_ranges;
477169ca4b3SJernej Skrabec 	unsigned int plane_cnt, format_count;
478134155a5SOndrej Jirman 	struct sun8i_layer *layer;
479169ca4b3SJernej Skrabec 	const u32 *formats;
4807480ba4dSJernej Skrabec 	int ret;
4817480ba4dSJernej Skrabec 
4827480ba4dSJernej Skrabec 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
4837480ba4dSJernej Skrabec 	if (!layer)
4847480ba4dSJernej Skrabec 		return ERR_PTR(-ENOMEM);
4857480ba4dSJernej Skrabec 
486169ca4b3SJernej Skrabec 	if (mixer->cfg->is_de3) {
487169ca4b3SJernej Skrabec 		formats = sun8i_vi_layer_de3_formats;
488169ca4b3SJernej Skrabec 		format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
489169ca4b3SJernej Skrabec 	} else {
490169ca4b3SJernej Skrabec 		formats = sun8i_vi_layer_formats;
491169ca4b3SJernej Skrabec 		format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
492169ca4b3SJernej Skrabec 	}
493169ca4b3SJernej Skrabec 
494c3779dabSSamuel Holland 	if (!mixer->cfg->ui_num && index == 0)
495c3779dabSSamuel Holland 		type = DRM_PLANE_TYPE_PRIMARY;
496c3779dabSSamuel Holland 
4977480ba4dSJernej Skrabec 	/* possible crtcs are set later */
4987480ba4dSJernej Skrabec 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
4997480ba4dSJernej Skrabec 				       &sun8i_vi_layer_funcs,
500169ca4b3SJernej Skrabec 				       formats, format_count,
501b36a6bb8SPiotr Oniszczuk 				       sun8i_layer_modifiers,
502c3779dabSSamuel Holland 				       type, NULL);
5037480ba4dSJernej Skrabec 	if (ret) {
5047480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't initialize layer\n");
5057480ba4dSJernej Skrabec 		return ERR_PTR(ret);
5067480ba4dSJernej Skrabec 	}
5077480ba4dSJernej Skrabec 
508f88c5ee7SJernej Skrabec 	plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
509f88c5ee7SJernej Skrabec 
510c0f6f0c4SRoman Stratiienko 	if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) {
511c0f6f0c4SRoman Stratiienko 		ret = drm_plane_create_alpha_property(&layer->plane);
512c0f6f0c4SRoman Stratiienko 		if (ret) {
513c0f6f0c4SRoman Stratiienko 			dev_err(drm->dev, "Couldn't add alpha property\n");
514c0f6f0c4SRoman Stratiienko 			return ERR_PTR(ret);
515c0f6f0c4SRoman Stratiienko 		}
516c0f6f0c4SRoman Stratiienko 	}
517c0f6f0c4SRoman Stratiienko 
518f88c5ee7SJernej Skrabec 	ret = drm_plane_create_zpos_property(&layer->plane, index,
519f88c5ee7SJernej Skrabec 					     0, plane_cnt - 1);
5207480ba4dSJernej Skrabec 	if (ret) {
5217480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't add zpos property\n");
5227480ba4dSJernej Skrabec 		return ERR_PTR(ret);
5237480ba4dSJernej Skrabec 	}
5247480ba4dSJernej Skrabec 
5255917e0bdSJernej Skrabec 	supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
5265917e0bdSJernej Skrabec 			      BIT(DRM_COLOR_YCBCR_BT709);
5270cf1d704SJernej Skrabec 	if (mixer->cfg->is_de3)
5280cf1d704SJernej Skrabec 		supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
5295917e0bdSJernej Skrabec 
5305917e0bdSJernej Skrabec 	supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
5315917e0bdSJernej Skrabec 			   BIT(DRM_COLOR_YCBCR_FULL_RANGE);
5325917e0bdSJernej Skrabec 
5335917e0bdSJernej Skrabec 	ret = drm_plane_create_color_properties(&layer->plane,
5345917e0bdSJernej Skrabec 						supported_encodings,
5355917e0bdSJernej Skrabec 						supported_ranges,
5365917e0bdSJernej Skrabec 						DRM_COLOR_YCBCR_BT709,
5375917e0bdSJernej Skrabec 						DRM_COLOR_YCBCR_LIMITED_RANGE);
5385917e0bdSJernej Skrabec 	if (ret) {
5395917e0bdSJernej Skrabec 		dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
5405917e0bdSJernej Skrabec 		return ERR_PTR(ret);
5415917e0bdSJernej Skrabec 	}
5425917e0bdSJernej Skrabec 
5437480ba4dSJernej Skrabec 	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
5447480ba4dSJernej Skrabec 	layer->mixer = mixer;
545*f8d59facSOndrej Jirman 	layer->type = SUN8I_LAYER_TYPE_VI;
5467480ba4dSJernej Skrabec 	layer->channel = index;
5477480ba4dSJernej Skrabec 	layer->overlay = 0;
5487480ba4dSJernej Skrabec 
5497480ba4dSJernej Skrabec 	return layer;
5507480ba4dSJernej Skrabec }
551