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>
87480ba4dSJernej Skrabec #include <drm/drm_crtc.h>
97480ba4dSJernej Skrabec #include <drm/drm_fb_cma_helper.h>
107480ba4dSJernej Skrabec #include <drm/drm_gem_cma_helper.h>
117b24eec7SQiang Yu #include <drm/drm_gem_framebuffer_helper.h>
127480ba4dSJernej Skrabec #include <drm/drm_plane_helper.h>
13fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
147480ba4dSJernej Skrabec 
157480ba4dSJernej Skrabec #include "sun8i_vi_layer.h"
167480ba4dSJernej Skrabec #include "sun8i_mixer.h"
17b862a648SJernej Skrabec #include "sun8i_vi_scaler.h"
187480ba4dSJernej Skrabec 
197480ba4dSJernej Skrabec static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
20d8b3f454SPaul Kocialkowski 				  int overlay, bool enable, unsigned int zpos,
21d8b3f454SPaul Kocialkowski 				  unsigned int old_zpos)
227480ba4dSJernej Skrabec {
234b09c073SJernej Skrabec 	u32 val, bld_base, ch_base;
244b09c073SJernej Skrabec 
254b09c073SJernej Skrabec 	bld_base = sun8i_blender_base(mixer);
264b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
277480ba4dSJernej Skrabec 
287480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
297480ba4dSJernej Skrabec 			 enable ? "En" : "Dis", channel, overlay);
307480ba4dSJernej Skrabec 
317480ba4dSJernej Skrabec 	if (enable)
327480ba4dSJernej Skrabec 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
337480ba4dSJernej Skrabec 	else
347480ba4dSJernej Skrabec 		val = 0;
357480ba4dSJernej Skrabec 
367480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
374b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
387480ba4dSJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
397480ba4dSJernej Skrabec 
40d8b3f454SPaul Kocialkowski 	if (!enable || zpos != old_zpos) {
41d8b3f454SPaul Kocialkowski 		regmap_update_bits(mixer->engine.regs,
424b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
43d8b3f454SPaul Kocialkowski 				   SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
44d8b3f454SPaul Kocialkowski 				   0);
45d8b3f454SPaul Kocialkowski 
46d8b3f454SPaul Kocialkowski 		regmap_update_bits(mixer->engine.regs,
474b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
48d8b3f454SPaul Kocialkowski 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
49d8b3f454SPaul Kocialkowski 				   0);
50d8b3f454SPaul Kocialkowski 	}
51d8b3f454SPaul Kocialkowski 
52f88c5ee7SJernej Skrabec 	if (enable) {
53f88c5ee7SJernej Skrabec 		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
547480ba4dSJernej Skrabec 
557480ba4dSJernej Skrabec 		regmap_update_bits(mixer->engine.regs,
564b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
574b09c073SJernej Skrabec 				   val, val);
58f88c5ee7SJernej Skrabec 
59f88c5ee7SJernej Skrabec 		val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
60f88c5ee7SJernej Skrabec 
61f88c5ee7SJernej Skrabec 		regmap_update_bits(mixer->engine.regs,
624b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
63f88c5ee7SJernej Skrabec 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
64f88c5ee7SJernej Skrabec 				   val);
65f88c5ee7SJernej Skrabec 	}
667480ba4dSJernej Skrabec }
677480ba4dSJernej Skrabec 
687480ba4dSJernej Skrabec static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
69f88c5ee7SJernej Skrabec 				       int overlay, struct drm_plane *plane,
70f88c5ee7SJernej Skrabec 				       unsigned int zpos)
717480ba4dSJernej Skrabec {
727480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
731343bd6cSJernej Skrabec 	const struct drm_format_info *format = state->fb->format;
74b862a648SJernej Skrabec 	u32 src_w, src_h, dst_w, dst_h;
754b09c073SJernej Skrabec 	u32 bld_base, ch_base;
76b862a648SJernej Skrabec 	u32 outsize, insize;
77b862a648SJernej Skrabec 	u32 hphase, vphase;
78a7db690cSJernej Skrabec 	u32 hn = 0, hm = 0;
79a7db690cSJernej Skrabec 	u32 vn = 0, vm = 0;
80e1ef9006SJernej Skrabec 	bool subsampled;
817480ba4dSJernej Skrabec 
827480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
837480ba4dSJernej Skrabec 			 channel, overlay);
84b862a648SJernej Skrabec 
854b09c073SJernej Skrabec 	bld_base = sun8i_blender_base(mixer);
864b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
874b09c073SJernej Skrabec 
88b862a648SJernej Skrabec 	src_w = drm_rect_width(&state->src) >> 16;
89b862a648SJernej Skrabec 	src_h = drm_rect_height(&state->src) >> 16;
90b862a648SJernej Skrabec 	dst_w = drm_rect_width(&state->dst);
91b862a648SJernej Skrabec 	dst_h = drm_rect_height(&state->dst);
92b862a648SJernej Skrabec 
93b862a648SJernej Skrabec 	hphase = state->src.x1 & 0xffff;
94b862a648SJernej Skrabec 	vphase = state->src.y1 & 0xffff;
95b862a648SJernej Skrabec 
96e1ef9006SJernej Skrabec 	/* make coordinates dividable by subsampling factor */
97e1ef9006SJernej Skrabec 	if (format->hsub > 1) {
98e1ef9006SJernej Skrabec 		int mask, remainder;
99e1ef9006SJernej Skrabec 
100e1ef9006SJernej Skrabec 		mask = format->hsub - 1;
101e1ef9006SJernej Skrabec 		remainder = (state->src.x1 >> 16) & mask;
102e1ef9006SJernej Skrabec 		src_w = (src_w + remainder) & ~mask;
103e1ef9006SJernej Skrabec 		hphase += remainder << 16;
104e1ef9006SJernej Skrabec 	}
105e1ef9006SJernej Skrabec 
106e1ef9006SJernej Skrabec 	if (format->vsub > 1) {
107e1ef9006SJernej Skrabec 		int mask, remainder;
108e1ef9006SJernej Skrabec 
109e1ef9006SJernej Skrabec 		mask = format->vsub - 1;
110e1ef9006SJernej Skrabec 		remainder = (state->src.y1 >> 16) & mask;
111e1ef9006SJernej Skrabec 		src_h = (src_h + remainder) & ~mask;
112e1ef9006SJernej Skrabec 		vphase += remainder << 16;
113e1ef9006SJernej Skrabec 	}
114e1ef9006SJernej Skrabec 
115b862a648SJernej Skrabec 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
116b862a648SJernej Skrabec 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
1177480ba4dSJernej Skrabec 
1187480ba4dSJernej Skrabec 	/* Set height and width */
119b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
120e1ef9006SJernej Skrabec 			 (state->src.x1 >> 16) & ~(format->hsub - 1),
121e1ef9006SJernej Skrabec 			 (state->src.y1 >> 16) & ~(format->vsub - 1));
122b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
1237480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1244b09c073SJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
125b862a648SJernej Skrabec 		     insize);
1267480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1274b09c073SJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
128b862a648SJernej Skrabec 		     insize);
129b862a648SJernej Skrabec 
130e1ef9006SJernej Skrabec 	/*
131e1ef9006SJernej Skrabec 	 * Scaler must be enabled for subsampled formats, so it scales
132e1ef9006SJernej Skrabec 	 * chroma to same size as luma.
133e1ef9006SJernej Skrabec 	 */
134e1ef9006SJernej Skrabec 	subsampled = format->hsub > 1 || format->vsub > 1;
135e1ef9006SJernej Skrabec 
136e1ef9006SJernej Skrabec 	if (insize != outsize || subsampled || hphase || vphase) {
137a7db690cSJernej Skrabec 		unsigned int scanline, required;
138a7db690cSJernej Skrabec 		struct drm_display_mode *mode;
139a7db690cSJernej Skrabec 		u32 hscale, vscale, fps;
140a7db690cSJernej Skrabec 		u64 ability;
141b862a648SJernej Skrabec 
142b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
143b862a648SJernej Skrabec 
144a7db690cSJernej Skrabec 		mode = &plane->state->crtc->state->mode;
145a7db690cSJernej Skrabec 		fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
146a7db690cSJernej Skrabec 		ability = clk_get_rate(mixer->mod_clk);
147a7db690cSJernej Skrabec 		/* BSP algorithm assumes 80% efficiency of VI scaler unit */
148a7db690cSJernej Skrabec 		ability *= 80;
149a7db690cSJernej Skrabec 		do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
150a7db690cSJernej Skrabec 
151a7db690cSJernej Skrabec 		required = src_h * 100 / dst_h;
152a7db690cSJernej Skrabec 
153a7db690cSJernej Skrabec 		if (ability < required) {
154a7db690cSJernej Skrabec 			DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
155a7db690cSJernej Skrabec 			vm = src_h;
156a7db690cSJernej Skrabec 			vn = (u32)ability * dst_h / 100;
157a7db690cSJernej Skrabec 			src_h = vn;
158a7db690cSJernej Skrabec 		}
159a7db690cSJernej Skrabec 
160a7db690cSJernej Skrabec 		/* it seems that every RGB scaler has buffer for 2048 pixels */
161a7db690cSJernej Skrabec 		scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
162a7db690cSJernej Skrabec 
163a7db690cSJernej Skrabec 		if (src_w > scanline) {
164a7db690cSJernej Skrabec 			DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
165a7db690cSJernej Skrabec 			hm = src_w;
166a7db690cSJernej Skrabec 			hn = scanline;
167a7db690cSJernej Skrabec 			src_w = hn;
168a7db690cSJernej Skrabec 		}
169a7db690cSJernej Skrabec 
170a7db690cSJernej Skrabec 		hscale = (src_w << 16) / dst_w;
171a7db690cSJernej Skrabec 		vscale = (src_h << 16) / dst_h;
172b862a648SJernej Skrabec 
173b862a648SJernej Skrabec 		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
1741343bd6cSJernej Skrabec 				      dst_h, hscale, vscale, hphase, vphase,
1751343bd6cSJernej Skrabec 				      format);
176b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, true);
177b862a648SJernej Skrabec 	} else {
178b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
179b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, false);
180b862a648SJernej Skrabec 	}
1817480ba4dSJernej Skrabec 
182a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
183a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
184a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
185a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
186a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
187a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
188a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
189a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
190a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
191a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
192a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
193a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
194a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
195a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
196a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
197a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
198a7db690cSJernej Skrabec 
1997480ba4dSJernej Skrabec 	/* Set base coordinates */
200b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
2017480ba4dSJernej Skrabec 			 state->dst.x1, state->dst.y1);
202b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
2037480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
2044b09c073SJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
2057480ba4dSJernej Skrabec 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
2067480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
2074b09c073SJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
208b862a648SJernej Skrabec 		     outsize);
2097480ba4dSJernej Skrabec 
2107480ba4dSJernej Skrabec 	return 0;
2117480ba4dSJernej Skrabec }
2127480ba4dSJernej Skrabec 
2137480ba4dSJernej Skrabec static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
2147480ba4dSJernej Skrabec 					 int overlay, struct drm_plane *plane)
2157480ba4dSJernej Skrabec {
2167480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
2177480ba4dSJernej Skrabec 	const struct de2_fmt_info *fmt_info;
2184b09c073SJernej Skrabec 	u32 val, ch_base;
2194b09c073SJernej Skrabec 
2204b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
2217480ba4dSJernej Skrabec 
2227480ba4dSJernej Skrabec 	fmt_info = sun8i_mixer_format_info(state->fb->format->format);
223e1ef9006SJernej Skrabec 	if (!fmt_info) {
2247480ba4dSJernej Skrabec 		DRM_DEBUG_DRIVER("Invalid format\n");
2257480ba4dSJernej Skrabec 		return -EINVAL;
2267480ba4dSJernej Skrabec 	}
2277480ba4dSJernej Skrabec 
2287480ba4dSJernej Skrabec 	val = fmt_info->de2_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
2297480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
2304b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
231e1ef9006SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
232e1ef9006SJernej Skrabec 
233e1ef9006SJernej Skrabec 	if (fmt_info->csc != SUN8I_CSC_MODE_OFF) {
234b72cb0dcSJernej Skrabec 		sun8i_csc_set_ccsc_coefficients(mixer, channel, fmt_info->csc,
235b72cb0dcSJernej Skrabec 						state->color_encoding,
236b72cb0dcSJernej Skrabec 						state->color_range);
237e1ef9006SJernej Skrabec 		sun8i_csc_enable_ccsc(mixer, channel, true);
238e1ef9006SJernej Skrabec 	} else {
239e1ef9006SJernej Skrabec 		sun8i_csc_enable_ccsc(mixer, channel, false);
240e1ef9006SJernej Skrabec 	}
241e1ef9006SJernej Skrabec 
242e1ef9006SJernej Skrabec 	if (fmt_info->rgb)
243e1ef9006SJernej Skrabec 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
244e1ef9006SJernej Skrabec 	else
245e1ef9006SJernej Skrabec 		val = 0;
246e1ef9006SJernej Skrabec 
247e1ef9006SJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
2484b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
249e1ef9006SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
2507480ba4dSJernej Skrabec 
251c50519e6SJernej Skrabec 	/* It seems that YUV formats use global alpha setting. */
252c50519e6SJernej Skrabec 	if (mixer->cfg->is_de3)
253c50519e6SJernej Skrabec 		regmap_update_bits(mixer->engine.regs,
254c50519e6SJernej Skrabec 				   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
255c50519e6SJernej Skrabec 								  overlay),
256c50519e6SJernej Skrabec 				   SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK,
257c50519e6SJernej Skrabec 				   SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA(0xff));
258c50519e6SJernej Skrabec 
2597480ba4dSJernej Skrabec 	return 0;
2607480ba4dSJernej Skrabec }
2617480ba4dSJernej Skrabec 
2627480ba4dSJernej Skrabec static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
2637480ba4dSJernej Skrabec 					int overlay, struct drm_plane *plane)
2647480ba4dSJernej Skrabec {
2657480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
2667480ba4dSJernej Skrabec 	struct drm_framebuffer *fb = state->fb;
267e1ef9006SJernej Skrabec 	const struct drm_format_info *format = fb->format;
2687480ba4dSJernej Skrabec 	struct drm_gem_cma_object *gem;
269e1ef9006SJernej Skrabec 	u32 dx, dy, src_x, src_y;
2707480ba4dSJernej Skrabec 	dma_addr_t paddr;
2714b09c073SJernej Skrabec 	u32 ch_base;
272e1ef9006SJernej Skrabec 	int i;
2737480ba4dSJernej Skrabec 
2744b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
2754b09c073SJernej Skrabec 
276e1ef9006SJernej Skrabec 	/* Adjust x and y to be dividable by subsampling factor */
277e1ef9006SJernej Skrabec 	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
278e1ef9006SJernej Skrabec 	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
279e1ef9006SJernej Skrabec 
280e1ef9006SJernej Skrabec 	for (i = 0; i < format->num_planes; i++) {
2817480ba4dSJernej Skrabec 		/* Get the physical address of the buffer in memory */
282e1ef9006SJernej Skrabec 		gem = drm_fb_cma_get_gem_obj(fb, i);
2837480ba4dSJernej Skrabec 
2847480ba4dSJernej Skrabec 		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
2857480ba4dSJernej Skrabec 
2867480ba4dSJernej Skrabec 		/* Compute the start of the displayed memory */
287e1ef9006SJernej Skrabec 		paddr = gem->paddr + fb->offsets[i];
288e1ef9006SJernej Skrabec 
289e1ef9006SJernej Skrabec 		dx = src_x;
290e1ef9006SJernej Skrabec 		dy = src_y;
291e1ef9006SJernej Skrabec 
292e1ef9006SJernej Skrabec 		if (i > 0) {
293e1ef9006SJernej Skrabec 			dx /= format->hsub;
294e1ef9006SJernej Skrabec 			dy /= format->vsub;
295e1ef9006SJernej Skrabec 		}
2967480ba4dSJernej Skrabec 
2977480ba4dSJernej Skrabec 		/* Fixup framebuffer address for src coordinates */
298e1ef9006SJernej Skrabec 		paddr += dx * format->cpp[i];
299e1ef9006SJernej Skrabec 		paddr += dy * fb->pitches[i];
3007480ba4dSJernej Skrabec 
3017480ba4dSJernej Skrabec 		/* Set the line width */
302e1ef9006SJernej Skrabec 		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
303e1ef9006SJernej Skrabec 				 i + 1, fb->pitches[i]);
3047480ba4dSJernej Skrabec 		regmap_write(mixer->engine.regs,
3054b09c073SJernej Skrabec 			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
306e1ef9006SJernej Skrabec 							     overlay, i),
307e1ef9006SJernej Skrabec 			     fb->pitches[i]);
3087480ba4dSJernej Skrabec 
309e1ef9006SJernej Skrabec 		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
310e1ef9006SJernej Skrabec 				 i + 1, &paddr);
3117480ba4dSJernej Skrabec 
3127480ba4dSJernej Skrabec 		regmap_write(mixer->engine.regs,
3134b09c073SJernej Skrabec 			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
314e1ef9006SJernej Skrabec 								 overlay, i),
3157480ba4dSJernej Skrabec 			     lower_32_bits(paddr));
316e1ef9006SJernej Skrabec 	}
3177480ba4dSJernej Skrabec 
3187480ba4dSJernej Skrabec 	return 0;
3197480ba4dSJernej Skrabec }
3207480ba4dSJernej Skrabec 
3217480ba4dSJernej Skrabec static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
3227480ba4dSJernej Skrabec 				       struct drm_plane_state *state)
3237480ba4dSJernej Skrabec {
324b862a648SJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
3257480ba4dSJernej Skrabec 	struct drm_crtc *crtc = state->crtc;
3267480ba4dSJernej Skrabec 	struct drm_crtc_state *crtc_state;
327b862a648SJernej Skrabec 	int min_scale, max_scale;
3287480ba4dSJernej Skrabec 
3297480ba4dSJernej Skrabec 	if (!crtc)
3307480ba4dSJernej Skrabec 		return 0;
3317480ba4dSJernej Skrabec 
3327480ba4dSJernej Skrabec 	crtc_state = drm_atomic_get_existing_crtc_state(state->state, crtc);
3337480ba4dSJernej Skrabec 	if (WARN_ON(!crtc_state))
3347480ba4dSJernej Skrabec 		return -EINVAL;
3357480ba4dSJernej Skrabec 
336bc29489fSJernej Skrabec 	min_scale = DRM_PLANE_HELPER_NO_SCALING;
337bc29489fSJernej Skrabec 	max_scale = DRM_PLANE_HELPER_NO_SCALING;
338bc29489fSJernej Skrabec 
339b862a648SJernej Skrabec 	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
340b862a648SJernej Skrabec 		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
341b862a648SJernej Skrabec 		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
342b862a648SJernej Skrabec 	}
343b862a648SJernej Skrabec 
34481af63a4SVille Syrjälä 	return drm_atomic_helper_check_plane_state(state, crtc_state,
345b862a648SJernej Skrabec 						   min_scale, max_scale,
3467480ba4dSJernej Skrabec 						   true, true);
3477480ba4dSJernej Skrabec }
3487480ba4dSJernej Skrabec 
3497480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
3507480ba4dSJernej Skrabec 					  struct drm_plane_state *old_state)
3517480ba4dSJernej Skrabec {
3527480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
353d8b3f454SPaul Kocialkowski 	unsigned int old_zpos = old_state->normalized_zpos;
3547480ba4dSJernej Skrabec 	struct sun8i_mixer *mixer = layer->mixer;
3557480ba4dSJernej Skrabec 
356d8b3f454SPaul Kocialkowski 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
357d8b3f454SPaul Kocialkowski 			      old_zpos);
3587480ba4dSJernej Skrabec }
3597480ba4dSJernej Skrabec 
3607480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
3617480ba4dSJernej Skrabec 					 struct drm_plane_state *old_state)
3627480ba4dSJernej Skrabec {
3637480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
364f88c5ee7SJernej Skrabec 	unsigned int zpos = plane->state->normalized_zpos;
365d8b3f454SPaul Kocialkowski 	unsigned int old_zpos = old_state->normalized_zpos;
3667480ba4dSJernej Skrabec 	struct sun8i_mixer *mixer = layer->mixer;
3677480ba4dSJernej Skrabec 
3687480ba4dSJernej Skrabec 	if (!plane->state->visible) {
3697480ba4dSJernej Skrabec 		sun8i_vi_layer_enable(mixer, layer->channel,
370d8b3f454SPaul Kocialkowski 				      layer->overlay, false, 0, old_zpos);
3717480ba4dSJernej Skrabec 		return;
3727480ba4dSJernej Skrabec 	}
3737480ba4dSJernej Skrabec 
3747480ba4dSJernej Skrabec 	sun8i_vi_layer_update_coord(mixer, layer->channel,
375f88c5ee7SJernej Skrabec 				    layer->overlay, plane, zpos);
3767480ba4dSJernej Skrabec 	sun8i_vi_layer_update_formats(mixer, layer->channel,
3777480ba4dSJernej Skrabec 				      layer->overlay, plane);
3787480ba4dSJernej Skrabec 	sun8i_vi_layer_update_buffer(mixer, layer->channel,
3797480ba4dSJernej Skrabec 				     layer->overlay, plane);
380f88c5ee7SJernej Skrabec 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
381d8b3f454SPaul Kocialkowski 			      true, zpos, old_zpos);
3827480ba4dSJernej Skrabec }
3837480ba4dSJernej Skrabec 
3847480ba4dSJernej Skrabec static struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
3857b24eec7SQiang Yu 	.prepare_fb	= drm_gem_fb_prepare_fb,
3867480ba4dSJernej Skrabec 	.atomic_check	= sun8i_vi_layer_atomic_check,
3877480ba4dSJernej Skrabec 	.atomic_disable	= sun8i_vi_layer_atomic_disable,
3887480ba4dSJernej Skrabec 	.atomic_update	= sun8i_vi_layer_atomic_update,
3897480ba4dSJernej Skrabec };
3907480ba4dSJernej Skrabec 
3917480ba4dSJernej Skrabec static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
3927480ba4dSJernej Skrabec 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
3937480ba4dSJernej Skrabec 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
3947480ba4dSJernej Skrabec 	.destroy		= drm_plane_cleanup,
3957480ba4dSJernej Skrabec 	.disable_plane		= drm_atomic_helper_disable_plane,
3967480ba4dSJernej Skrabec 	.reset			= drm_atomic_helper_plane_reset,
3977480ba4dSJernej Skrabec 	.update_plane		= drm_atomic_helper_update_plane,
3987480ba4dSJernej Skrabec };
3997480ba4dSJernej Skrabec 
4007480ba4dSJernej Skrabec /*
4017480ba4dSJernej Skrabec  * While all RGB formats are supported, VI planes don't support
4027480ba4dSJernej Skrabec  * alpha blending, so there is no point having formats with alpha
4037480ba4dSJernej Skrabec  * channel if their opaque analog exist.
4047480ba4dSJernej Skrabec  */
4057480ba4dSJernej Skrabec static const u32 sun8i_vi_layer_formats[] = {
4067480ba4dSJernej Skrabec 	DRM_FORMAT_ABGR1555,
4077480ba4dSJernej Skrabec 	DRM_FORMAT_ABGR4444,
4087480ba4dSJernej Skrabec 	DRM_FORMAT_ARGB1555,
4097480ba4dSJernej Skrabec 	DRM_FORMAT_ARGB4444,
4107480ba4dSJernej Skrabec 	DRM_FORMAT_BGR565,
4117480ba4dSJernej Skrabec 	DRM_FORMAT_BGR888,
4127480ba4dSJernej Skrabec 	DRM_FORMAT_BGRA5551,
4137480ba4dSJernej Skrabec 	DRM_FORMAT_BGRA4444,
4147480ba4dSJernej Skrabec 	DRM_FORMAT_BGRX8888,
4157480ba4dSJernej Skrabec 	DRM_FORMAT_RGB565,
4167480ba4dSJernej Skrabec 	DRM_FORMAT_RGB888,
4177480ba4dSJernej Skrabec 	DRM_FORMAT_RGBA4444,
4187480ba4dSJernej Skrabec 	DRM_FORMAT_RGBA5551,
4197480ba4dSJernej Skrabec 	DRM_FORMAT_RGBX8888,
4207480ba4dSJernej Skrabec 	DRM_FORMAT_XBGR8888,
4217480ba4dSJernej Skrabec 	DRM_FORMAT_XRGB8888,
422e1ef9006SJernej Skrabec 
423e1ef9006SJernej Skrabec 	DRM_FORMAT_NV16,
424e1ef9006SJernej Skrabec 	DRM_FORMAT_NV12,
425e1ef9006SJernej Skrabec 	DRM_FORMAT_NV21,
426e1ef9006SJernej Skrabec 	DRM_FORMAT_NV61,
427e1ef9006SJernej Skrabec 	DRM_FORMAT_UYVY,
428e1ef9006SJernej Skrabec 	DRM_FORMAT_VYUY,
429e1ef9006SJernej Skrabec 	DRM_FORMAT_YUYV,
430e1ef9006SJernej Skrabec 	DRM_FORMAT_YVYU,
431e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV411,
432e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV420,
433e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV422,
434e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV444,
435e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU411,
436e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU420,
437e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU422,
438e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU444,
4397480ba4dSJernej Skrabec };
4407480ba4dSJernej Skrabec 
4417480ba4dSJernej Skrabec struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
4427480ba4dSJernej Skrabec 					       struct sun8i_mixer *mixer,
4437480ba4dSJernej Skrabec 					       int index)
4447480ba4dSJernej Skrabec {
4455917e0bdSJernej Skrabec 	u32 supported_encodings, supported_ranges;
4467480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer;
447f88c5ee7SJernej Skrabec 	unsigned int plane_cnt;
4487480ba4dSJernej Skrabec 	int ret;
4497480ba4dSJernej Skrabec 
4507480ba4dSJernej Skrabec 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
4517480ba4dSJernej Skrabec 	if (!layer)
4527480ba4dSJernej Skrabec 		return ERR_PTR(-ENOMEM);
4537480ba4dSJernej Skrabec 
4547480ba4dSJernej Skrabec 	/* possible crtcs are set later */
4557480ba4dSJernej Skrabec 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
4567480ba4dSJernej Skrabec 				       &sun8i_vi_layer_funcs,
4577480ba4dSJernej Skrabec 				       sun8i_vi_layer_formats,
4587480ba4dSJernej Skrabec 				       ARRAY_SIZE(sun8i_vi_layer_formats),
4597480ba4dSJernej Skrabec 				       NULL, DRM_PLANE_TYPE_OVERLAY, NULL);
4607480ba4dSJernej Skrabec 	if (ret) {
4617480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't initialize layer\n");
4627480ba4dSJernej Skrabec 		return ERR_PTR(ret);
4637480ba4dSJernej Skrabec 	}
4647480ba4dSJernej Skrabec 
465f88c5ee7SJernej Skrabec 	plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
466f88c5ee7SJernej Skrabec 
467f88c5ee7SJernej Skrabec 	ret = drm_plane_create_zpos_property(&layer->plane, index,
468f88c5ee7SJernej Skrabec 					     0, plane_cnt - 1);
4697480ba4dSJernej Skrabec 	if (ret) {
4707480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't add zpos property\n");
4717480ba4dSJernej Skrabec 		return ERR_PTR(ret);
4727480ba4dSJernej Skrabec 	}
4737480ba4dSJernej Skrabec 
4745917e0bdSJernej Skrabec 	supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
4755917e0bdSJernej Skrabec 			      BIT(DRM_COLOR_YCBCR_BT709);
4765917e0bdSJernej Skrabec 
4775917e0bdSJernej Skrabec 	supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
4785917e0bdSJernej Skrabec 			   BIT(DRM_COLOR_YCBCR_FULL_RANGE);
4795917e0bdSJernej Skrabec 
4805917e0bdSJernej Skrabec 	ret = drm_plane_create_color_properties(&layer->plane,
4815917e0bdSJernej Skrabec 						supported_encodings,
4825917e0bdSJernej Skrabec 						supported_ranges,
4835917e0bdSJernej Skrabec 						DRM_COLOR_YCBCR_BT709,
4845917e0bdSJernej Skrabec 						DRM_COLOR_YCBCR_LIMITED_RANGE);
4855917e0bdSJernej Skrabec 	if (ret) {
4865917e0bdSJernej Skrabec 		dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
4875917e0bdSJernej Skrabec 		return ERR_PTR(ret);
4885917e0bdSJernej Skrabec 	}
4895917e0bdSJernej Skrabec 
4907480ba4dSJernej Skrabec 	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
4917480ba4dSJernej Skrabec 	layer->mixer = mixer;
4927480ba4dSJernej Skrabec 	layer->channel = index;
4937480ba4dSJernej Skrabec 	layer->overlay = 0;
4947480ba4dSJernej Skrabec 
4957480ba4dSJernej Skrabec 	return layer;
4967480ba4dSJernej Skrabec }
497