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>
10820c1707SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
117480ba4dSJernej Skrabec #include <drm/drm_gem_cma_helper.h>
127480ba4dSJernej Skrabec #include <drm/drm_plane_helper.h>
13fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
147480ba4dSJernej Skrabec 
15daab3d0eSJernej Skrabec #include "sun8i_csc.h"
167480ba4dSJernej Skrabec #include "sun8i_mixer.h"
171ef1380cSJernej Skrabec #include "sun8i_vi_layer.h"
18b862a648SJernej Skrabec #include "sun8i_vi_scaler.h"
197480ba4dSJernej Skrabec 
207480ba4dSJernej Skrabec static void sun8i_vi_layer_enable(struct sun8i_mixer *mixer, int channel,
21d8b3f454SPaul Kocialkowski 				  int overlay, bool enable, unsigned int zpos,
22d8b3f454SPaul Kocialkowski 				  unsigned int old_zpos)
237480ba4dSJernej Skrabec {
244b09c073SJernej Skrabec 	u32 val, bld_base, ch_base;
254b09c073SJernej Skrabec 
264b09c073SJernej Skrabec 	bld_base = sun8i_blender_base(mixer);
274b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
287480ba4dSJernej Skrabec 
297480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("%sabling VI channel %d overlay %d\n",
307480ba4dSJernej Skrabec 			 enable ? "En" : "Dis", channel, overlay);
317480ba4dSJernej Skrabec 
327480ba4dSJernej Skrabec 	if (enable)
337480ba4dSJernej Skrabec 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN;
347480ba4dSJernej Skrabec 	else
357480ba4dSJernej Skrabec 		val = 0;
367480ba4dSJernej Skrabec 
377480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
384b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
397480ba4dSJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_EN, val);
407480ba4dSJernej Skrabec 
41d8b3f454SPaul Kocialkowski 	if (!enable || zpos != old_zpos) {
42d8b3f454SPaul Kocialkowski 		regmap_update_bits(mixer->engine.regs,
434b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
44d8b3f454SPaul Kocialkowski 				   SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
45d8b3f454SPaul Kocialkowski 				   0);
46d8b3f454SPaul Kocialkowski 
47d8b3f454SPaul Kocialkowski 		regmap_update_bits(mixer->engine.regs,
484b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
49d8b3f454SPaul Kocialkowski 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
50d8b3f454SPaul Kocialkowski 				   0);
51d8b3f454SPaul Kocialkowski 	}
52d8b3f454SPaul Kocialkowski 
53f88c5ee7SJernej Skrabec 	if (enable) {
54f88c5ee7SJernej Skrabec 		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
557480ba4dSJernej Skrabec 
567480ba4dSJernej Skrabec 		regmap_update_bits(mixer->engine.regs,
574b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
584b09c073SJernej Skrabec 				   val, val);
59f88c5ee7SJernej Skrabec 
60f88c5ee7SJernej Skrabec 		val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
61f88c5ee7SJernej Skrabec 
62f88c5ee7SJernej Skrabec 		regmap_update_bits(mixer->engine.regs,
634b09c073SJernej Skrabec 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
64f88c5ee7SJernej Skrabec 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
65f88c5ee7SJernej Skrabec 				   val);
66f88c5ee7SJernej Skrabec 	}
677480ba4dSJernej Skrabec }
687480ba4dSJernej Skrabec 
69c0f6f0c4SRoman Stratiienko static void sun8i_vi_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
70c0f6f0c4SRoman Stratiienko 					int overlay, struct drm_plane *plane)
71c0f6f0c4SRoman Stratiienko {
72c0f6f0c4SRoman Stratiienko 	u32 mask, val, ch_base;
73c0f6f0c4SRoman Stratiienko 
74c0f6f0c4SRoman Stratiienko 	ch_base = sun8i_channel_base(mixer, channel);
75c0f6f0c4SRoman Stratiienko 
76c0f6f0c4SRoman Stratiienko 	if (mixer->cfg->is_de3) {
77c0f6f0c4SRoman Stratiienko 		mask = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MASK |
78c0f6f0c4SRoman Stratiienko 		       SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_MASK;
79c0f6f0c4SRoman Stratiienko 		val = SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA
80c0f6f0c4SRoman Stratiienko 			(plane->state->alpha >> 8);
81c0f6f0c4SRoman Stratiienko 
82c0f6f0c4SRoman Stratiienko 		val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
83c0f6f0c4SRoman Stratiienko 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_PIXEL :
84c0f6f0c4SRoman Stratiienko 			SUN50I_MIXER_CHAN_VI_LAYER_ATTR_ALPHA_MODE_COMBINED;
85c0f6f0c4SRoman Stratiienko 
86c0f6f0c4SRoman Stratiienko 		regmap_update_bits(mixer->engine.regs,
87c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base,
88c0f6f0c4SRoman Stratiienko 								  overlay),
89c0f6f0c4SRoman Stratiienko 				   mask, val);
90c0f6f0c4SRoman Stratiienko 	} else if (mixer->cfg->vi_num == 1) {
91c0f6f0c4SRoman Stratiienko 		regmap_update_bits(mixer->engine.regs,
92c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_REG,
93c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA_MASK,
94c0f6f0c4SRoman Stratiienko 				   SUN8I_MIXER_FCC_GLOBAL_ALPHA
95c0f6f0c4SRoman Stratiienko 					(plane->state->alpha >> 8));
96c0f6f0c4SRoman Stratiienko 	}
97c0f6f0c4SRoman Stratiienko }
98c0f6f0c4SRoman Stratiienko 
997480ba4dSJernej Skrabec static int sun8i_vi_layer_update_coord(struct sun8i_mixer *mixer, int channel,
100f88c5ee7SJernej Skrabec 				       int overlay, struct drm_plane *plane,
101f88c5ee7SJernej Skrabec 				       unsigned int zpos)
1027480ba4dSJernej Skrabec {
1037480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
1041343bd6cSJernej Skrabec 	const struct drm_format_info *format = state->fb->format;
105b862a648SJernej Skrabec 	u32 src_w, src_h, dst_w, dst_h;
1064b09c073SJernej Skrabec 	u32 bld_base, ch_base;
107b862a648SJernej Skrabec 	u32 outsize, insize;
108b862a648SJernej Skrabec 	u32 hphase, vphase;
109a7db690cSJernej Skrabec 	u32 hn = 0, hm = 0;
110a7db690cSJernej Skrabec 	u32 vn = 0, vm = 0;
111e1ef9006SJernej Skrabec 	bool subsampled;
1127480ba4dSJernej Skrabec 
1137480ba4dSJernej Skrabec 	DRM_DEBUG_DRIVER("Updating VI channel %d overlay %d\n",
1147480ba4dSJernej Skrabec 			 channel, overlay);
115b862a648SJernej Skrabec 
1164b09c073SJernej Skrabec 	bld_base = sun8i_blender_base(mixer);
1174b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
1184b09c073SJernej Skrabec 
119b862a648SJernej Skrabec 	src_w = drm_rect_width(&state->src) >> 16;
120b862a648SJernej Skrabec 	src_h = drm_rect_height(&state->src) >> 16;
121b862a648SJernej Skrabec 	dst_w = drm_rect_width(&state->dst);
122b862a648SJernej Skrabec 	dst_h = drm_rect_height(&state->dst);
123b862a648SJernej Skrabec 
124b862a648SJernej Skrabec 	hphase = state->src.x1 & 0xffff;
125b862a648SJernej Skrabec 	vphase = state->src.y1 & 0xffff;
126b862a648SJernej Skrabec 
127e1ef9006SJernej Skrabec 	/* make coordinates dividable by subsampling factor */
128e1ef9006SJernej Skrabec 	if (format->hsub > 1) {
129e1ef9006SJernej Skrabec 		int mask, remainder;
130e1ef9006SJernej Skrabec 
131e1ef9006SJernej Skrabec 		mask = format->hsub - 1;
132e1ef9006SJernej Skrabec 		remainder = (state->src.x1 >> 16) & mask;
133e1ef9006SJernej Skrabec 		src_w = (src_w + remainder) & ~mask;
134e1ef9006SJernej Skrabec 		hphase += remainder << 16;
135e1ef9006SJernej Skrabec 	}
136e1ef9006SJernej Skrabec 
137e1ef9006SJernej Skrabec 	if (format->vsub > 1) {
138e1ef9006SJernej Skrabec 		int mask, remainder;
139e1ef9006SJernej Skrabec 
140e1ef9006SJernej Skrabec 		mask = format->vsub - 1;
141e1ef9006SJernej Skrabec 		remainder = (state->src.y1 >> 16) & mask;
142e1ef9006SJernej Skrabec 		src_h = (src_h + remainder) & ~mask;
143e1ef9006SJernej Skrabec 		vphase += remainder << 16;
144e1ef9006SJernej Skrabec 	}
145e1ef9006SJernej Skrabec 
146b862a648SJernej Skrabec 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
147b862a648SJernej Skrabec 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
1487480ba4dSJernej Skrabec 
1497480ba4dSJernej Skrabec 	/* Set height and width */
150b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
151e1ef9006SJernej Skrabec 			 (state->src.x1 >> 16) & ~(format->hsub - 1),
152e1ef9006SJernej Skrabec 			 (state->src.y1 >> 16) & ~(format->vsub - 1));
153b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
1547480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1554b09c073SJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_LAYER_SIZE(ch_base, overlay),
156b862a648SJernej Skrabec 		     insize);
1577480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
1584b09c073SJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_OVL_SIZE(ch_base),
159b862a648SJernej Skrabec 		     insize);
160b862a648SJernej Skrabec 
161e1ef9006SJernej Skrabec 	/*
162e1ef9006SJernej Skrabec 	 * Scaler must be enabled for subsampled formats, so it scales
163e1ef9006SJernej Skrabec 	 * chroma to same size as luma.
164e1ef9006SJernej Skrabec 	 */
165e1ef9006SJernej Skrabec 	subsampled = format->hsub > 1 || format->vsub > 1;
166e1ef9006SJernej Skrabec 
167e1ef9006SJernej Skrabec 	if (insize != outsize || subsampled || hphase || vphase) {
168a7db690cSJernej Skrabec 		unsigned int scanline, required;
169a7db690cSJernej Skrabec 		struct drm_display_mode *mode;
170a7db690cSJernej Skrabec 		u32 hscale, vscale, fps;
171a7db690cSJernej Skrabec 		u64 ability;
172b862a648SJernej Skrabec 
173b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
174b862a648SJernej Skrabec 
175a7db690cSJernej Skrabec 		mode = &plane->state->crtc->state->mode;
176a7db690cSJernej Skrabec 		fps = (mode->clock * 1000) / (mode->vtotal * mode->htotal);
177a7db690cSJernej Skrabec 		ability = clk_get_rate(mixer->mod_clk);
178a7db690cSJernej Skrabec 		/* BSP algorithm assumes 80% efficiency of VI scaler unit */
179a7db690cSJernej Skrabec 		ability *= 80;
180a7db690cSJernej Skrabec 		do_div(ability, mode->vdisplay * fps * max(src_w, dst_w));
181a7db690cSJernej Skrabec 
182a7db690cSJernej Skrabec 		required = src_h * 100 / dst_h;
183a7db690cSJernej Skrabec 
184a7db690cSJernej Skrabec 		if (ability < required) {
185a7db690cSJernej Skrabec 			DRM_DEBUG_DRIVER("Using vertical coarse scaling\n");
186a7db690cSJernej Skrabec 			vm = src_h;
187a7db690cSJernej Skrabec 			vn = (u32)ability * dst_h / 100;
188a7db690cSJernej Skrabec 			src_h = vn;
189a7db690cSJernej Skrabec 		}
190a7db690cSJernej Skrabec 
191a7db690cSJernej Skrabec 		/* it seems that every RGB scaler has buffer for 2048 pixels */
192a7db690cSJernej Skrabec 		scanline = subsampled ? mixer->cfg->scanline_yuv : 2048;
193a7db690cSJernej Skrabec 
194a7db690cSJernej Skrabec 		if (src_w > scanline) {
195a7db690cSJernej Skrabec 			DRM_DEBUG_DRIVER("Using horizontal coarse scaling\n");
196a7db690cSJernej Skrabec 			hm = src_w;
197a7db690cSJernej Skrabec 			hn = scanline;
198a7db690cSJernej Skrabec 			src_w = hn;
199a7db690cSJernej Skrabec 		}
200a7db690cSJernej Skrabec 
201a7db690cSJernej Skrabec 		hscale = (src_w << 16) / dst_w;
202a7db690cSJernej Skrabec 		vscale = (src_h << 16) / dst_h;
203b862a648SJernej Skrabec 
204b862a648SJernej Skrabec 		sun8i_vi_scaler_setup(mixer, channel, src_w, src_h, dst_w,
2051343bd6cSJernej Skrabec 				      dst_h, hscale, vscale, hphase, vphase,
2061343bd6cSJernej Skrabec 				      format);
207b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, true);
208b862a648SJernej Skrabec 	} else {
209b862a648SJernej Skrabec 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
210b862a648SJernej Skrabec 		sun8i_vi_scaler_enable(mixer, channel, false);
211b862a648SJernej Skrabec 	}
2127480ba4dSJernej Skrabec 
213a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
214a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_HDS_Y(ch_base),
215a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
216a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
217a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
218a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_HDS_UV(ch_base),
219a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(hn) |
220a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(hm));
221a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
222a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_VDS_Y(ch_base),
223a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
224a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
225a7db690cSJernej Skrabec 	regmap_write(mixer->engine.regs,
226a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_VDS_UV(ch_base),
227a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_N(vn) |
228a7db690cSJernej Skrabec 		     SUN8I_MIXER_CHAN_VI_DS_M(vm));
229a7db690cSJernej Skrabec 
2307480ba4dSJernej Skrabec 	/* Set base coordinates */
231b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
2327480ba4dSJernej Skrabec 			 state->dst.x1, state->dst.y1);
233b862a648SJernej Skrabec 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
2347480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
2354b09c073SJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
2367480ba4dSJernej Skrabec 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
2377480ba4dSJernej Skrabec 	regmap_write(mixer->engine.regs,
2384b09c073SJernej Skrabec 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
239b862a648SJernej Skrabec 		     outsize);
2407480ba4dSJernej Skrabec 
2417480ba4dSJernej Skrabec 	return 0;
2427480ba4dSJernej Skrabec }
2437480ba4dSJernej Skrabec 
2440ee9f600SJernej Skrabec static u32 sun8i_vi_layer_get_csc_mode(const struct drm_format_info *format)
245daab3d0eSJernej Skrabec {
246daab3d0eSJernej Skrabec 	if (!format->is_yuv)
247daab3d0eSJernej Skrabec 		return SUN8I_CSC_MODE_OFF;
248daab3d0eSJernej Skrabec 
249daab3d0eSJernej Skrabec 	switch (format->format) {
250daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU411:
251daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU420:
252daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU422:
253daab3d0eSJernej Skrabec 	case DRM_FORMAT_YVU444:
254daab3d0eSJernej Skrabec 		return SUN8I_CSC_MODE_YVU2RGB;
255daab3d0eSJernej Skrabec 	default:
256daab3d0eSJernej Skrabec 		return SUN8I_CSC_MODE_YUV2RGB;
257daab3d0eSJernej Skrabec 	}
258daab3d0eSJernej Skrabec }
259daab3d0eSJernej Skrabec 
2607480ba4dSJernej Skrabec static int sun8i_vi_layer_update_formats(struct sun8i_mixer *mixer, int channel,
2617480ba4dSJernej Skrabec 					 int overlay, struct drm_plane *plane)
2627480ba4dSJernej Skrabec {
2637480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
2647831112eSJernej Skrabec 	u32 val, ch_base, csc_mode, hw_fmt;
265a9a75359SJernej Skrabec 	const struct drm_format_info *fmt;
2667831112eSJernej Skrabec 	int ret;
2674b09c073SJernej Skrabec 
2684b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
2697480ba4dSJernej Skrabec 
270a9a75359SJernej Skrabec 	fmt = state->fb->format;
2717831112eSJernej Skrabec 	ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
2727831112eSJernej Skrabec 	if (ret) {
2737480ba4dSJernej Skrabec 		DRM_DEBUG_DRIVER("Invalid format\n");
2747831112eSJernej Skrabec 		return ret;
2757480ba4dSJernej Skrabec 	}
2767480ba4dSJernej Skrabec 
2777831112eSJernej Skrabec 	val = hw_fmt << SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_OFFSET;
2787480ba4dSJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
2794b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
280e1ef9006SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_FBFMT_MASK, val);
281e1ef9006SJernej Skrabec 
282daab3d0eSJernej Skrabec 	csc_mode = sun8i_vi_layer_get_csc_mode(fmt);
283daab3d0eSJernej Skrabec 	if (csc_mode != SUN8I_CSC_MODE_OFF) {
284daab3d0eSJernej Skrabec 		sun8i_csc_set_ccsc_coefficients(mixer, channel, csc_mode,
285b72cb0dcSJernej Skrabec 						state->color_encoding,
286b72cb0dcSJernej Skrabec 						state->color_range);
287e1ef9006SJernej Skrabec 		sun8i_csc_enable_ccsc(mixer, channel, true);
288e1ef9006SJernej Skrabec 	} else {
289e1ef9006SJernej Skrabec 		sun8i_csc_enable_ccsc(mixer, channel, false);
290e1ef9006SJernej Skrabec 	}
291e1ef9006SJernej Skrabec 
292a9a75359SJernej Skrabec 	if (!fmt->is_yuv)
293e1ef9006SJernej Skrabec 		val = SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE;
294e1ef9006SJernej Skrabec 	else
295e1ef9006SJernej Skrabec 		val = 0;
296e1ef9006SJernej Skrabec 
297e1ef9006SJernej Skrabec 	regmap_update_bits(mixer->engine.regs,
2984b09c073SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR(ch_base, overlay),
299e1ef9006SJernej Skrabec 			   SUN8I_MIXER_CHAN_VI_LAYER_ATTR_RGB_MODE, val);
3007480ba4dSJernej Skrabec 
3017480ba4dSJernej Skrabec 	return 0;
3027480ba4dSJernej Skrabec }
3037480ba4dSJernej Skrabec 
3047480ba4dSJernej Skrabec static int sun8i_vi_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
3057480ba4dSJernej Skrabec 					int overlay, struct drm_plane *plane)
3067480ba4dSJernej Skrabec {
3077480ba4dSJernej Skrabec 	struct drm_plane_state *state = plane->state;
3087480ba4dSJernej Skrabec 	struct drm_framebuffer *fb = state->fb;
309e1ef9006SJernej Skrabec 	const struct drm_format_info *format = fb->format;
3107480ba4dSJernej Skrabec 	struct drm_gem_cma_object *gem;
311e1ef9006SJernej Skrabec 	u32 dx, dy, src_x, src_y;
3127480ba4dSJernej Skrabec 	dma_addr_t paddr;
3134b09c073SJernej Skrabec 	u32 ch_base;
314e1ef9006SJernej Skrabec 	int i;
3157480ba4dSJernej Skrabec 
3164b09c073SJernej Skrabec 	ch_base = sun8i_channel_base(mixer, channel);
3174b09c073SJernej Skrabec 
318e1ef9006SJernej Skrabec 	/* Adjust x and y to be dividable by subsampling factor */
319e1ef9006SJernej Skrabec 	src_x = (state->src.x1 >> 16) & ~(format->hsub - 1);
320e1ef9006SJernej Skrabec 	src_y = (state->src.y1 >> 16) & ~(format->vsub - 1);
321e1ef9006SJernej Skrabec 
322e1ef9006SJernej Skrabec 	for (i = 0; i < format->num_planes; i++) {
3237480ba4dSJernej Skrabec 		/* Get the physical address of the buffer in memory */
324e1ef9006SJernej Skrabec 		gem = drm_fb_cma_get_gem_obj(fb, i);
3257480ba4dSJernej Skrabec 
3267480ba4dSJernej Skrabec 		DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
3277480ba4dSJernej Skrabec 
3287480ba4dSJernej Skrabec 		/* Compute the start of the displayed memory */
329e1ef9006SJernej Skrabec 		paddr = gem->paddr + fb->offsets[i];
330e1ef9006SJernej Skrabec 
331e1ef9006SJernej Skrabec 		dx = src_x;
332e1ef9006SJernej Skrabec 		dy = src_y;
333e1ef9006SJernej Skrabec 
334e1ef9006SJernej Skrabec 		if (i > 0) {
335e1ef9006SJernej Skrabec 			dx /= format->hsub;
336e1ef9006SJernej Skrabec 			dy /= format->vsub;
337e1ef9006SJernej Skrabec 		}
3387480ba4dSJernej Skrabec 
3397480ba4dSJernej Skrabec 		/* Fixup framebuffer address for src coordinates */
340e1ef9006SJernej Skrabec 		paddr += dx * format->cpp[i];
341e1ef9006SJernej Skrabec 		paddr += dy * fb->pitches[i];
3427480ba4dSJernej Skrabec 
3437480ba4dSJernej Skrabec 		/* Set the line width */
344e1ef9006SJernej Skrabec 		DRM_DEBUG_DRIVER("Layer %d. line width: %d bytes\n",
345e1ef9006SJernej Skrabec 				 i + 1, fb->pitches[i]);
3467480ba4dSJernej Skrabec 		regmap_write(mixer->engine.regs,
3474b09c073SJernej Skrabec 			     SUN8I_MIXER_CHAN_VI_LAYER_PITCH(ch_base,
348e1ef9006SJernej Skrabec 							     overlay, i),
349e1ef9006SJernej Skrabec 			     fb->pitches[i]);
3507480ba4dSJernej Skrabec 
351e1ef9006SJernej Skrabec 		DRM_DEBUG_DRIVER("Setting %d. buffer address to %pad\n",
352e1ef9006SJernej Skrabec 				 i + 1, &paddr);
3537480ba4dSJernej Skrabec 
3547480ba4dSJernej Skrabec 		regmap_write(mixer->engine.regs,
3554b09c073SJernej Skrabec 			     SUN8I_MIXER_CHAN_VI_LAYER_TOP_LADDR(ch_base,
356e1ef9006SJernej Skrabec 								 overlay, i),
3577480ba4dSJernej Skrabec 			     lower_32_bits(paddr));
358e1ef9006SJernej Skrabec 	}
3597480ba4dSJernej Skrabec 
3607480ba4dSJernej Skrabec 	return 0;
3617480ba4dSJernej Skrabec }
3627480ba4dSJernej Skrabec 
3637480ba4dSJernej Skrabec static int sun8i_vi_layer_atomic_check(struct drm_plane *plane,
364*7c11b99aSMaxime Ripard 				       struct drm_atomic_state *state)
3657480ba4dSJernej Skrabec {
366*7c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
367*7c11b99aSMaxime Ripard 										 plane);
368b862a648SJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
369ba5c1649SMaxime Ripard 	struct drm_crtc *crtc = new_plane_state->crtc;
3707480ba4dSJernej Skrabec 	struct drm_crtc_state *crtc_state;
371b862a648SJernej Skrabec 	int min_scale, max_scale;
3727480ba4dSJernej Skrabec 
3737480ba4dSJernej Skrabec 	if (!crtc)
3747480ba4dSJernej Skrabec 		return 0;
3757480ba4dSJernej Skrabec 
376ba5c1649SMaxime Ripard 	crtc_state = drm_atomic_get_existing_crtc_state(new_plane_state->state,
377ba5c1649SMaxime Ripard 							crtc);
3787480ba4dSJernej Skrabec 	if (WARN_ON(!crtc_state))
3797480ba4dSJernej Skrabec 		return -EINVAL;
3807480ba4dSJernej Skrabec 
381bc29489fSJernej Skrabec 	min_scale = DRM_PLANE_HELPER_NO_SCALING;
382bc29489fSJernej Skrabec 	max_scale = DRM_PLANE_HELPER_NO_SCALING;
383bc29489fSJernej Skrabec 
384b862a648SJernej Skrabec 	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
385b862a648SJernej Skrabec 		min_scale = SUN8I_VI_SCALER_SCALE_MIN;
386b862a648SJernej Skrabec 		max_scale = SUN8I_VI_SCALER_SCALE_MAX;
387b862a648SJernej Skrabec 	}
388b862a648SJernej Skrabec 
389ba5c1649SMaxime Ripard 	return drm_atomic_helper_check_plane_state(new_plane_state,
390ba5c1649SMaxime Ripard 						   crtc_state,
391b862a648SJernej Skrabec 						   min_scale, max_scale,
3927480ba4dSJernej Skrabec 						   true, true);
3937480ba4dSJernej Skrabec }
3947480ba4dSJernej Skrabec 
3957480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_disable(struct drm_plane *plane,
3967480ba4dSJernej Skrabec 					  struct drm_plane_state *old_state)
3977480ba4dSJernej Skrabec {
3987480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
399d8b3f454SPaul Kocialkowski 	unsigned int old_zpos = old_state->normalized_zpos;
4007480ba4dSJernej Skrabec 	struct sun8i_mixer *mixer = layer->mixer;
4017480ba4dSJernej Skrabec 
402d8b3f454SPaul Kocialkowski 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
403d8b3f454SPaul Kocialkowski 			      old_zpos);
4047480ba4dSJernej Skrabec }
4057480ba4dSJernej Skrabec 
4067480ba4dSJernej Skrabec static void sun8i_vi_layer_atomic_update(struct drm_plane *plane,
4077480ba4dSJernej Skrabec 					 struct drm_plane_state *old_state)
4087480ba4dSJernej Skrabec {
4097480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer = plane_to_sun8i_vi_layer(plane);
410f88c5ee7SJernej Skrabec 	unsigned int zpos = plane->state->normalized_zpos;
411d8b3f454SPaul Kocialkowski 	unsigned int old_zpos = old_state->normalized_zpos;
4127480ba4dSJernej Skrabec 	struct sun8i_mixer *mixer = layer->mixer;
4137480ba4dSJernej Skrabec 
4147480ba4dSJernej Skrabec 	if (!plane->state->visible) {
4157480ba4dSJernej Skrabec 		sun8i_vi_layer_enable(mixer, layer->channel,
416d8b3f454SPaul Kocialkowski 				      layer->overlay, false, 0, old_zpos);
4177480ba4dSJernej Skrabec 		return;
4187480ba4dSJernej Skrabec 	}
4197480ba4dSJernej Skrabec 
4207480ba4dSJernej Skrabec 	sun8i_vi_layer_update_coord(mixer, layer->channel,
421f88c5ee7SJernej Skrabec 				    layer->overlay, plane, zpos);
422c0f6f0c4SRoman Stratiienko 	sun8i_vi_layer_update_alpha(mixer, layer->channel,
423c0f6f0c4SRoman Stratiienko 				    layer->overlay, plane);
4247480ba4dSJernej Skrabec 	sun8i_vi_layer_update_formats(mixer, layer->channel,
4257480ba4dSJernej Skrabec 				      layer->overlay, plane);
4267480ba4dSJernej Skrabec 	sun8i_vi_layer_update_buffer(mixer, layer->channel,
4277480ba4dSJernej Skrabec 				     layer->overlay, plane);
428f88c5ee7SJernej Skrabec 	sun8i_vi_layer_enable(mixer, layer->channel, layer->overlay,
429d8b3f454SPaul Kocialkowski 			      true, zpos, old_zpos);
4307480ba4dSJernej Skrabec }
4317480ba4dSJernej Skrabec 
432f13478c9SRikard Falkeborn static const struct drm_plane_helper_funcs sun8i_vi_layer_helper_funcs = {
433820c1707SThomas Zimmermann 	.prepare_fb	= drm_gem_plane_helper_prepare_fb,
4347480ba4dSJernej Skrabec 	.atomic_check	= sun8i_vi_layer_atomic_check,
4357480ba4dSJernej Skrabec 	.atomic_disable	= sun8i_vi_layer_atomic_disable,
4367480ba4dSJernej Skrabec 	.atomic_update	= sun8i_vi_layer_atomic_update,
4377480ba4dSJernej Skrabec };
4387480ba4dSJernej Skrabec 
4397480ba4dSJernej Skrabec static const struct drm_plane_funcs sun8i_vi_layer_funcs = {
4407480ba4dSJernej Skrabec 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
4417480ba4dSJernej Skrabec 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
4427480ba4dSJernej Skrabec 	.destroy		= drm_plane_cleanup,
4437480ba4dSJernej Skrabec 	.disable_plane		= drm_atomic_helper_disable_plane,
4447480ba4dSJernej Skrabec 	.reset			= drm_atomic_helper_plane_reset,
4457480ba4dSJernej Skrabec 	.update_plane		= drm_atomic_helper_update_plane,
4467480ba4dSJernej Skrabec };
4477480ba4dSJernej Skrabec 
4487480ba4dSJernej Skrabec /*
44920896ef1SJernej Skrabec  * While DE2 VI layer supports same RGB formats as UI layer, alpha
45020896ef1SJernej Skrabec  * channel is ignored. This structure lists all unique variants
45120896ef1SJernej Skrabec  * where alpha channel is replaced with "don't care" (X) channel.
4527480ba4dSJernej Skrabec  */
4537480ba4dSJernej Skrabec static const u32 sun8i_vi_layer_formats[] = {
4547480ba4dSJernej Skrabec 	DRM_FORMAT_BGR565,
4557480ba4dSJernej Skrabec 	DRM_FORMAT_BGR888,
45620896ef1SJernej Skrabec 	DRM_FORMAT_BGRX4444,
45720896ef1SJernej Skrabec 	DRM_FORMAT_BGRX5551,
4587480ba4dSJernej Skrabec 	DRM_FORMAT_BGRX8888,
4597480ba4dSJernej Skrabec 	DRM_FORMAT_RGB565,
4607480ba4dSJernej Skrabec 	DRM_FORMAT_RGB888,
46120896ef1SJernej Skrabec 	DRM_FORMAT_RGBX4444,
46220896ef1SJernej Skrabec 	DRM_FORMAT_RGBX5551,
4637480ba4dSJernej Skrabec 	DRM_FORMAT_RGBX8888,
46420896ef1SJernej Skrabec 	DRM_FORMAT_XBGR1555,
46520896ef1SJernej Skrabec 	DRM_FORMAT_XBGR4444,
4667480ba4dSJernej Skrabec 	DRM_FORMAT_XBGR8888,
46720896ef1SJernej Skrabec 	DRM_FORMAT_XRGB1555,
46820896ef1SJernej Skrabec 	DRM_FORMAT_XRGB4444,
4697480ba4dSJernej Skrabec 	DRM_FORMAT_XRGB8888,
470e1ef9006SJernej Skrabec 
471e1ef9006SJernej Skrabec 	DRM_FORMAT_NV16,
472e1ef9006SJernej Skrabec 	DRM_FORMAT_NV12,
473e1ef9006SJernej Skrabec 	DRM_FORMAT_NV21,
474e1ef9006SJernej Skrabec 	DRM_FORMAT_NV61,
475e1ef9006SJernej Skrabec 	DRM_FORMAT_UYVY,
476e1ef9006SJernej Skrabec 	DRM_FORMAT_VYUY,
477e1ef9006SJernej Skrabec 	DRM_FORMAT_YUYV,
478e1ef9006SJernej Skrabec 	DRM_FORMAT_YVYU,
479e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV411,
480e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV420,
481e1ef9006SJernej Skrabec 	DRM_FORMAT_YUV422,
482e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU411,
483e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU420,
484e1ef9006SJernej Skrabec 	DRM_FORMAT_YVU422,
4857480ba4dSJernej Skrabec };
4867480ba4dSJernej Skrabec 
487169ca4b3SJernej Skrabec static const u32 sun8i_vi_layer_de3_formats[] = {
488169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR1555,
489169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR2101010,
490169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR4444,
491169ca4b3SJernej Skrabec 	DRM_FORMAT_ABGR8888,
492169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB1555,
493169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB2101010,
494169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB4444,
495169ca4b3SJernej Skrabec 	DRM_FORMAT_ARGB8888,
496169ca4b3SJernej Skrabec 	DRM_FORMAT_BGR565,
497169ca4b3SJernej Skrabec 	DRM_FORMAT_BGR888,
498169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA1010102,
499169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA5551,
500169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA4444,
501169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRA8888,
502169ca4b3SJernej Skrabec 	DRM_FORMAT_BGRX8888,
503169ca4b3SJernej Skrabec 	DRM_FORMAT_RGB565,
504169ca4b3SJernej Skrabec 	DRM_FORMAT_RGB888,
505169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA1010102,
506169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA4444,
507169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA5551,
508169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBA8888,
509169ca4b3SJernej Skrabec 	DRM_FORMAT_RGBX8888,
510169ca4b3SJernej Skrabec 	DRM_FORMAT_XBGR8888,
511169ca4b3SJernej Skrabec 	DRM_FORMAT_XRGB8888,
512169ca4b3SJernej Skrabec 
513169ca4b3SJernej Skrabec 	DRM_FORMAT_NV16,
514169ca4b3SJernej Skrabec 	DRM_FORMAT_NV12,
515169ca4b3SJernej Skrabec 	DRM_FORMAT_NV21,
516169ca4b3SJernej Skrabec 	DRM_FORMAT_NV61,
517169ca4b3SJernej Skrabec 	DRM_FORMAT_P010,
518169ca4b3SJernej Skrabec 	DRM_FORMAT_P210,
519169ca4b3SJernej Skrabec 	DRM_FORMAT_UYVY,
520169ca4b3SJernej Skrabec 	DRM_FORMAT_VYUY,
521169ca4b3SJernej Skrabec 	DRM_FORMAT_YUYV,
522169ca4b3SJernej Skrabec 	DRM_FORMAT_YVYU,
523169ca4b3SJernej Skrabec 	DRM_FORMAT_YUV411,
524169ca4b3SJernej Skrabec 	DRM_FORMAT_YUV420,
525169ca4b3SJernej Skrabec 	DRM_FORMAT_YUV422,
526169ca4b3SJernej Skrabec 	DRM_FORMAT_YVU411,
527169ca4b3SJernej Skrabec 	DRM_FORMAT_YVU420,
528169ca4b3SJernej Skrabec 	DRM_FORMAT_YVU422,
529169ca4b3SJernej Skrabec };
530169ca4b3SJernej Skrabec 
5317480ba4dSJernej Skrabec struct sun8i_vi_layer *sun8i_vi_layer_init_one(struct drm_device *drm,
5327480ba4dSJernej Skrabec 					       struct sun8i_mixer *mixer,
5337480ba4dSJernej Skrabec 					       int index)
5347480ba4dSJernej Skrabec {
5355917e0bdSJernej Skrabec 	u32 supported_encodings, supported_ranges;
536169ca4b3SJernej Skrabec 	unsigned int plane_cnt, format_count;
5377480ba4dSJernej Skrabec 	struct sun8i_vi_layer *layer;
538169ca4b3SJernej Skrabec 	const u32 *formats;
5397480ba4dSJernej Skrabec 	int ret;
5407480ba4dSJernej Skrabec 
5417480ba4dSJernej Skrabec 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
5427480ba4dSJernej Skrabec 	if (!layer)
5437480ba4dSJernej Skrabec 		return ERR_PTR(-ENOMEM);
5447480ba4dSJernej Skrabec 
545169ca4b3SJernej Skrabec 	if (mixer->cfg->is_de3) {
546169ca4b3SJernej Skrabec 		formats = sun8i_vi_layer_de3_formats;
547169ca4b3SJernej Skrabec 		format_count = ARRAY_SIZE(sun8i_vi_layer_de3_formats);
548169ca4b3SJernej Skrabec 	} else {
549169ca4b3SJernej Skrabec 		formats = sun8i_vi_layer_formats;
550169ca4b3SJernej Skrabec 		format_count = ARRAY_SIZE(sun8i_vi_layer_formats);
551169ca4b3SJernej Skrabec 	}
552169ca4b3SJernej Skrabec 
5537480ba4dSJernej Skrabec 	/* possible crtcs are set later */
5547480ba4dSJernej Skrabec 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
5557480ba4dSJernej Skrabec 				       &sun8i_vi_layer_funcs,
556169ca4b3SJernej Skrabec 				       formats, format_count,
5577480ba4dSJernej Skrabec 				       NULL, DRM_PLANE_TYPE_OVERLAY, NULL);
5587480ba4dSJernej Skrabec 	if (ret) {
5597480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't initialize layer\n");
5607480ba4dSJernej Skrabec 		return ERR_PTR(ret);
5617480ba4dSJernej Skrabec 	}
5627480ba4dSJernej Skrabec 
563f88c5ee7SJernej Skrabec 	plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
564f88c5ee7SJernej Skrabec 
565c0f6f0c4SRoman Stratiienko 	if (mixer->cfg->vi_num == 1 || mixer->cfg->is_de3) {
566c0f6f0c4SRoman Stratiienko 		ret = drm_plane_create_alpha_property(&layer->plane);
567c0f6f0c4SRoman Stratiienko 		if (ret) {
568c0f6f0c4SRoman Stratiienko 			dev_err(drm->dev, "Couldn't add alpha property\n");
569c0f6f0c4SRoman Stratiienko 			return ERR_PTR(ret);
570c0f6f0c4SRoman Stratiienko 		}
571c0f6f0c4SRoman Stratiienko 	}
572c0f6f0c4SRoman Stratiienko 
573f88c5ee7SJernej Skrabec 	ret = drm_plane_create_zpos_property(&layer->plane, index,
574f88c5ee7SJernej Skrabec 					     0, plane_cnt - 1);
5757480ba4dSJernej Skrabec 	if (ret) {
5767480ba4dSJernej Skrabec 		dev_err(drm->dev, "Couldn't add zpos property\n");
5777480ba4dSJernej Skrabec 		return ERR_PTR(ret);
5787480ba4dSJernej Skrabec 	}
5797480ba4dSJernej Skrabec 
5805917e0bdSJernej Skrabec 	supported_encodings = BIT(DRM_COLOR_YCBCR_BT601) |
5815917e0bdSJernej Skrabec 			      BIT(DRM_COLOR_YCBCR_BT709);
5820cf1d704SJernej Skrabec 	if (mixer->cfg->is_de3)
5830cf1d704SJernej Skrabec 		supported_encodings |= BIT(DRM_COLOR_YCBCR_BT2020);
5845917e0bdSJernej Skrabec 
5855917e0bdSJernej Skrabec 	supported_ranges = BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
5865917e0bdSJernej Skrabec 			   BIT(DRM_COLOR_YCBCR_FULL_RANGE);
5875917e0bdSJernej Skrabec 
5885917e0bdSJernej Skrabec 	ret = drm_plane_create_color_properties(&layer->plane,
5895917e0bdSJernej Skrabec 						supported_encodings,
5905917e0bdSJernej Skrabec 						supported_ranges,
5915917e0bdSJernej Skrabec 						DRM_COLOR_YCBCR_BT709,
5925917e0bdSJernej Skrabec 						DRM_COLOR_YCBCR_LIMITED_RANGE);
5935917e0bdSJernej Skrabec 	if (ret) {
5945917e0bdSJernej Skrabec 		dev_err(drm->dev, "Couldn't add encoding and range properties!\n");
5955917e0bdSJernej Skrabec 		return ERR_PTR(ret);
5965917e0bdSJernej Skrabec 	}
5975917e0bdSJernej Skrabec 
5987480ba4dSJernej Skrabec 	drm_plane_helper_add(&layer->plane, &sun8i_vi_layer_helper_funcs);
5997480ba4dSJernej Skrabec 	layer->mixer = mixer;
6007480ba4dSJernej Skrabec 	layer->channel = index;
6017480ba4dSJernej Skrabec 	layer->overlay = 0;
6027480ba4dSJernej Skrabec 
6037480ba4dSJernej Skrabec 	return layer;
6047480ba4dSJernej Skrabec }
605