xref: /linux/drivers/gpu/drm/tegra/plane.c (revision c1d3cfbc41a15d3eadde760c00fe6ce58a1d4e29)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
25acd3514SThierry Reding /*
35acd3514SThierry Reding  * Copyright (C) 2017 NVIDIA CORPORATION.  All rights reserved.
45acd3514SThierry Reding  */
55acd3514SThierry Reding 
6273da5a0SThierry Reding #include <linux/iommu.h>
7273da5a0SThierry Reding 
85acd3514SThierry Reding #include <drm/drm_atomic.h>
95acd3514SThierry Reding #include <drm/drm_atomic_helper.h>
10eb1df694SSam Ravnborg #include <drm/drm_fourcc.h>
11820c1707SThomas Zimmermann #include <drm/drm_gem_atomic_helper.h>
125acd3514SThierry Reding #include <drm/drm_plane_helper.h>
135acd3514SThierry Reding 
145acd3514SThierry Reding #include "dc.h"
155acd3514SThierry Reding #include "plane.h"
165acd3514SThierry Reding 
175acd3514SThierry Reding static void tegra_plane_destroy(struct drm_plane *plane)
185acd3514SThierry Reding {
195acd3514SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
205acd3514SThierry Reding 
215acd3514SThierry Reding 	drm_plane_cleanup(plane);
225acd3514SThierry Reding 	kfree(p);
235acd3514SThierry Reding }
245acd3514SThierry Reding 
255acd3514SThierry Reding static void tegra_plane_reset(struct drm_plane *plane)
265acd3514SThierry Reding {
273dae08bcSDmitry Osipenko 	struct tegra_plane *p = to_tegra_plane(plane);
285acd3514SThierry Reding 	struct tegra_plane_state *state;
292e8d8749SThierry Reding 	unsigned int i;
305acd3514SThierry Reding 
315acd3514SThierry Reding 	if (plane->state)
325acd3514SThierry Reding 		__drm_atomic_helper_plane_destroy_state(plane->state);
335acd3514SThierry Reding 
345acd3514SThierry Reding 	kfree(plane->state);
355acd3514SThierry Reding 	plane->state = NULL;
365acd3514SThierry Reding 
375acd3514SThierry Reding 	state = kzalloc(sizeof(*state), GFP_KERNEL);
385acd3514SThierry Reding 	if (state) {
395acd3514SThierry Reding 		plane->state = &state->base;
405acd3514SThierry Reding 		plane->state->plane = plane;
413dae08bcSDmitry Osipenko 		plane->state->zpos = p->index;
423dae08bcSDmitry Osipenko 		plane->state->normalized_zpos = p->index;
432e8d8749SThierry Reding 
442e8d8749SThierry Reding 		for (i = 0; i < 3; i++)
452e8d8749SThierry Reding 			state->iova[i] = DMA_MAPPING_ERROR;
465acd3514SThierry Reding 	}
475acd3514SThierry Reding }
485acd3514SThierry Reding 
495acd3514SThierry Reding static struct drm_plane_state *
505acd3514SThierry Reding tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
515acd3514SThierry Reding {
525acd3514SThierry Reding 	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
535acd3514SThierry Reding 	struct tegra_plane_state *copy;
54ebae8d07SThierry Reding 	unsigned int i;
555acd3514SThierry Reding 
565acd3514SThierry Reding 	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
575acd3514SThierry Reding 	if (!copy)
585acd3514SThierry Reding 		return NULL;
595acd3514SThierry Reding 
605acd3514SThierry Reding 	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
615acd3514SThierry Reding 	copy->tiling = state->tiling;
625acd3514SThierry Reding 	copy->format = state->format;
635acd3514SThierry Reding 	copy->swap = state->swap;
64cd740777SDmitry Osipenko 	copy->reflect_x = state->reflect_x;
65e9e476f7SDmitry Osipenko 	copy->reflect_y = state->reflect_y;
66ebae8d07SThierry Reding 	copy->opaque = state->opaque;
67ebae8d07SThierry Reding 
683dae08bcSDmitry Osipenko 	for (i = 0; i < 2; i++)
693dae08bcSDmitry Osipenko 		copy->blending[i] = state->blending[i];
705acd3514SThierry Reding 
712e8d8749SThierry Reding 	for (i = 0; i < 3; i++) {
722e8d8749SThierry Reding 		copy->iova[i] = DMA_MAPPING_ERROR;
732e8d8749SThierry Reding 		copy->sgt[i] = NULL;
742e8d8749SThierry Reding 	}
752e8d8749SThierry Reding 
765acd3514SThierry Reding 	return &copy->base;
775acd3514SThierry Reding }
785acd3514SThierry Reding 
795acd3514SThierry Reding static void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
805acd3514SThierry Reding 					     struct drm_plane_state *state)
815acd3514SThierry Reding {
825acd3514SThierry Reding 	__drm_atomic_helper_plane_destroy_state(state);
835acd3514SThierry Reding 	kfree(state);
845acd3514SThierry Reding }
855acd3514SThierry Reding 
867b6f8467SThierry Reding static bool tegra_plane_supports_sector_layout(struct drm_plane *plane)
877b6f8467SThierry Reding {
887b6f8467SThierry Reding 	struct drm_crtc *crtc;
897b6f8467SThierry Reding 
907b6f8467SThierry Reding 	drm_for_each_crtc(crtc, plane->dev) {
917b6f8467SThierry Reding 		if (plane->possible_crtcs & drm_crtc_mask(crtc)) {
927b6f8467SThierry Reding 			struct tegra_dc *dc = to_tegra_dc(crtc);
937b6f8467SThierry Reding 
947b6f8467SThierry Reding 			if (!dc->soc->supports_sector_layout)
957b6f8467SThierry Reding 				return false;
967b6f8467SThierry Reding 		}
977b6f8467SThierry Reding 	}
987b6f8467SThierry Reding 
997b6f8467SThierry Reding 	return true;
1007b6f8467SThierry Reding }
1017b6f8467SThierry Reding 
102e90124cbSThierry Reding static bool tegra_plane_format_mod_supported(struct drm_plane *plane,
103e90124cbSThierry Reding 					     uint32_t format,
104e90124cbSThierry Reding 					     uint64_t modifier)
105e90124cbSThierry Reding {
106e90124cbSThierry Reding 	const struct drm_format_info *info = drm_format_info(format);
107e90124cbSThierry Reding 
108e90124cbSThierry Reding 	if (modifier == DRM_FORMAT_MOD_LINEAR)
109e90124cbSThierry Reding 		return true;
110e90124cbSThierry Reding 
1117b6f8467SThierry Reding 	/* check for the sector layout bit */
112*c1d3cfbcSThierry Reding 	if (fourcc_mod_is_vendor(modifier, NVIDIA)) {
1137b6f8467SThierry Reding 		if (modifier & DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT) {
1147b6f8467SThierry Reding 			if (!tegra_plane_supports_sector_layout(plane))
1157b6f8467SThierry Reding 				return false;
1167b6f8467SThierry Reding 		}
1177b6f8467SThierry Reding 	}
1187b6f8467SThierry Reding 
119e90124cbSThierry Reding 	if (info->num_planes == 1)
120e90124cbSThierry Reding 		return true;
121e90124cbSThierry Reding 
122e90124cbSThierry Reding 	return false;
123e90124cbSThierry Reding }
124e90124cbSThierry Reding 
1255acd3514SThierry Reding const struct drm_plane_funcs tegra_plane_funcs = {
1265acd3514SThierry Reding 	.update_plane = drm_atomic_helper_update_plane,
1275acd3514SThierry Reding 	.disable_plane = drm_atomic_helper_disable_plane,
1285acd3514SThierry Reding 	.destroy = tegra_plane_destroy,
1295acd3514SThierry Reding 	.reset = tegra_plane_reset,
1305acd3514SThierry Reding 	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
1315acd3514SThierry Reding 	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
132e90124cbSThierry Reding 	.format_mod_supported = tegra_plane_format_mod_supported,
1335acd3514SThierry Reding };
1345acd3514SThierry Reding 
1352e8d8749SThierry Reding static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
1362e8d8749SThierry Reding {
137273da5a0SThierry Reding 	struct iommu_domain *domain = iommu_get_domain_for_dev(dc->dev);
1382e8d8749SThierry Reding 	unsigned int i;
1392e8d8749SThierry Reding 	int err;
1402e8d8749SThierry Reding 
1412e8d8749SThierry Reding 	for (i = 0; i < state->base.fb->format->num_planes; i++) {
1422e8d8749SThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
143273da5a0SThierry Reding 		dma_addr_t phys_addr, *phys;
1442e8d8749SThierry Reding 		struct sg_table *sgt;
1452e8d8749SThierry Reding 
146476e9320SThierry Reding 		/*
147476e9320SThierry Reding 		 * If we're not attached to a domain, we already stored the
148476e9320SThierry Reding 		 * physical address when the buffer was allocated. If we're
149476e9320SThierry Reding 		 * part of a group that's shared between all display
150476e9320SThierry Reding 		 * controllers, we've also already mapped the framebuffer
151476e9320SThierry Reding 		 * through the SMMU. In both cases we can short-circuit the
152476e9320SThierry Reding 		 * code below and retrieve the stored IOV address.
153476e9320SThierry Reding 		 */
154273da5a0SThierry Reding 		if (!domain || dc->client.group)
155273da5a0SThierry Reding 			phys = &phys_addr;
156273da5a0SThierry Reding 		else
157273da5a0SThierry Reding 			phys = NULL;
158273da5a0SThierry Reding 
159273da5a0SThierry Reding 		sgt = host1x_bo_pin(dc->dev, &bo->base, phys);
1602e8d8749SThierry Reding 		if (IS_ERR(sgt)) {
1612e8d8749SThierry Reding 			err = PTR_ERR(sgt);
1622e8d8749SThierry Reding 			goto unpin;
1632e8d8749SThierry Reding 		}
1642e8d8749SThierry Reding 
165273da5a0SThierry Reding 		if (sgt) {
166d4fea3e6SMarek Szyprowski 			err = dma_map_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
167d4fea3e6SMarek Szyprowski 			if (err)
1682e8d8749SThierry Reding 				goto unpin;
1692e8d8749SThierry Reding 
17049f82191SThierry Reding 			/*
17149f82191SThierry Reding 			 * The display controller needs contiguous memory, so
17249f82191SThierry Reding 			 * fail if the buffer is discontiguous and we fail to
17349f82191SThierry Reding 			 * map its SG table to a single contiguous chunk of
17449f82191SThierry Reding 			 * I/O virtual memory.
17549f82191SThierry Reding 			 */
176d4fea3e6SMarek Szyprowski 			if (sgt->nents > 1) {
17749f82191SThierry Reding 				err = -EINVAL;
17849f82191SThierry Reding 				goto unpin;
17949f82191SThierry Reding 			}
18049f82191SThierry Reding 
1812e8d8749SThierry Reding 			state->iova[i] = sg_dma_address(sgt->sgl);
1822e8d8749SThierry Reding 			state->sgt[i] = sgt;
1832e8d8749SThierry Reding 		} else {
184273da5a0SThierry Reding 			state->iova[i] = phys_addr;
1852e8d8749SThierry Reding 		}
1862e8d8749SThierry Reding 	}
1872e8d8749SThierry Reding 
1882e8d8749SThierry Reding 	return 0;
1892e8d8749SThierry Reding 
1902e8d8749SThierry Reding unpin:
1912e8d8749SThierry Reding 	dev_err(dc->dev, "failed to map plane %u: %d\n", i, err);
1922e8d8749SThierry Reding 
1932e8d8749SThierry Reding 	while (i--) {
1942e8d8749SThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
1952e8d8749SThierry Reding 		struct sg_table *sgt = state->sgt[i];
1962e8d8749SThierry Reding 
197273da5a0SThierry Reding 		if (sgt)
198d4fea3e6SMarek Szyprowski 			dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
1992e8d8749SThierry Reding 
200273da5a0SThierry Reding 		host1x_bo_unpin(dc->dev, &bo->base, sgt);
2012e8d8749SThierry Reding 		state->iova[i] = DMA_MAPPING_ERROR;
2022e8d8749SThierry Reding 		state->sgt[i] = NULL;
2032e8d8749SThierry Reding 	}
2042e8d8749SThierry Reding 
2052e8d8749SThierry Reding 	return err;
2062e8d8749SThierry Reding }
2072e8d8749SThierry Reding 
2082e8d8749SThierry Reding static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state)
2092e8d8749SThierry Reding {
2102e8d8749SThierry Reding 	unsigned int i;
2112e8d8749SThierry Reding 
2122e8d8749SThierry Reding 	for (i = 0; i < state->base.fb->format->num_planes; i++) {
2132e8d8749SThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
2142e8d8749SThierry Reding 		struct sg_table *sgt = state->sgt[i];
2152e8d8749SThierry Reding 
216273da5a0SThierry Reding 		if (sgt)
217d4fea3e6SMarek Szyprowski 			dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
2182e8d8749SThierry Reding 
219273da5a0SThierry Reding 		host1x_bo_unpin(dc->dev, &bo->base, sgt);
2202e8d8749SThierry Reding 		state->iova[i] = DMA_MAPPING_ERROR;
2212e8d8749SThierry Reding 		state->sgt[i] = NULL;
2222e8d8749SThierry Reding 	}
2232e8d8749SThierry Reding }
2242e8d8749SThierry Reding 
2252e8d8749SThierry Reding int tegra_plane_prepare_fb(struct drm_plane *plane,
2262e8d8749SThierry Reding 			   struct drm_plane_state *state)
2272e8d8749SThierry Reding {
2282e8d8749SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
2292e8d8749SThierry Reding 
2302e8d8749SThierry Reding 	if (!state->fb)
2312e8d8749SThierry Reding 		return 0;
2322e8d8749SThierry Reding 
233820c1707SThomas Zimmermann 	drm_gem_plane_helper_prepare_fb(plane, state);
2342e8d8749SThierry Reding 
2352e8d8749SThierry Reding 	return tegra_dc_pin(dc, to_tegra_plane_state(state));
2362e8d8749SThierry Reding }
2372e8d8749SThierry Reding 
2382e8d8749SThierry Reding void tegra_plane_cleanup_fb(struct drm_plane *plane,
2392e8d8749SThierry Reding 			    struct drm_plane_state *state)
2402e8d8749SThierry Reding {
2412e8d8749SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
2422e8d8749SThierry Reding 
2432e8d8749SThierry Reding 	if (dc)
2442e8d8749SThierry Reding 		tegra_dc_unpin(dc, to_tegra_plane_state(state));
2452e8d8749SThierry Reding }
2462e8d8749SThierry Reding 
2475acd3514SThierry Reding int tegra_plane_state_add(struct tegra_plane *plane,
2485acd3514SThierry Reding 			  struct drm_plane_state *state)
2495acd3514SThierry Reding {
2505acd3514SThierry Reding 	struct drm_crtc_state *crtc_state;
2515acd3514SThierry Reding 	struct tegra_dc_state *tegra;
2525acd3514SThierry Reding 	int err;
2535acd3514SThierry Reding 
2545acd3514SThierry Reding 	/* Propagate errors from allocation or locking failures. */
2555acd3514SThierry Reding 	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
2565acd3514SThierry Reding 	if (IS_ERR(crtc_state))
2575acd3514SThierry Reding 		return PTR_ERR(crtc_state);
2585acd3514SThierry Reding 
2595acd3514SThierry Reding 	/* Check plane state for visibility and calculate clipping bounds */
26081af63a4SVille Syrjälä 	err = drm_atomic_helper_check_plane_state(state, crtc_state,
2615acd3514SThierry Reding 						  0, INT_MAX, true, true);
2625acd3514SThierry Reding 	if (err < 0)
2635acd3514SThierry Reding 		return err;
2645acd3514SThierry Reding 
2655acd3514SThierry Reding 	tegra = to_dc_state(crtc_state);
2665acd3514SThierry Reding 
2675acd3514SThierry Reding 	tegra->planes |= WIN_A_ACT_REQ << plane->index;
2685acd3514SThierry Reding 
2695acd3514SThierry Reding 	return 0;
2705acd3514SThierry Reding }
2715acd3514SThierry Reding 
2725acd3514SThierry Reding int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap)
2735acd3514SThierry Reding {
2745acd3514SThierry Reding 	/* assume no swapping of fetched data */
2755acd3514SThierry Reding 	if (swap)
2765acd3514SThierry Reding 		*swap = BYTE_SWAP_NOSWAP;
2775acd3514SThierry Reding 
2785acd3514SThierry Reding 	switch (fourcc) {
279511c7023SThierry Reding 	case DRM_FORMAT_ARGB4444:
280511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B4G4R4A4;
2817772fdaeSThierry Reding 		break;
2827772fdaeSThierry Reding 
283511c7023SThierry Reding 	case DRM_FORMAT_ARGB1555:
284511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B5G5R5A1;
2855acd3514SThierry Reding 		break;
2865acd3514SThierry Reding 
287511c7023SThierry Reding 	case DRM_FORMAT_RGB565:
288511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B5G6R5;
289511c7023SThierry Reding 		break;
290511c7023SThierry Reding 
291511c7023SThierry Reding 	case DRM_FORMAT_RGBA5551:
292511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_A1B5G5R5;
2937772fdaeSThierry Reding 		break;
2947772fdaeSThierry Reding 
2957772fdaeSThierry Reding 	case DRM_FORMAT_ARGB8888:
2965acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_B8G8R8A8;
2975acd3514SThierry Reding 		break;
2985acd3514SThierry Reding 
299511c7023SThierry Reding 	case DRM_FORMAT_ABGR8888:
300511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R8G8B8A8;
301511c7023SThierry Reding 		break;
302511c7023SThierry Reding 
303511c7023SThierry Reding 	case DRM_FORMAT_ABGR4444:
304511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R4G4B4A4;
305511c7023SThierry Reding 		break;
306511c7023SThierry Reding 
307511c7023SThierry Reding 	case DRM_FORMAT_ABGR1555:
308511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R5G5B5A;
309511c7023SThierry Reding 		break;
310511c7023SThierry Reding 
311511c7023SThierry Reding 	case DRM_FORMAT_BGRA5551:
312511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_AR5G5B5;
313511c7023SThierry Reding 		break;
314511c7023SThierry Reding 
315511c7023SThierry Reding 	case DRM_FORMAT_XRGB1555:
316511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B5G5R5X1;
317511c7023SThierry Reding 		break;
318511c7023SThierry Reding 
319511c7023SThierry Reding 	case DRM_FORMAT_RGBX5551:
320511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_X1B5G5R5;
321511c7023SThierry Reding 		break;
322511c7023SThierry Reding 
323511c7023SThierry Reding 	case DRM_FORMAT_XBGR1555:
324511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R5G5B5X1;
325511c7023SThierry Reding 		break;
326511c7023SThierry Reding 
327511c7023SThierry Reding 	case DRM_FORMAT_BGRX5551:
328511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_X1R5G5B5;
329511c7023SThierry Reding 		break;
330511c7023SThierry Reding 
331511c7023SThierry Reding 	case DRM_FORMAT_BGR565:
332511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R5G6B5;
333511c7023SThierry Reding 		break;
334511c7023SThierry Reding 
335511c7023SThierry Reding 	case DRM_FORMAT_BGRA8888:
336511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_A8R8G8B8;
337511c7023SThierry Reding 		break;
338511c7023SThierry Reding 
339511c7023SThierry Reding 	case DRM_FORMAT_RGBA8888:
340511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_A8B8G8R8;
341511c7023SThierry Reding 		break;
342511c7023SThierry Reding 
343511c7023SThierry Reding 	case DRM_FORMAT_XRGB8888:
344511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B8G8R8X8;
345511c7023SThierry Reding 		break;
346511c7023SThierry Reding 
347511c7023SThierry Reding 	case DRM_FORMAT_XBGR8888:
348511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R8G8B8X8;
3495acd3514SThierry Reding 		break;
3505acd3514SThierry Reding 
3515acd3514SThierry Reding 	case DRM_FORMAT_UYVY:
3525acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr422;
3535acd3514SThierry Reding 		break;
3545acd3514SThierry Reding 
3555acd3514SThierry Reding 	case DRM_FORMAT_YUYV:
3565acd3514SThierry Reding 		if (!swap)
3575acd3514SThierry Reding 			return -EINVAL;
3585acd3514SThierry Reding 
3595acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr422;
3605acd3514SThierry Reding 		*swap = BYTE_SWAP_SWAP2;
3615acd3514SThierry Reding 		break;
3625acd3514SThierry Reding 
3635acd3514SThierry Reding 	case DRM_FORMAT_YUV420:
3645acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr420P;
3655acd3514SThierry Reding 		break;
3665acd3514SThierry Reding 
3675acd3514SThierry Reding 	case DRM_FORMAT_YUV422:
3685acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr422P;
3695acd3514SThierry Reding 		break;
3705acd3514SThierry Reding 
3715acd3514SThierry Reding 	default:
3725acd3514SThierry Reding 		return -EINVAL;
3735acd3514SThierry Reding 	}
3745acd3514SThierry Reding 
3755acd3514SThierry Reding 	return 0;
3765acd3514SThierry Reding }
3775acd3514SThierry Reding 
378e16efff4SThierry Reding bool tegra_plane_format_is_indexed(unsigned int format)
379e16efff4SThierry Reding {
380e16efff4SThierry Reding 	switch (format) {
381e16efff4SThierry Reding 	case WIN_COLOR_DEPTH_P1:
382e16efff4SThierry Reding 	case WIN_COLOR_DEPTH_P2:
383e16efff4SThierry Reding 	case WIN_COLOR_DEPTH_P4:
384e16efff4SThierry Reding 	case WIN_COLOR_DEPTH_P8:
385e16efff4SThierry Reding 		return true;
386e16efff4SThierry Reding 	}
387e16efff4SThierry Reding 
388e16efff4SThierry Reding 	return false;
389e16efff4SThierry Reding }
390e16efff4SThierry Reding 
391e16efff4SThierry Reding bool tegra_plane_format_is_yuv(unsigned int format, bool *planar, unsigned int *bpc)
3925acd3514SThierry Reding {
3935acd3514SThierry Reding 	switch (format) {
3945acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422:
3955acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422:
3965acd3514SThierry Reding 		if (planar)
3975acd3514SThierry Reding 			*planar = false;
3985acd3514SThierry Reding 
399e16efff4SThierry Reding 		if (bpc)
400e16efff4SThierry Reding 			*bpc = 8;
401e16efff4SThierry Reding 
4025acd3514SThierry Reding 		return true;
4035acd3514SThierry Reding 
4045acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr420P:
4055acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV420P:
4065acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422P:
4075acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422P:
4085acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422R:
4095acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422R:
4105acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422RA:
4115acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422RA:
4125acd3514SThierry Reding 		if (planar)
4135acd3514SThierry Reding 			*planar = true;
4145acd3514SThierry Reding 
415e16efff4SThierry Reding 		if (bpc)
416e16efff4SThierry Reding 			*bpc = 8;
417e16efff4SThierry Reding 
4185acd3514SThierry Reding 		return true;
4195acd3514SThierry Reding 	}
4205acd3514SThierry Reding 
4215acd3514SThierry Reding 	if (planar)
4225acd3514SThierry Reding 		*planar = false;
4235acd3514SThierry Reding 
4245acd3514SThierry Reding 	return false;
4255acd3514SThierry Reding }
426ebae8d07SThierry Reding 
427ebae8d07SThierry Reding static bool __drm_format_has_alpha(u32 format)
428ebae8d07SThierry Reding {
429ebae8d07SThierry Reding 	switch (format) {
430ebae8d07SThierry Reding 	case DRM_FORMAT_ARGB1555:
431ebae8d07SThierry Reding 	case DRM_FORMAT_RGBA5551:
432ebae8d07SThierry Reding 	case DRM_FORMAT_ABGR8888:
433ebae8d07SThierry Reding 	case DRM_FORMAT_ARGB8888:
434ebae8d07SThierry Reding 		return true;
435ebae8d07SThierry Reding 	}
436ebae8d07SThierry Reding 
437ebae8d07SThierry Reding 	return false;
438ebae8d07SThierry Reding }
439ebae8d07SThierry Reding 
4403dae08bcSDmitry Osipenko static int tegra_plane_format_get_alpha(unsigned int opaque,
4413dae08bcSDmitry Osipenko 					unsigned int *alpha)
442ebae8d07SThierry Reding {
443e16efff4SThierry Reding 	if (tegra_plane_format_is_yuv(opaque, NULL, NULL)) {
4445467a8b8SThierry Reding 		*alpha = opaque;
4455467a8b8SThierry Reding 		return 0;
4465467a8b8SThierry Reding 	}
4475467a8b8SThierry Reding 
448ebae8d07SThierry Reding 	switch (opaque) {
449ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_B5G5R5X1:
450ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_B5G5R5A1;
451ebae8d07SThierry Reding 		return 0;
452ebae8d07SThierry Reding 
453ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_X1B5G5R5:
454ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_A1B5G5R5;
455ebae8d07SThierry Reding 		return 0;
456ebae8d07SThierry Reding 
457ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_R8G8B8X8:
458ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_R8G8B8A8;
459ebae8d07SThierry Reding 		return 0;
460ebae8d07SThierry Reding 
461ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_B8G8R8X8:
462ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_B8G8R8A8;
463ebae8d07SThierry Reding 		return 0;
4648a927d64SThierry Reding 
4658a927d64SThierry Reding 	case WIN_COLOR_DEPTH_B5G6R5:
4668a927d64SThierry Reding 		*alpha = opaque;
4678a927d64SThierry Reding 		return 0;
468ebae8d07SThierry Reding 	}
469ebae8d07SThierry Reding 
470ebae8d07SThierry Reding 	return -EINVAL;
471ebae8d07SThierry Reding }
472ebae8d07SThierry Reding 
4733dae08bcSDmitry Osipenko /*
4743dae08bcSDmitry Osipenko  * This is applicable to Tegra20 and Tegra30 only where the opaque formats can
4753dae08bcSDmitry Osipenko  * be emulated using the alpha formats and alpha blending disabled.
4763dae08bcSDmitry Osipenko  */
4773dae08bcSDmitry Osipenko static int tegra_plane_setup_opacity(struct tegra_plane *tegra,
4783dae08bcSDmitry Osipenko 				     struct tegra_plane_state *state)
4793dae08bcSDmitry Osipenko {
4803dae08bcSDmitry Osipenko 	unsigned int format;
4813dae08bcSDmitry Osipenko 	int err;
4823dae08bcSDmitry Osipenko 
4833dae08bcSDmitry Osipenko 	switch (state->format) {
4843dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_B5G5R5A1:
4853dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_A1B5G5R5:
4863dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_R8G8B8A8:
4873dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_B8G8R8A8:
4883dae08bcSDmitry Osipenko 		state->opaque = false;
4893dae08bcSDmitry Osipenko 		break;
4903dae08bcSDmitry Osipenko 
4913dae08bcSDmitry Osipenko 	default:
4923dae08bcSDmitry Osipenko 		err = tegra_plane_format_get_alpha(state->format, &format);
4933dae08bcSDmitry Osipenko 		if (err < 0)
4943dae08bcSDmitry Osipenko 			return err;
4953dae08bcSDmitry Osipenko 
4963dae08bcSDmitry Osipenko 		state->format = format;
4973dae08bcSDmitry Osipenko 		state->opaque = true;
4983dae08bcSDmitry Osipenko 		break;
4993dae08bcSDmitry Osipenko 	}
5003dae08bcSDmitry Osipenko 
5013dae08bcSDmitry Osipenko 	return 0;
5023dae08bcSDmitry Osipenko }
5033dae08bcSDmitry Osipenko 
5043dae08bcSDmitry Osipenko static int tegra_plane_check_transparency(struct tegra_plane *tegra,
5053dae08bcSDmitry Osipenko 					  struct tegra_plane_state *state)
5063dae08bcSDmitry Osipenko {
5073dae08bcSDmitry Osipenko 	struct drm_plane_state *old, *plane_state;
5083dae08bcSDmitry Osipenko 	struct drm_plane *plane;
5093dae08bcSDmitry Osipenko 
5103dae08bcSDmitry Osipenko 	old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
5113dae08bcSDmitry Osipenko 
5123dae08bcSDmitry Osipenko 	/* check if zpos / transparency changed */
5133dae08bcSDmitry Osipenko 	if (old->normalized_zpos == state->base.normalized_zpos &&
5143dae08bcSDmitry Osipenko 	    to_tegra_plane_state(old)->opaque == state->opaque)
5153dae08bcSDmitry Osipenko 		return 0;
5163dae08bcSDmitry Osipenko 
5173dae08bcSDmitry Osipenko 	/* include all sibling planes into this commit */
5183dae08bcSDmitry Osipenko 	drm_for_each_plane(plane, tegra->base.dev) {
5193dae08bcSDmitry Osipenko 		struct tegra_plane *p = to_tegra_plane(plane);
5203dae08bcSDmitry Osipenko 
5213dae08bcSDmitry Osipenko 		/* skip this plane and planes on different CRTCs */
5223dae08bcSDmitry Osipenko 		if (p == tegra || p->dc != tegra->dc)
5233dae08bcSDmitry Osipenko 			continue;
5243dae08bcSDmitry Osipenko 
5253dae08bcSDmitry Osipenko 		plane_state = drm_atomic_get_plane_state(state->base.state,
5263dae08bcSDmitry Osipenko 							 plane);
5273dae08bcSDmitry Osipenko 		if (IS_ERR(plane_state))
5283dae08bcSDmitry Osipenko 			return PTR_ERR(plane_state);
5293dae08bcSDmitry Osipenko 	}
5303dae08bcSDmitry Osipenko 
5313dae08bcSDmitry Osipenko 	return 1;
5323dae08bcSDmitry Osipenko }
5333dae08bcSDmitry Osipenko 
5345e2e86f1SDmitry Osipenko static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
535ebae8d07SThierry Reding 						  struct tegra_plane *other)
536ebae8d07SThierry Reding {
537ebae8d07SThierry Reding 	unsigned int index = 0, i;
538ebae8d07SThierry Reding 
539ebae8d07SThierry Reding 	WARN_ON(plane == other);
540ebae8d07SThierry Reding 
541ebae8d07SThierry Reding 	for (i = 0; i < 3; i++) {
542ebae8d07SThierry Reding 		if (i == plane->index)
543ebae8d07SThierry Reding 			continue;
544ebae8d07SThierry Reding 
545ebae8d07SThierry Reding 		if (i == other->index)
546ebae8d07SThierry Reding 			break;
547ebae8d07SThierry Reding 
548ebae8d07SThierry Reding 		index++;
549ebae8d07SThierry Reding 	}
550ebae8d07SThierry Reding 
551ebae8d07SThierry Reding 	return index;
552ebae8d07SThierry Reding }
553ebae8d07SThierry Reding 
5543dae08bcSDmitry Osipenko static void tegra_plane_update_transparency(struct tegra_plane *tegra,
555ebae8d07SThierry Reding 					    struct tegra_plane_state *state)
556ebae8d07SThierry Reding {
5573dae08bcSDmitry Osipenko 	struct drm_plane_state *new;
558ebae8d07SThierry Reding 	struct drm_plane *plane;
559ebae8d07SThierry Reding 	unsigned int i;
560ebae8d07SThierry Reding 
5613dae08bcSDmitry Osipenko 	for_each_new_plane_in_state(state->base.state, plane, new, i) {
562ebae8d07SThierry Reding 		struct tegra_plane *p = to_tegra_plane(plane);
563ebae8d07SThierry Reding 		unsigned index;
564ebae8d07SThierry Reding 
565ebae8d07SThierry Reding 		/* skip this plane and planes on different CRTCs */
5663dae08bcSDmitry Osipenko 		if (p == tegra || p->dc != tegra->dc)
567ebae8d07SThierry Reding 			continue;
568ebae8d07SThierry Reding 
569ebae8d07SThierry Reding 		index = tegra_plane_get_overlap_index(tegra, p);
570ebae8d07SThierry Reding 
5713dae08bcSDmitry Osipenko 		if (new->fb && __drm_format_has_alpha(new->fb->format->format))
5723dae08bcSDmitry Osipenko 			state->blending[index].alpha = true;
5733dae08bcSDmitry Osipenko 		else
5743dae08bcSDmitry Osipenko 			state->blending[index].alpha = false;
5753dae08bcSDmitry Osipenko 
5763dae08bcSDmitry Osipenko 		if (new->normalized_zpos > state->base.normalized_zpos)
5773dae08bcSDmitry Osipenko 			state->blending[index].top = true;
5783dae08bcSDmitry Osipenko 		else
5793dae08bcSDmitry Osipenko 			state->blending[index].top = false;
58048519232SDmitry Osipenko 
581ebae8d07SThierry Reding 		/*
5823dae08bcSDmitry Osipenko 		 * Missing framebuffer means that plane is disabled, in this
5833dae08bcSDmitry Osipenko 		 * case mark B / C window as top to be able to differentiate
5843dae08bcSDmitry Osipenko 		 * windows indices order in regards to zPos for the middle
5853dae08bcSDmitry Osipenko 		 * window X / Y registers programming.
586ebae8d07SThierry Reding 		 */
5873dae08bcSDmitry Osipenko 		if (!new->fb)
5883dae08bcSDmitry Osipenko 			state->blending[index].top = (index == 1);
589ebae8d07SThierry Reding 	}
590ebae8d07SThierry Reding }
591ebae8d07SThierry Reding 
5923dae08bcSDmitry Osipenko static int tegra_plane_setup_transparency(struct tegra_plane *tegra,
5933dae08bcSDmitry Osipenko 					  struct tegra_plane_state *state)
5943dae08bcSDmitry Osipenko {
5953dae08bcSDmitry Osipenko 	struct tegra_plane_state *tegra_state;
5963dae08bcSDmitry Osipenko 	struct drm_plane_state *new;
5973dae08bcSDmitry Osipenko 	struct drm_plane *plane;
5983dae08bcSDmitry Osipenko 	int err;
599ebae8d07SThierry Reding 
600ebae8d07SThierry Reding 	/*
6013dae08bcSDmitry Osipenko 	 * If planes zpos / transparency changed, sibling planes blending
6023dae08bcSDmitry Osipenko 	 * state may require adjustment and in this case they will be included
6033dae08bcSDmitry Osipenko 	 * into this atom commit, otherwise blending state is unchanged.
604ebae8d07SThierry Reding 	 */
6053dae08bcSDmitry Osipenko 	err = tegra_plane_check_transparency(tegra, state);
6063dae08bcSDmitry Osipenko 	if (err <= 0)
6073dae08bcSDmitry Osipenko 		return err;
6083dae08bcSDmitry Osipenko 
6093dae08bcSDmitry Osipenko 	/*
6103dae08bcSDmitry Osipenko 	 * All planes are now in the atomic state, walk them up and update
6113dae08bcSDmitry Osipenko 	 * transparency state for each plane.
6123dae08bcSDmitry Osipenko 	 */
6133dae08bcSDmitry Osipenko 	drm_for_each_plane(plane, tegra->base.dev) {
6143dae08bcSDmitry Osipenko 		struct tegra_plane *p = to_tegra_plane(plane);
6153dae08bcSDmitry Osipenko 
6163dae08bcSDmitry Osipenko 		/* skip planes on different CRTCs */
6173dae08bcSDmitry Osipenko 		if (p->dc != tegra->dc)
6183dae08bcSDmitry Osipenko 			continue;
6193dae08bcSDmitry Osipenko 
6203dae08bcSDmitry Osipenko 		new = drm_atomic_get_new_plane_state(state->base.state, plane);
6213dae08bcSDmitry Osipenko 		tegra_state = to_tegra_plane_state(new);
6223dae08bcSDmitry Osipenko 
6233dae08bcSDmitry Osipenko 		/*
6243dae08bcSDmitry Osipenko 		 * There is no need to update blending state for the disabled
6253dae08bcSDmitry Osipenko 		 * plane.
6263dae08bcSDmitry Osipenko 		 */
6273dae08bcSDmitry Osipenko 		if (new->fb)
6283dae08bcSDmitry Osipenko 			tegra_plane_update_transparency(p, tegra_state);
629ebae8d07SThierry Reding 	}
6303dae08bcSDmitry Osipenko 
6313dae08bcSDmitry Osipenko 	return 0;
6323dae08bcSDmitry Osipenko }
6333dae08bcSDmitry Osipenko 
6343dae08bcSDmitry Osipenko int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
6353dae08bcSDmitry Osipenko 				   struct tegra_plane_state *state)
6363dae08bcSDmitry Osipenko {
6373dae08bcSDmitry Osipenko 	int err;
6383dae08bcSDmitry Osipenko 
6393dae08bcSDmitry Osipenko 	err = tegra_plane_setup_opacity(tegra, state);
6403dae08bcSDmitry Osipenko 	if (err < 0)
6413dae08bcSDmitry Osipenko 		return err;
6423dae08bcSDmitry Osipenko 
6433dae08bcSDmitry Osipenko 	err = tegra_plane_setup_transparency(tegra, state);
6443dae08bcSDmitry Osipenko 	if (err < 0)
6453dae08bcSDmitry Osipenko 		return err;
6463dae08bcSDmitry Osipenko 
6473dae08bcSDmitry Osipenko 	return 0;
648ebae8d07SThierry Reding }
649