xref: /linux/drivers/gpu/drm/sun4i/sun8i_ui_layer.c (revision 3f0a50f345f78183f6e9b39c2f45ca5dcaa511ca)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (C) Icenowy Zheng <icenowy@aosc.io>
4  *
5  * Based on sun4i_layer.h, which is:
6  *   Copyright (C) 2015 Free Electrons
7  *   Copyright (C) 2015 NextThing Co
8  *
9  *   Maxime Ripard <maxime.ripard@free-electrons.com>
10  */
11 
12 #include <drm/drm_atomic.h>
13 #include <drm/drm_atomic_helper.h>
14 #include <drm/drm_crtc.h>
15 #include <drm/drm_fb_cma_helper.h>
16 #include <drm/drm_fourcc.h>
17 #include <drm/drm_gem_atomic_helper.h>
18 #include <drm/drm_gem_cma_helper.h>
19 #include <drm/drm_plane_helper.h>
20 #include <drm/drm_probe_helper.h>
21 
22 #include "sun8i_mixer.h"
23 #include "sun8i_ui_layer.h"
24 #include "sun8i_ui_scaler.h"
25 
26 static void sun8i_ui_layer_enable(struct sun8i_mixer *mixer, int channel,
27 				  int overlay, bool enable, unsigned int zpos,
28 				  unsigned int old_zpos)
29 {
30 	u32 val, bld_base, ch_base;
31 
32 	bld_base = sun8i_blender_base(mixer);
33 	ch_base = sun8i_channel_base(mixer, channel);
34 
35 	DRM_DEBUG_DRIVER("%sabling channel %d overlay %d\n",
36 			 enable ? "En" : "Dis", channel, overlay);
37 
38 	if (enable)
39 		val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN;
40 	else
41 		val = 0;
42 
43 	regmap_update_bits(mixer->engine.regs,
44 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
45 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_EN, val);
46 
47 	if (!enable || zpos != old_zpos) {
48 		regmap_update_bits(mixer->engine.regs,
49 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
50 				   SUN8I_MIXER_BLEND_PIPE_CTL_EN(old_zpos),
51 				   0);
52 
53 		regmap_update_bits(mixer->engine.regs,
54 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
55 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(old_zpos),
56 				   0);
57 	}
58 
59 	if (enable) {
60 		val = SUN8I_MIXER_BLEND_PIPE_CTL_EN(zpos);
61 
62 		regmap_update_bits(mixer->engine.regs,
63 				   SUN8I_MIXER_BLEND_PIPE_CTL(bld_base),
64 				   val, val);
65 
66 		val = channel << SUN8I_MIXER_BLEND_ROUTE_PIPE_SHIFT(zpos);
67 
68 		regmap_update_bits(mixer->engine.regs,
69 				   SUN8I_MIXER_BLEND_ROUTE(bld_base),
70 				   SUN8I_MIXER_BLEND_ROUTE_PIPE_MSK(zpos),
71 				   val);
72 	}
73 }
74 
75 static void sun8i_ui_layer_update_alpha(struct sun8i_mixer *mixer, int channel,
76 					int overlay, struct drm_plane *plane)
77 {
78 	u32 mask, val, ch_base;
79 
80 	ch_base = sun8i_channel_base(mixer, channel);
81 
82 	mask = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_MASK |
83 		SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MASK;
84 
85 	val = SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA(plane->state->alpha >> 8);
86 
87 	val |= (plane->state->alpha == DRM_BLEND_ALPHA_OPAQUE) ?
88 		SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_PIXEL :
89 		SUN8I_MIXER_CHAN_UI_LAYER_ATTR_ALPHA_MODE_COMBINED;
90 
91 	regmap_update_bits(mixer->engine.regs,
92 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
93 			   mask, val);
94 }
95 
96 static int sun8i_ui_layer_update_coord(struct sun8i_mixer *mixer, int channel,
97 				       int overlay, struct drm_plane *plane,
98 				       unsigned int zpos)
99 {
100 	struct drm_plane_state *state = plane->state;
101 	u32 src_w, src_h, dst_w, dst_h;
102 	u32 bld_base, ch_base;
103 	u32 outsize, insize;
104 	u32 hphase, vphase;
105 
106 	DRM_DEBUG_DRIVER("Updating UI channel %d overlay %d\n",
107 			 channel, overlay);
108 
109 	bld_base = sun8i_blender_base(mixer);
110 	ch_base = sun8i_channel_base(mixer, channel);
111 
112 	src_w = drm_rect_width(&state->src) >> 16;
113 	src_h = drm_rect_height(&state->src) >> 16;
114 	dst_w = drm_rect_width(&state->dst);
115 	dst_h = drm_rect_height(&state->dst);
116 
117 	hphase = state->src.x1 & 0xffff;
118 	vphase = state->src.y1 & 0xffff;
119 
120 	insize = SUN8I_MIXER_SIZE(src_w, src_h);
121 	outsize = SUN8I_MIXER_SIZE(dst_w, dst_h);
122 
123 	if (plane->type == DRM_PLANE_TYPE_PRIMARY) {
124 		bool interlaced = false;
125 		u32 val;
126 
127 		DRM_DEBUG_DRIVER("Primary layer, updating global size W: %u H: %u\n",
128 				 dst_w, dst_h);
129 		regmap_write(mixer->engine.regs,
130 			     SUN8I_MIXER_GLOBAL_SIZE,
131 			     outsize);
132 		regmap_write(mixer->engine.regs,
133 			     SUN8I_MIXER_BLEND_OUTSIZE(bld_base), outsize);
134 
135 		if (state->crtc)
136 			interlaced = state->crtc->state->adjusted_mode.flags
137 				& DRM_MODE_FLAG_INTERLACE;
138 
139 		if (interlaced)
140 			val = SUN8I_MIXER_BLEND_OUTCTL_INTERLACED;
141 		else
142 			val = 0;
143 
144 		regmap_update_bits(mixer->engine.regs,
145 				   SUN8I_MIXER_BLEND_OUTCTL(bld_base),
146 				   SUN8I_MIXER_BLEND_OUTCTL_INTERLACED,
147 				   val);
148 
149 		DRM_DEBUG_DRIVER("Switching display mixer interlaced mode %s\n",
150 				 interlaced ? "on" : "off");
151 	}
152 
153 	/* Set height and width */
154 	DRM_DEBUG_DRIVER("Layer source offset X: %d Y: %d\n",
155 			 state->src.x1 >> 16, state->src.y1 >> 16);
156 	DRM_DEBUG_DRIVER("Layer source size W: %d H: %d\n", src_w, src_h);
157 	regmap_write(mixer->engine.regs,
158 		     SUN8I_MIXER_CHAN_UI_LAYER_SIZE(ch_base, overlay),
159 		     insize);
160 	regmap_write(mixer->engine.regs,
161 		     SUN8I_MIXER_CHAN_UI_OVL_SIZE(ch_base),
162 		     insize);
163 
164 	if (insize != outsize || hphase || vphase) {
165 		u32 hscale, vscale;
166 
167 		DRM_DEBUG_DRIVER("HW scaling is enabled\n");
168 
169 		hscale = state->src_w / state->crtc_w;
170 		vscale = state->src_h / state->crtc_h;
171 
172 		sun8i_ui_scaler_setup(mixer, channel, src_w, src_h, dst_w,
173 				      dst_h, hscale, vscale, hphase, vphase);
174 		sun8i_ui_scaler_enable(mixer, channel, true);
175 	} else {
176 		DRM_DEBUG_DRIVER("HW scaling is not needed\n");
177 		sun8i_ui_scaler_enable(mixer, channel, false);
178 	}
179 
180 	/* Set base coordinates */
181 	DRM_DEBUG_DRIVER("Layer destination coordinates X: %d Y: %d\n",
182 			 state->dst.x1, state->dst.y1);
183 	DRM_DEBUG_DRIVER("Layer destination size W: %d H: %d\n", dst_w, dst_h);
184 	regmap_write(mixer->engine.regs,
185 		     SUN8I_MIXER_BLEND_ATTR_COORD(bld_base, zpos),
186 		     SUN8I_MIXER_COORD(state->dst.x1, state->dst.y1));
187 	regmap_write(mixer->engine.regs,
188 		     SUN8I_MIXER_BLEND_ATTR_INSIZE(bld_base, zpos),
189 		     outsize);
190 
191 	return 0;
192 }
193 
194 static int sun8i_ui_layer_update_formats(struct sun8i_mixer *mixer, int channel,
195 					 int overlay, struct drm_plane *plane)
196 {
197 	struct drm_plane_state *state = plane->state;
198 	const struct drm_format_info *fmt;
199 	u32 val, ch_base, hw_fmt;
200 	int ret;
201 
202 	ch_base = sun8i_channel_base(mixer, channel);
203 
204 	fmt = state->fb->format;
205 	ret = sun8i_mixer_drm_format_to_hw(fmt->format, &hw_fmt);
206 	if (ret || fmt->is_yuv) {
207 		DRM_DEBUG_DRIVER("Invalid format\n");
208 		return -EINVAL;
209 	}
210 
211 	val = hw_fmt << SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_OFFSET;
212 	regmap_update_bits(mixer->engine.regs,
213 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR(ch_base, overlay),
214 			   SUN8I_MIXER_CHAN_UI_LAYER_ATTR_FBFMT_MASK, val);
215 
216 	return 0;
217 }
218 
219 static int sun8i_ui_layer_update_buffer(struct sun8i_mixer *mixer, int channel,
220 					int overlay, struct drm_plane *plane)
221 {
222 	struct drm_plane_state *state = plane->state;
223 	struct drm_framebuffer *fb = state->fb;
224 	struct drm_gem_cma_object *gem;
225 	dma_addr_t paddr;
226 	u32 ch_base;
227 	int bpp;
228 
229 	ch_base = sun8i_channel_base(mixer, channel);
230 
231 	/* Get the physical address of the buffer in memory */
232 	gem = drm_fb_cma_get_gem_obj(fb, 0);
233 
234 	DRM_DEBUG_DRIVER("Using GEM @ %pad\n", &gem->paddr);
235 
236 	/* Compute the start of the displayed memory */
237 	bpp = fb->format->cpp[0];
238 	paddr = gem->paddr + fb->offsets[0];
239 
240 	/* Fixup framebuffer address for src coordinates */
241 	paddr += (state->src.x1 >> 16) * bpp;
242 	paddr += (state->src.y1 >> 16) * fb->pitches[0];
243 
244 	/* Set the line width */
245 	DRM_DEBUG_DRIVER("Layer line width: %d bytes\n", fb->pitches[0]);
246 	regmap_write(mixer->engine.regs,
247 		     SUN8I_MIXER_CHAN_UI_LAYER_PITCH(ch_base, overlay),
248 		     fb->pitches[0]);
249 
250 	DRM_DEBUG_DRIVER("Setting buffer address to %pad\n", &paddr);
251 
252 	regmap_write(mixer->engine.regs,
253 		     SUN8I_MIXER_CHAN_UI_LAYER_TOP_LADDR(ch_base, overlay),
254 		     lower_32_bits(paddr));
255 
256 	return 0;
257 }
258 
259 static int sun8i_ui_layer_atomic_check(struct drm_plane *plane,
260 				       struct drm_atomic_state *state)
261 {
262 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
263 										 plane);
264 	struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
265 	struct drm_crtc *crtc = new_plane_state->crtc;
266 	struct drm_crtc_state *crtc_state;
267 	int min_scale, max_scale;
268 
269 	if (!crtc)
270 		return 0;
271 
272 	crtc_state = drm_atomic_get_existing_crtc_state(state,
273 							crtc);
274 	if (WARN_ON(!crtc_state))
275 		return -EINVAL;
276 
277 	min_scale = DRM_PLANE_HELPER_NO_SCALING;
278 	max_scale = DRM_PLANE_HELPER_NO_SCALING;
279 
280 	if (layer->mixer->cfg->scaler_mask & BIT(layer->channel)) {
281 		min_scale = SUN8I_UI_SCALER_SCALE_MIN;
282 		max_scale = SUN8I_UI_SCALER_SCALE_MAX;
283 	}
284 
285 	return drm_atomic_helper_check_plane_state(new_plane_state,
286 						   crtc_state,
287 						   min_scale, max_scale,
288 						   true, true);
289 }
290 
291 static void sun8i_ui_layer_atomic_disable(struct drm_plane *plane,
292 					  struct drm_atomic_state *state)
293 {
294 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
295 									   plane);
296 	struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
297 	unsigned int old_zpos = old_state->normalized_zpos;
298 	struct sun8i_mixer *mixer = layer->mixer;
299 
300 	sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay, false, 0,
301 			      old_zpos);
302 }
303 
304 static void sun8i_ui_layer_atomic_update(struct drm_plane *plane,
305 					 struct drm_atomic_state *state)
306 {
307 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
308 									   plane);
309 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
310 									   plane);
311 	struct sun8i_ui_layer *layer = plane_to_sun8i_ui_layer(plane);
312 	unsigned int zpos = new_state->normalized_zpos;
313 	unsigned int old_zpos = old_state->normalized_zpos;
314 	struct sun8i_mixer *mixer = layer->mixer;
315 
316 	if (!new_state->visible) {
317 		sun8i_ui_layer_enable(mixer, layer->channel,
318 				      layer->overlay, false, 0, old_zpos);
319 		return;
320 	}
321 
322 	sun8i_ui_layer_update_coord(mixer, layer->channel,
323 				    layer->overlay, plane, zpos);
324 	sun8i_ui_layer_update_alpha(mixer, layer->channel,
325 				    layer->overlay, plane);
326 	sun8i_ui_layer_update_formats(mixer, layer->channel,
327 				      layer->overlay, plane);
328 	sun8i_ui_layer_update_buffer(mixer, layer->channel,
329 				     layer->overlay, plane);
330 	sun8i_ui_layer_enable(mixer, layer->channel, layer->overlay,
331 			      true, zpos, old_zpos);
332 }
333 
334 static const struct drm_plane_helper_funcs sun8i_ui_layer_helper_funcs = {
335 	.atomic_check	= sun8i_ui_layer_atomic_check,
336 	.atomic_disable	= sun8i_ui_layer_atomic_disable,
337 	.atomic_update	= sun8i_ui_layer_atomic_update,
338 };
339 
340 static const struct drm_plane_funcs sun8i_ui_layer_funcs = {
341 	.atomic_destroy_state	= drm_atomic_helper_plane_destroy_state,
342 	.atomic_duplicate_state	= drm_atomic_helper_plane_duplicate_state,
343 	.destroy		= drm_plane_cleanup,
344 	.disable_plane		= drm_atomic_helper_disable_plane,
345 	.reset			= drm_atomic_helper_plane_reset,
346 	.update_plane		= drm_atomic_helper_update_plane,
347 };
348 
349 static const u32 sun8i_ui_layer_formats[] = {
350 	DRM_FORMAT_ABGR1555,
351 	DRM_FORMAT_ABGR4444,
352 	DRM_FORMAT_ABGR8888,
353 	DRM_FORMAT_ARGB1555,
354 	DRM_FORMAT_ARGB4444,
355 	DRM_FORMAT_ARGB8888,
356 	DRM_FORMAT_BGR565,
357 	DRM_FORMAT_BGR888,
358 	DRM_FORMAT_BGRA5551,
359 	DRM_FORMAT_BGRA4444,
360 	DRM_FORMAT_BGRA8888,
361 	DRM_FORMAT_BGRX8888,
362 	DRM_FORMAT_RGB565,
363 	DRM_FORMAT_RGB888,
364 	DRM_FORMAT_RGBA4444,
365 	DRM_FORMAT_RGBA5551,
366 	DRM_FORMAT_RGBA8888,
367 	DRM_FORMAT_RGBX8888,
368 	DRM_FORMAT_XBGR8888,
369 	DRM_FORMAT_XRGB8888,
370 };
371 
372 static const uint64_t sun8i_layer_modifiers[] = {
373 	DRM_FORMAT_MOD_LINEAR,
374 	DRM_FORMAT_MOD_INVALID
375 };
376 
377 struct sun8i_ui_layer *sun8i_ui_layer_init_one(struct drm_device *drm,
378 					       struct sun8i_mixer *mixer,
379 					       int index)
380 {
381 	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
382 	int channel = mixer->cfg->vi_num + index;
383 	struct sun8i_ui_layer *layer;
384 	unsigned int plane_cnt;
385 	int ret;
386 
387 	layer = devm_kzalloc(drm->dev, sizeof(*layer), GFP_KERNEL);
388 	if (!layer)
389 		return ERR_PTR(-ENOMEM);
390 
391 	if (index == 0)
392 		type = DRM_PLANE_TYPE_PRIMARY;
393 
394 	/* possible crtcs are set later */
395 	ret = drm_universal_plane_init(drm, &layer->plane, 0,
396 				       &sun8i_ui_layer_funcs,
397 				       sun8i_ui_layer_formats,
398 				       ARRAY_SIZE(sun8i_ui_layer_formats),
399 				       sun8i_layer_modifiers, type, NULL);
400 	if (ret) {
401 		dev_err(drm->dev, "Couldn't initialize layer\n");
402 		return ERR_PTR(ret);
403 	}
404 
405 	plane_cnt = mixer->cfg->ui_num + mixer->cfg->vi_num;
406 
407 	ret = drm_plane_create_alpha_property(&layer->plane);
408 	if (ret) {
409 		dev_err(drm->dev, "Couldn't add alpha property\n");
410 		return ERR_PTR(ret);
411 	}
412 
413 	ret = drm_plane_create_zpos_property(&layer->plane, channel,
414 					     0, plane_cnt - 1);
415 	if (ret) {
416 		dev_err(drm->dev, "Couldn't add zpos property\n");
417 		return ERR_PTR(ret);
418 	}
419 
420 	drm_plane_helper_add(&layer->plane, &sun8i_ui_layer_helper_funcs);
421 	layer->mixer = mixer;
422 	layer->channel = channel;
423 	layer->overlay = 0;
424 
425 	return layer;
426 }
427