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