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