xref: /linux/drivers/gpu/drm/omapdrm/omap_plane.c (revision e7d759f31ca295d589f7420719c311870bb3166f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/
4  * Author: Rob Clark <rob.clark@linaro.org>
5  */
6 
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_atomic_helper.h>
9 #include <drm/drm_blend.h>
10 #include <drm/drm_gem_atomic_helper.h>
11 #include <drm/drm_fourcc.h>
12 #include <drm/drm_framebuffer.h>
13 
14 #include "omap_dmm_tiler.h"
15 #include "omap_drv.h"
16 
17 /*
18  * plane funcs
19  */
20 
21 #define to_omap_plane_state(x) container_of(x, struct omap_plane_state, base)
22 
23 struct omap_plane_state {
24 	/* Must be first. */
25 	struct drm_plane_state base;
26 
27 	struct omap_hw_overlay *overlay;
28 	struct omap_hw_overlay *r_overlay;  /* right overlay */
29 };
30 
31 #define to_omap_plane(x) container_of(x, struct omap_plane, base)
32 
33 struct omap_plane {
34 	struct drm_plane base;
35 	enum omap_plane_id id;
36 };
37 
38 bool is_omap_plane_dual_overlay(struct drm_plane_state *state)
39 {
40 	struct omap_plane_state *omap_state = to_omap_plane_state(state);
41 
42 	return !!omap_state->r_overlay;
43 }
44 
45 static int omap_plane_prepare_fb(struct drm_plane *plane,
46 				 struct drm_plane_state *new_state)
47 {
48 	if (!new_state->fb)
49 		return 0;
50 
51 	drm_gem_plane_helper_prepare_fb(plane, new_state);
52 
53 	return omap_framebuffer_pin(new_state->fb);
54 }
55 
56 static void omap_plane_cleanup_fb(struct drm_plane *plane,
57 				  struct drm_plane_state *old_state)
58 {
59 	if (old_state->fb)
60 		omap_framebuffer_unpin(old_state->fb);
61 }
62 
63 static void omap_plane_atomic_update(struct drm_plane *plane,
64 				     struct drm_atomic_state *state)
65 {
66 	struct omap_drm_private *priv = plane->dev->dev_private;
67 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
68 									   plane);
69 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
70 									   plane);
71 	struct omap_plane_state *new_omap_state;
72 	struct omap_plane_state *old_omap_state;
73 	struct omap_overlay_info info, r_info;
74 	enum omap_plane_id ovl_id, r_ovl_id;
75 	int ret;
76 	bool dual_ovl;
77 
78 	new_omap_state = to_omap_plane_state(new_state);
79 	old_omap_state = to_omap_plane_state(old_state);
80 
81 	dual_ovl = is_omap_plane_dual_overlay(new_state);
82 
83 	/* Cleanup previously held overlay if needed */
84 	if (old_omap_state->overlay)
85 		omap_overlay_update_state(priv, old_omap_state->overlay);
86 	if (old_omap_state->r_overlay)
87 		omap_overlay_update_state(priv, old_omap_state->r_overlay);
88 
89 	if (!new_omap_state->overlay) {
90 		DBG("[PLANE:%d:%s] no overlay attached", plane->base.id, plane->name);
91 		return;
92 	}
93 
94 	ovl_id = new_omap_state->overlay->id;
95 	DBG("%s, crtc=%p fb=%p", plane->name, new_state->crtc,
96 	    new_state->fb);
97 
98 	memset(&info, 0, sizeof(info));
99 	info.rotation_type = OMAP_DSS_ROT_NONE;
100 	info.rotation = DRM_MODE_ROTATE_0;
101 	info.global_alpha = new_state->alpha >> 8;
102 	info.zorder = new_state->normalized_zpos;
103 	if (new_state->pixel_blend_mode == DRM_MODE_BLEND_PREMULTI)
104 		info.pre_mult_alpha = 1;
105 	else
106 		info.pre_mult_alpha = 0;
107 	info.color_encoding = new_state->color_encoding;
108 	info.color_range = new_state->color_range;
109 
110 	r_info = info;
111 
112 	/* update scanout: */
113 	omap_framebuffer_update_scanout(new_state->fb, new_state, &info,
114 					dual_ovl ? &r_info : NULL);
115 
116 	DBG("%s: %dx%d -> %dx%d (%d)",
117 			new_omap_state->overlay->name, info.width, info.height,
118 			info.out_width, info.out_height, info.screen_width);
119 	DBG("%d,%d %pad %pad", info.pos_x, info.pos_y,
120 			&info.paddr, &info.p_uv_addr);
121 
122 	if (dual_ovl) {
123 		r_ovl_id = new_omap_state->r_overlay->id;
124 		/*
125 		 * If the current plane uses 2 hw planes the very next
126 		 * zorder is used by the r_overlay so we just use the
127 		 * main overlay zorder + 1
128 		 */
129 		r_info.zorder = info.zorder + 1;
130 
131 		DBG("%s: %dx%d -> %dx%d (%d)",
132 		    new_omap_state->r_overlay->name,
133 		    r_info.width, r_info.height,
134 		    r_info.out_width, r_info.out_height, r_info.screen_width);
135 		DBG("%d,%d %pad %pad", r_info.pos_x, r_info.pos_y,
136 		    &r_info.paddr, &r_info.p_uv_addr);
137 	}
138 
139 	/* and finally, update omapdss: */
140 	ret = dispc_ovl_setup(priv->dispc, ovl_id, &info,
141 			      omap_crtc_timings(new_state->crtc), false,
142 			      omap_crtc_channel(new_state->crtc));
143 	if (ret) {
144 		dev_err(plane->dev->dev, "Failed to setup plane %s\n",
145 			plane->name);
146 		dispc_ovl_enable(priv->dispc, ovl_id, false);
147 		return;
148 	}
149 
150 	dispc_ovl_enable(priv->dispc, ovl_id, true);
151 
152 	if (dual_ovl) {
153 		ret = dispc_ovl_setup(priv->dispc, r_ovl_id, &r_info,
154 				      omap_crtc_timings(new_state->crtc), false,
155 				      omap_crtc_channel(new_state->crtc));
156 		if (ret) {
157 			dev_err(plane->dev->dev, "Failed to setup plane right-overlay %s\n",
158 				plane->name);
159 			dispc_ovl_enable(priv->dispc, r_ovl_id, false);
160 			dispc_ovl_enable(priv->dispc, ovl_id, false);
161 			return;
162 		}
163 
164 		dispc_ovl_enable(priv->dispc, r_ovl_id, true);
165 	}
166 }
167 
168 static void omap_plane_atomic_disable(struct drm_plane *plane,
169 				      struct drm_atomic_state *state)
170 {
171 	struct omap_drm_private *priv = plane->dev->dev_private;
172 	struct omap_plane *omap_plane = to_omap_plane(plane);
173 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
174 									   plane);
175 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
176 									   plane);
177 	struct omap_plane_state *new_omap_state;
178 	struct omap_plane_state *old_omap_state;
179 
180 	new_omap_state = to_omap_plane_state(new_state);
181 	old_omap_state = to_omap_plane_state(old_state);
182 
183 	if (!old_omap_state->overlay)
184 		return;
185 
186 	new_state->rotation = DRM_MODE_ROTATE_0;
187 	new_state->zpos = plane->type == DRM_PLANE_TYPE_PRIMARY ? 0 : omap_plane->id;
188 
189 	omap_overlay_update_state(priv, old_omap_state->overlay);
190 	new_omap_state->overlay = NULL;
191 
192 	if (is_omap_plane_dual_overlay(old_state)) {
193 		omap_overlay_update_state(priv, old_omap_state->r_overlay);
194 		new_omap_state->r_overlay = NULL;
195 	}
196 }
197 
198 #define FRAC_16_16(mult, div)    (((mult) << 16) / (div))
199 
200 static int omap_plane_atomic_check(struct drm_plane *plane,
201 				   struct drm_atomic_state *state)
202 {
203 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
204 										 plane);
205 	struct drm_plane_state *old_plane_state = drm_atomic_get_old_plane_state(state,
206 										 plane);
207 	struct omap_drm_private *priv = plane->dev->dev_private;
208 	struct omap_plane_state *omap_state = to_omap_plane_state(new_plane_state);
209 	struct omap_global_state *omap_overlay_global_state;
210 	struct drm_crtc_state *crtc_state;
211 	bool new_r_hw_overlay = false;
212 	bool new_hw_overlay = false;
213 	u32 max_width, max_height;
214 	struct drm_crtc *crtc;
215 	u16 width, height;
216 	u32 caps = 0;
217 	u32 fourcc;
218 	int ret;
219 
220 	omap_overlay_global_state = omap_get_global_state(state);
221 	if (IS_ERR(omap_overlay_global_state))
222 		return PTR_ERR(omap_overlay_global_state);
223 
224 	dispc_ovl_get_max_size(priv->dispc, &width, &height);
225 	max_width = width << 16;
226 	max_height = height << 16;
227 
228 	crtc = new_plane_state->crtc ? new_plane_state->crtc : plane->state->crtc;
229 	if (!crtc)
230 		return 0;
231 
232 	crtc_state = drm_atomic_get_existing_crtc_state(state, crtc);
233 	/* we should have a crtc state if the plane is attached to a crtc */
234 	if (WARN_ON(!crtc_state))
235 		return 0;
236 
237 	/*
238 	 * Note: these are just sanity checks to filter out totally bad scaling
239 	 * factors. The real limits must be calculated case by case, and
240 	 * unfortunately we currently do those checks only at the commit
241 	 * phase in dispc.
242 	 */
243 	ret = drm_atomic_helper_check_plane_state(new_plane_state, crtc_state,
244 						  FRAC_16_16(1, 8), FRAC_16_16(8, 1),
245 						  true, true);
246 	if (ret)
247 		return ret;
248 
249 	DBG("%s: visible %d -> %d", plane->name,
250 	    old_plane_state->visible, new_plane_state->visible);
251 
252 	if (!new_plane_state->visible) {
253 		omap_overlay_release(state, omap_state->overlay);
254 		omap_overlay_release(state, omap_state->r_overlay);
255 		omap_state->overlay = NULL;
256 		omap_state->r_overlay = NULL;
257 		return 0;
258 	}
259 
260 	if (new_plane_state->crtc_x < 0 || new_plane_state->crtc_y < 0)
261 		return -EINVAL;
262 
263 	if (new_plane_state->crtc_x + new_plane_state->crtc_w > crtc_state->adjusted_mode.hdisplay)
264 		return -EINVAL;
265 
266 	if (new_plane_state->crtc_y + new_plane_state->crtc_h > crtc_state->adjusted_mode.vdisplay)
267 		return -EINVAL;
268 
269 	/* Make sure dimensions are within bounds. */
270 	if (new_plane_state->src_h > max_height || new_plane_state->crtc_h > height)
271 		return -EINVAL;
272 
273 
274 	if (new_plane_state->src_w > max_width || new_plane_state->crtc_w > width) {
275 		bool is_fourcc_yuv = new_plane_state->fb->format->is_yuv;
276 
277 		if (is_fourcc_yuv && (((new_plane_state->src_w >> 16) / 2 & 1) ||
278 				      new_plane_state->crtc_w / 2 & 1)) {
279 			/*
280 			 * When calculating the split overlay width
281 			 * and it yield an odd value we will need to adjust
282 			 * the indivual width +/- 1. So make sure it fits
283 			 */
284 			if (new_plane_state->src_w <= ((2 * width - 1) << 16) &&
285 			    new_plane_state->crtc_w <= (2 * width - 1))
286 				new_r_hw_overlay = true;
287 			else
288 				return -EINVAL;
289 		} else {
290 			if (new_plane_state->src_w <= (2 * max_width) &&
291 			    new_plane_state->crtc_w <= (2 * width))
292 				new_r_hw_overlay = true;
293 			else
294 				return -EINVAL;
295 		}
296 	}
297 
298 	if (new_plane_state->rotation != DRM_MODE_ROTATE_0 &&
299 	    !omap_framebuffer_supports_rotation(new_plane_state->fb))
300 		return -EINVAL;
301 
302 	if ((new_plane_state->src_w >> 16) != new_plane_state->crtc_w ||
303 	    (new_plane_state->src_h >> 16) != new_plane_state->crtc_h)
304 		caps |= OMAP_DSS_OVL_CAP_SCALE;
305 
306 	fourcc = new_plane_state->fb->format->format;
307 
308 	/*
309 	 * (re)allocate hw overlay if we don't have one or
310 	 * there is a caps mismatch
311 	 */
312 	if (!omap_state->overlay || (caps & ~omap_state->overlay->caps)) {
313 		new_hw_overlay = true;
314 	} else {
315 		/* check supported format */
316 		if (!dispc_ovl_color_mode_supported(priv->dispc, omap_state->overlay->id,
317 						    fourcc))
318 			new_hw_overlay = true;
319 	}
320 
321 	/*
322 	 * check if we need two overlays and only have 1 or
323 	 * if we had 2 overlays but will only need 1
324 	 */
325 	if ((new_r_hw_overlay && !omap_state->r_overlay) ||
326 	    (!new_r_hw_overlay && omap_state->r_overlay))
327 		new_hw_overlay = true;
328 
329 	if (new_hw_overlay) {
330 		struct omap_hw_overlay *old_ovl = omap_state->overlay;
331 		struct omap_hw_overlay *old_r_ovl = omap_state->r_overlay;
332 		struct omap_hw_overlay *new_ovl = NULL;
333 		struct omap_hw_overlay *new_r_ovl = NULL;
334 
335 		omap_overlay_release(state, old_ovl);
336 		omap_overlay_release(state, old_r_ovl);
337 
338 		ret = omap_overlay_assign(state, plane, caps, fourcc, &new_ovl,
339 					  new_r_hw_overlay ? &new_r_ovl : NULL);
340 		if (ret) {
341 			DBG("%s: failed to assign hw_overlay", plane->name);
342 			omap_state->overlay = NULL;
343 			omap_state->r_overlay = NULL;
344 			return ret;
345 		}
346 
347 		omap_state->overlay = new_ovl;
348 		if (new_r_hw_overlay)
349 			omap_state->r_overlay = new_r_ovl;
350 		else
351 			omap_state->r_overlay = NULL;
352 	}
353 
354 	DBG("plane: %s overlay_id: %d", plane->name, omap_state->overlay->id);
355 
356 	if (omap_state->r_overlay)
357 		DBG("plane: %s r_overlay_id: %d", plane->name, omap_state->r_overlay->id);
358 
359 	return 0;
360 }
361 
362 static const struct drm_plane_helper_funcs omap_plane_helper_funcs = {
363 	.prepare_fb = omap_plane_prepare_fb,
364 	.cleanup_fb = omap_plane_cleanup_fb,
365 	.atomic_check = omap_plane_atomic_check,
366 	.atomic_update = omap_plane_atomic_update,
367 	.atomic_disable = omap_plane_atomic_disable,
368 };
369 
370 static void omap_plane_destroy(struct drm_plane *plane)
371 {
372 	struct omap_plane *omap_plane = to_omap_plane(plane);
373 
374 	DBG("%s", plane->name);
375 
376 	drm_plane_cleanup(plane);
377 
378 	kfree(omap_plane);
379 }
380 
381 /* helper to install properties which are common to planes and crtcs */
382 void omap_plane_install_properties(struct drm_plane *plane,
383 		struct drm_mode_object *obj)
384 {
385 	struct drm_device *dev = plane->dev;
386 	struct omap_drm_private *priv = dev->dev_private;
387 
388 	if (priv->has_dmm) {
389 		if (!plane->rotation_property)
390 			drm_plane_create_rotation_property(plane,
391 							   DRM_MODE_ROTATE_0,
392 							   DRM_MODE_ROTATE_0 | DRM_MODE_ROTATE_90 |
393 							   DRM_MODE_ROTATE_180 | DRM_MODE_ROTATE_270 |
394 							   DRM_MODE_REFLECT_X | DRM_MODE_REFLECT_Y);
395 
396 		/* Attach the rotation property also to the crtc object */
397 		if (plane->rotation_property && obj != &plane->base)
398 			drm_object_attach_property(obj, plane->rotation_property,
399 						   DRM_MODE_ROTATE_0);
400 	}
401 
402 	drm_object_attach_property(obj, priv->zorder_prop, 0);
403 }
404 
405 static void omap_plane_reset(struct drm_plane *plane)
406 {
407 	struct omap_plane_state *omap_state;
408 
409 	if (plane->state)
410 		drm_atomic_helper_plane_destroy_state(plane, plane->state);
411 
412 	omap_state = kzalloc(sizeof(*omap_state), GFP_KERNEL);
413 	if (!omap_state)
414 		return;
415 
416 	__drm_atomic_helper_plane_reset(plane, &omap_state->base);
417 }
418 
419 static struct drm_plane_state *
420 omap_plane_atomic_duplicate_state(struct drm_plane *plane)
421 {
422 	struct omap_plane_state *state, *current_state;
423 
424 	if (WARN_ON(!plane->state))
425 		return NULL;
426 
427 	current_state = to_omap_plane_state(plane->state);
428 
429 	state = kmalloc(sizeof(*state), GFP_KERNEL);
430 	if (!state)
431 		return NULL;
432 
433 	__drm_atomic_helper_plane_duplicate_state(plane, &state->base);
434 
435 	state->overlay = current_state->overlay;
436 	state->r_overlay = current_state->r_overlay;
437 
438 	return &state->base;
439 }
440 
441 static void omap_plane_atomic_print_state(struct drm_printer *p,
442 					  const struct drm_plane_state *state)
443 {
444 	struct omap_plane_state *omap_state = to_omap_plane_state(state);
445 
446 	if (omap_state->overlay)
447 		drm_printf(p, "\toverlay=%s (caps=0x%x)\n",
448 			   omap_state->overlay->name,
449 			   omap_state->overlay->caps);
450 	else
451 		drm_printf(p, "\toverlay=None\n");
452 	if (omap_state->r_overlay)
453 		drm_printf(p, "\tr_overlay=%s (caps=0x%x)\n",
454 			   omap_state->r_overlay->name,
455 			   omap_state->r_overlay->caps);
456 	else
457 		drm_printf(p, "\tr_overlay=None\n");
458 }
459 
460 static int omap_plane_atomic_set_property(struct drm_plane *plane,
461 					  struct drm_plane_state *state,
462 					  struct drm_property *property,
463 					  u64 val)
464 {
465 	struct omap_drm_private *priv = plane->dev->dev_private;
466 
467 	if (property == priv->zorder_prop)
468 		state->zpos = val;
469 	else
470 		return -EINVAL;
471 
472 	return 0;
473 }
474 
475 static int omap_plane_atomic_get_property(struct drm_plane *plane,
476 					  const struct drm_plane_state *state,
477 					  struct drm_property *property,
478 					  u64 *val)
479 {
480 	struct omap_drm_private *priv = plane->dev->dev_private;
481 
482 	if (property == priv->zorder_prop)
483 		*val = state->zpos;
484 	else
485 		return -EINVAL;
486 
487 	return 0;
488 }
489 
490 static const struct drm_plane_funcs omap_plane_funcs = {
491 	.update_plane = drm_atomic_helper_update_plane,
492 	.disable_plane = drm_atomic_helper_disable_plane,
493 	.reset = omap_plane_reset,
494 	.destroy = omap_plane_destroy,
495 	.atomic_duplicate_state = omap_plane_atomic_duplicate_state,
496 	.atomic_destroy_state = drm_atomic_helper_plane_destroy_state,
497 	.atomic_set_property = omap_plane_atomic_set_property,
498 	.atomic_get_property = omap_plane_atomic_get_property,
499 	.atomic_print_state = omap_plane_atomic_print_state,
500 };
501 
502 static bool omap_plane_supports_yuv(struct drm_plane *plane)
503 {
504 	struct omap_drm_private *priv = plane->dev->dev_private;
505 	struct omap_plane *omap_plane = to_omap_plane(plane);
506 	const u32 *formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
507 	u32 i;
508 
509 	for (i = 0; formats[i]; i++)
510 		if (formats[i] == DRM_FORMAT_YUYV ||
511 		    formats[i] == DRM_FORMAT_UYVY ||
512 		    formats[i] == DRM_FORMAT_NV12)
513 			return true;
514 
515 	return false;
516 }
517 
518 /* initialize plane */
519 struct drm_plane *omap_plane_init(struct drm_device *dev,
520 		int idx, enum drm_plane_type type,
521 		u32 possible_crtcs)
522 {
523 	struct omap_drm_private *priv = dev->dev_private;
524 	unsigned int num_planes = dispc_get_num_ovls(priv->dispc);
525 	struct drm_plane *plane;
526 	struct omap_plane *omap_plane;
527 	unsigned int zpos;
528 	int ret;
529 	u32 nformats;
530 	const u32 *formats;
531 
532 	if (WARN_ON(idx >= num_planes))
533 		return ERR_PTR(-EINVAL);
534 
535 	omap_plane = kzalloc(sizeof(*omap_plane), GFP_KERNEL);
536 	if (!omap_plane)
537 		return ERR_PTR(-ENOMEM);
538 
539 	omap_plane->id = idx;
540 
541 	DBG("%d: type=%d", omap_plane->id, type);
542 	DBG("	crtc_mask: 0x%04x", possible_crtcs);
543 
544 	formats = dispc_ovl_get_color_modes(priv->dispc, omap_plane->id);
545 	for (nformats = 0; formats[nformats]; ++nformats)
546 		;
547 
548 	plane = &omap_plane->base;
549 
550 	ret = drm_universal_plane_init(dev, plane, possible_crtcs,
551 				       &omap_plane_funcs, formats,
552 				       nformats, NULL, type, NULL);
553 	if (ret < 0)
554 		goto error;
555 
556 	drm_plane_helper_add(plane, &omap_plane_helper_funcs);
557 
558 	omap_plane_install_properties(plane, &plane->base);
559 
560 	/*
561 	 * Set the zpos default depending on whether we are a primary or overlay
562 	 * plane.
563 	 */
564 	if (plane->type == DRM_PLANE_TYPE_PRIMARY)
565 		zpos = 0;
566 	else
567 		zpos = omap_plane->id;
568 	drm_plane_create_zpos_property(plane, zpos, 0, num_planes - 1);
569 	drm_plane_create_alpha_property(plane);
570 	drm_plane_create_blend_mode_property(plane, BIT(DRM_MODE_BLEND_PREMULTI) |
571 					     BIT(DRM_MODE_BLEND_COVERAGE));
572 
573 	if (omap_plane_supports_yuv(plane))
574 		drm_plane_create_color_properties(plane,
575 						  BIT(DRM_COLOR_YCBCR_BT601) |
576 						  BIT(DRM_COLOR_YCBCR_BT709),
577 						  BIT(DRM_COLOR_YCBCR_FULL_RANGE) |
578 						  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE),
579 						  DRM_COLOR_YCBCR_BT601,
580 						  DRM_COLOR_YCBCR_FULL_RANGE);
581 
582 	return plane;
583 
584 error:
585 	dev_err(dev->dev, "%s(): could not create plane: %d\n",
586 		__func__, omap_plane->id);
587 
588 	kfree(omap_plane);
589 	return NULL;
590 }
591