xref: /openbmc/linux/drivers/gpu/drm/tegra/plane.c (revision d9e32672)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2017 NVIDIA CORPORATION.  All rights reserved.
4  */
5 
6 #include <drm/drm_atomic.h>
7 #include <drm/drm_atomic_helper.h>
8 #include <drm/drm_fourcc.h>
9 #include <drm/drm_gem_framebuffer_helper.h>
10 #include <drm/drm_plane_helper.h>
11 
12 #include "dc.h"
13 #include "plane.h"
14 
15 static void tegra_plane_destroy(struct drm_plane *plane)
16 {
17 	struct tegra_plane *p = to_tegra_plane(plane);
18 
19 	drm_plane_cleanup(plane);
20 	kfree(p);
21 }
22 
23 static void tegra_plane_reset(struct drm_plane *plane)
24 {
25 	struct tegra_plane *p = to_tegra_plane(plane);
26 	struct tegra_plane_state *state;
27 	unsigned int i;
28 
29 	if (plane->state)
30 		__drm_atomic_helper_plane_destroy_state(plane->state);
31 
32 	kfree(plane->state);
33 	plane->state = NULL;
34 
35 	state = kzalloc(sizeof(*state), GFP_KERNEL);
36 	if (state) {
37 		plane->state = &state->base;
38 		plane->state->plane = plane;
39 		plane->state->zpos = p->index;
40 		plane->state->normalized_zpos = p->index;
41 
42 		for (i = 0; i < 3; i++)
43 			state->iova[i] = DMA_MAPPING_ERROR;
44 	}
45 }
46 
47 static struct drm_plane_state *
48 tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
49 {
50 	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
51 	struct tegra_plane_state *copy;
52 	unsigned int i;
53 
54 	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
55 	if (!copy)
56 		return NULL;
57 
58 	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
59 	copy->tiling = state->tiling;
60 	copy->format = state->format;
61 	copy->swap = state->swap;
62 	copy->bottom_up = state->bottom_up;
63 	copy->opaque = state->opaque;
64 
65 	for (i = 0; i < 2; i++)
66 		copy->blending[i] = state->blending[i];
67 
68 	for (i = 0; i < 3; i++) {
69 		copy->iova[i] = DMA_MAPPING_ERROR;
70 		copy->sgt[i] = NULL;
71 	}
72 
73 	return &copy->base;
74 }
75 
76 static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
77 					     struct drm_plane_state *state)
78 {
79 	__drm_atomic_helper_plane_destroy_state(state);
80 	kfree(state);
81 }
82 
83 static bool tegra_plane_format_mod_supported(struct drm_plane *plane,
84 					     uint32_t format,
85 					     uint64_t modifier)
86 {
87 	const struct drm_format_info *info = drm_format_info(format);
88 
89 	if (modifier == DRM_FORMAT_MOD_LINEAR)
90 		return true;
91 
92 	if (info->num_planes == 1)
93 		return true;
94 
95 	return false;
96 }
97 
98 const struct drm_plane_funcs tegra_plane_funcs = {
99 	.update_plane = drm_atomic_helper_update_plane,
100 	.disable_plane = drm_atomic_helper_disable_plane,
101 	.destroy = tegra_plane_destroy,
102 	.reset = tegra_plane_reset,
103 	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
104 	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
105 	.format_mod_supported = tegra_plane_format_mod_supported,
106 };
107 
108 static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
109 {
110 	unsigned int i;
111 	int err;
112 
113 	for (i = 0; i < state->base.fb->format->num_planes; i++) {
114 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
115 
116 		if (!dc->client.group) {
117 			struct sg_table *sgt;
118 
119 			sgt = host1x_bo_pin(dc->dev, &bo->base, NULL);
120 			if (IS_ERR(sgt)) {
121 				err = PTR_ERR(sgt);
122 				goto unpin;
123 			}
124 
125 			err = dma_map_sg(dc->dev, sgt->sgl, sgt->nents,
126 					 DMA_TO_DEVICE);
127 			if (err == 0) {
128 				err = -ENOMEM;
129 				goto unpin;
130 			}
131 
132 			state->iova[i] = sg_dma_address(sgt->sgl);
133 			state->sgt[i] = sgt;
134 		} else {
135 			state->iova[i] = bo->iova;
136 		}
137 	}
138 
139 	return 0;
140 
141 unpin:
142 	dev_err(dc->dev, "failed to map plane %u: %d\n", i, err);
143 
144 	while (i--) {
145 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
146 		struct sg_table *sgt = state->sgt[i];
147 
148 		dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents, DMA_TO_DEVICE);
149 		host1x_bo_unpin(dc->dev, &bo->base, sgt);
150 
151 		state->iova[i] = DMA_MAPPING_ERROR;
152 		state->sgt[i] = NULL;
153 	}
154 
155 	return err;
156 }
157 
158 static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state)
159 {
160 	unsigned int i;
161 
162 	for (i = 0; i < state->base.fb->format->num_planes; i++) {
163 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
164 
165 		if (!dc->client.group) {
166 			struct sg_table *sgt = state->sgt[i];
167 
168 			if (sgt) {
169 				dma_unmap_sg(dc->dev, sgt->sgl, sgt->nents,
170 					     DMA_TO_DEVICE);
171 				host1x_bo_unpin(dc->dev, &bo->base, sgt);
172 			}
173 		}
174 
175 		state->iova[i] = DMA_MAPPING_ERROR;
176 		state->sgt[i] = NULL;
177 	}
178 }
179 
180 int tegra_plane_prepare_fb(struct drm_plane *plane,
181 			   struct drm_plane_state *state)
182 {
183 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
184 
185 	if (!state->fb)
186 		return 0;
187 
188 	drm_gem_fb_prepare_fb(plane, state);
189 
190 	return tegra_dc_pin(dc, to_tegra_plane_state(state));
191 }
192 
193 void tegra_plane_cleanup_fb(struct drm_plane *plane,
194 			    struct drm_plane_state *state)
195 {
196 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
197 
198 	if (dc)
199 		tegra_dc_unpin(dc, to_tegra_plane_state(state));
200 }
201 
202 int tegra_plane_state_add(struct tegra_plane *plane,
203 			  struct drm_plane_state *state)
204 {
205 	struct drm_crtc_state *crtc_state;
206 	struct tegra_dc_state *tegra;
207 	int err;
208 
209 	/* Propagate errors from allocation or locking failures. */
210 	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
211 	if (IS_ERR(crtc_state))
212 		return PTR_ERR(crtc_state);
213 
214 	/* Check plane state for visibility and calculate clipping bounds */
215 	err = drm_atomic_helper_check_plane_state(state, crtc_state,
216 						  0, INT_MAX, true, true);
217 	if (err < 0)
218 		return err;
219 
220 	tegra = to_dc_state(crtc_state);
221 
222 	tegra->planes |= WIN_A_ACT_REQ << plane->index;
223 
224 	return 0;
225 }
226 
227 int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap)
228 {
229 	/* assume no swapping of fetched data */
230 	if (swap)
231 		*swap = BYTE_SWAP_NOSWAP;
232 
233 	switch (fourcc) {
234 	case DRM_FORMAT_ARGB4444:
235 		*format = WIN_COLOR_DEPTH_B4G4R4A4;
236 		break;
237 
238 	case DRM_FORMAT_ARGB1555:
239 		*format = WIN_COLOR_DEPTH_B5G5R5A1;
240 		break;
241 
242 	case DRM_FORMAT_RGB565:
243 		*format = WIN_COLOR_DEPTH_B5G6R5;
244 		break;
245 
246 	case DRM_FORMAT_RGBA5551:
247 		*format = WIN_COLOR_DEPTH_A1B5G5R5;
248 		break;
249 
250 	case DRM_FORMAT_ARGB8888:
251 		*format = WIN_COLOR_DEPTH_B8G8R8A8;
252 		break;
253 
254 	case DRM_FORMAT_ABGR8888:
255 		*format = WIN_COLOR_DEPTH_R8G8B8A8;
256 		break;
257 
258 	case DRM_FORMAT_ABGR4444:
259 		*format = WIN_COLOR_DEPTH_R4G4B4A4;
260 		break;
261 
262 	case DRM_FORMAT_ABGR1555:
263 		*format = WIN_COLOR_DEPTH_R5G5B5A;
264 		break;
265 
266 	case DRM_FORMAT_BGRA5551:
267 		*format = WIN_COLOR_DEPTH_AR5G5B5;
268 		break;
269 
270 	case DRM_FORMAT_XRGB1555:
271 		*format = WIN_COLOR_DEPTH_B5G5R5X1;
272 		break;
273 
274 	case DRM_FORMAT_RGBX5551:
275 		*format = WIN_COLOR_DEPTH_X1B5G5R5;
276 		break;
277 
278 	case DRM_FORMAT_XBGR1555:
279 		*format = WIN_COLOR_DEPTH_R5G5B5X1;
280 		break;
281 
282 	case DRM_FORMAT_BGRX5551:
283 		*format = WIN_COLOR_DEPTH_X1R5G5B5;
284 		break;
285 
286 	case DRM_FORMAT_BGR565:
287 		*format = WIN_COLOR_DEPTH_R5G6B5;
288 		break;
289 
290 	case DRM_FORMAT_BGRA8888:
291 		*format = WIN_COLOR_DEPTH_A8R8G8B8;
292 		break;
293 
294 	case DRM_FORMAT_RGBA8888:
295 		*format = WIN_COLOR_DEPTH_A8B8G8R8;
296 		break;
297 
298 	case DRM_FORMAT_XRGB8888:
299 		*format = WIN_COLOR_DEPTH_B8G8R8X8;
300 		break;
301 
302 	case DRM_FORMAT_XBGR8888:
303 		*format = WIN_COLOR_DEPTH_R8G8B8X8;
304 		break;
305 
306 	case DRM_FORMAT_UYVY:
307 		*format = WIN_COLOR_DEPTH_YCbCr422;
308 		break;
309 
310 	case DRM_FORMAT_YUYV:
311 		if (!swap)
312 			return -EINVAL;
313 
314 		*format = WIN_COLOR_DEPTH_YCbCr422;
315 		*swap = BYTE_SWAP_SWAP2;
316 		break;
317 
318 	case DRM_FORMAT_YUV420:
319 		*format = WIN_COLOR_DEPTH_YCbCr420P;
320 		break;
321 
322 	case DRM_FORMAT_YUV422:
323 		*format = WIN_COLOR_DEPTH_YCbCr422P;
324 		break;
325 
326 	default:
327 		return -EINVAL;
328 	}
329 
330 	return 0;
331 }
332 
333 bool tegra_plane_format_is_yuv(unsigned int format, bool *planar)
334 {
335 	switch (format) {
336 	case WIN_COLOR_DEPTH_YCbCr422:
337 	case WIN_COLOR_DEPTH_YUV422:
338 		if (planar)
339 			*planar = false;
340 
341 		return true;
342 
343 	case WIN_COLOR_DEPTH_YCbCr420P:
344 	case WIN_COLOR_DEPTH_YUV420P:
345 	case WIN_COLOR_DEPTH_YCbCr422P:
346 	case WIN_COLOR_DEPTH_YUV422P:
347 	case WIN_COLOR_DEPTH_YCbCr422R:
348 	case WIN_COLOR_DEPTH_YUV422R:
349 	case WIN_COLOR_DEPTH_YCbCr422RA:
350 	case WIN_COLOR_DEPTH_YUV422RA:
351 		if (planar)
352 			*planar = true;
353 
354 		return true;
355 	}
356 
357 	if (planar)
358 		*planar = false;
359 
360 	return false;
361 }
362 
363 static bool __drm_format_has_alpha(u32 format)
364 {
365 	switch (format) {
366 	case DRM_FORMAT_ARGB1555:
367 	case DRM_FORMAT_RGBA5551:
368 	case DRM_FORMAT_ABGR8888:
369 	case DRM_FORMAT_ARGB8888:
370 		return true;
371 	}
372 
373 	return false;
374 }
375 
376 static int tegra_plane_format_get_alpha(unsigned int opaque,
377 					unsigned int *alpha)
378 {
379 	if (tegra_plane_format_is_yuv(opaque, NULL)) {
380 		*alpha = opaque;
381 		return 0;
382 	}
383 
384 	switch (opaque) {
385 	case WIN_COLOR_DEPTH_B5G5R5X1:
386 		*alpha = WIN_COLOR_DEPTH_B5G5R5A1;
387 		return 0;
388 
389 	case WIN_COLOR_DEPTH_X1B5G5R5:
390 		*alpha = WIN_COLOR_DEPTH_A1B5G5R5;
391 		return 0;
392 
393 	case WIN_COLOR_DEPTH_R8G8B8X8:
394 		*alpha = WIN_COLOR_DEPTH_R8G8B8A8;
395 		return 0;
396 
397 	case WIN_COLOR_DEPTH_B8G8R8X8:
398 		*alpha = WIN_COLOR_DEPTH_B8G8R8A8;
399 		return 0;
400 
401 	case WIN_COLOR_DEPTH_B5G6R5:
402 		*alpha = opaque;
403 		return 0;
404 	}
405 
406 	return -EINVAL;
407 }
408 
409 /*
410  * This is applicable to Tegra20 and Tegra30 only where the opaque formats can
411  * be emulated using the alpha formats and alpha blending disabled.
412  */
413 static int tegra_plane_setup_opacity(struct tegra_plane *tegra,
414 				     struct tegra_plane_state *state)
415 {
416 	unsigned int format;
417 	int err;
418 
419 	switch (state->format) {
420 	case WIN_COLOR_DEPTH_B5G5R5A1:
421 	case WIN_COLOR_DEPTH_A1B5G5R5:
422 	case WIN_COLOR_DEPTH_R8G8B8A8:
423 	case WIN_COLOR_DEPTH_B8G8R8A8:
424 		state->opaque = false;
425 		break;
426 
427 	default:
428 		err = tegra_plane_format_get_alpha(state->format, &format);
429 		if (err < 0)
430 			return err;
431 
432 		state->format = format;
433 		state->opaque = true;
434 		break;
435 	}
436 
437 	return 0;
438 }
439 
440 static int tegra_plane_check_transparency(struct tegra_plane *tegra,
441 					  struct tegra_plane_state *state)
442 {
443 	struct drm_plane_state *old, *plane_state;
444 	struct drm_plane *plane;
445 
446 	old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
447 
448 	/* check if zpos / transparency changed */
449 	if (old->normalized_zpos == state->base.normalized_zpos &&
450 	    to_tegra_plane_state(old)->opaque == state->opaque)
451 		return 0;
452 
453 	/* include all sibling planes into this commit */
454 	drm_for_each_plane(plane, tegra->base.dev) {
455 		struct tegra_plane *p = to_tegra_plane(plane);
456 
457 		/* skip this plane and planes on different CRTCs */
458 		if (p == tegra || p->dc != tegra->dc)
459 			continue;
460 
461 		plane_state = drm_atomic_get_plane_state(state->base.state,
462 							 plane);
463 		if (IS_ERR(plane_state))
464 			return PTR_ERR(plane_state);
465 	}
466 
467 	return 1;
468 }
469 
470 static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
471 						  struct tegra_plane *other)
472 {
473 	unsigned int index = 0, i;
474 
475 	WARN_ON(plane == other);
476 
477 	for (i = 0; i < 3; i++) {
478 		if (i == plane->index)
479 			continue;
480 
481 		if (i == other->index)
482 			break;
483 
484 		index++;
485 	}
486 
487 	return index;
488 }
489 
490 static void tegra_plane_update_transparency(struct tegra_plane *tegra,
491 					    struct tegra_plane_state *state)
492 {
493 	struct drm_plane_state *new;
494 	struct drm_plane *plane;
495 	unsigned int i;
496 
497 	for_each_new_plane_in_state(state->base.state, plane, new, i) {
498 		struct tegra_plane *p = to_tegra_plane(plane);
499 		unsigned index;
500 
501 		/* skip this plane and planes on different CRTCs */
502 		if (p == tegra || p->dc != tegra->dc)
503 			continue;
504 
505 		index = tegra_plane_get_overlap_index(tegra, p);
506 
507 		if (new->fb && __drm_format_has_alpha(new->fb->format->format))
508 			state->blending[index].alpha = true;
509 		else
510 			state->blending[index].alpha = false;
511 
512 		if (new->normalized_zpos > state->base.normalized_zpos)
513 			state->blending[index].top = true;
514 		else
515 			state->blending[index].top = false;
516 
517 		/*
518 		 * Missing framebuffer means that plane is disabled, in this
519 		 * case mark B / C window as top to be able to differentiate
520 		 * windows indices order in regards to zPos for the middle
521 		 * window X / Y registers programming.
522 		 */
523 		if (!new->fb)
524 			state->blending[index].top = (index == 1);
525 	}
526 }
527 
528 static int tegra_plane_setup_transparency(struct tegra_plane *tegra,
529 					  struct tegra_plane_state *state)
530 {
531 	struct tegra_plane_state *tegra_state;
532 	struct drm_plane_state *new;
533 	struct drm_plane *plane;
534 	int err;
535 
536 	/*
537 	 * If planes zpos / transparency changed, sibling planes blending
538 	 * state may require adjustment and in this case they will be included
539 	 * into this atom commit, otherwise blending state is unchanged.
540 	 */
541 	err = tegra_plane_check_transparency(tegra, state);
542 	if (err <= 0)
543 		return err;
544 
545 	/*
546 	 * All planes are now in the atomic state, walk them up and update
547 	 * transparency state for each plane.
548 	 */
549 	drm_for_each_plane(plane, tegra->base.dev) {
550 		struct tegra_plane *p = to_tegra_plane(plane);
551 
552 		/* skip planes on different CRTCs */
553 		if (p->dc != tegra->dc)
554 			continue;
555 
556 		new = drm_atomic_get_new_plane_state(state->base.state, plane);
557 		tegra_state = to_tegra_plane_state(new);
558 
559 		/*
560 		 * There is no need to update blending state for the disabled
561 		 * plane.
562 		 */
563 		if (new->fb)
564 			tegra_plane_update_transparency(p, tegra_state);
565 	}
566 
567 	return 0;
568 }
569 
570 int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
571 				   struct tegra_plane_state *state)
572 {
573 	int err;
574 
575 	err = tegra_plane_setup_opacity(tegra, state);
576 	if (err < 0)
577 		return err;
578 
579 	err = tegra_plane_setup_transparency(tegra, state);
580 	if (err < 0)
581 		return err;
582 
583 	return 0;
584 }
585