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