xref: /linux/drivers/gpu/drm/tegra/plane.c (revision d4fea3e61b47a22e57a58d7afd6f0209051208bd)
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>
112e8d8749SThierry Reding #include <drm/drm_gem_framebuffer_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 
86e90124cbSThierry Reding static bool tegra_plane_format_mod_supported(struct drm_plane *plane,
87e90124cbSThierry Reding 					     uint32_t format,
88e90124cbSThierry Reding 					     uint64_t modifier)
89e90124cbSThierry Reding {
90e90124cbSThierry Reding 	const struct drm_format_info *info = drm_format_info(format);
91e90124cbSThierry Reding 
92e90124cbSThierry Reding 	if (modifier == DRM_FORMAT_MOD_LINEAR)
93e90124cbSThierry Reding 		return true;
94e90124cbSThierry Reding 
95e90124cbSThierry Reding 	if (info->num_planes == 1)
96e90124cbSThierry Reding 		return true;
97e90124cbSThierry Reding 
98e90124cbSThierry Reding 	return false;
99e90124cbSThierry Reding }
100e90124cbSThierry Reding 
1015acd3514SThierry Reding const struct drm_plane_funcs tegra_plane_funcs = {
1025acd3514SThierry Reding 	.update_plane = drm_atomic_helper_update_plane,
1035acd3514SThierry Reding 	.disable_plane = drm_atomic_helper_disable_plane,
1045acd3514SThierry Reding 	.destroy = tegra_plane_destroy,
1055acd3514SThierry Reding 	.reset = tegra_plane_reset,
1065acd3514SThierry Reding 	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
1075acd3514SThierry Reding 	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
108e90124cbSThierry Reding 	.format_mod_supported = tegra_plane_format_mod_supported,
1095acd3514SThierry Reding };
1105acd3514SThierry Reding 
1112e8d8749SThierry Reding static int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
1122e8d8749SThierry Reding {
113273da5a0SThierry Reding 	struct iommu_domain *domain = iommu_get_domain_for_dev(dc->dev);
1142e8d8749SThierry Reding 	unsigned int i;
1152e8d8749SThierry Reding 	int err;
1162e8d8749SThierry Reding 
1172e8d8749SThierry Reding 	for (i = 0; i < state->base.fb->format->num_planes; i++) {
1182e8d8749SThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
119273da5a0SThierry Reding 		dma_addr_t phys_addr, *phys;
1202e8d8749SThierry Reding 		struct sg_table *sgt;
1212e8d8749SThierry Reding 
122273da5a0SThierry Reding 		if (!domain || dc->client.group)
123273da5a0SThierry Reding 			phys = &phys_addr;
124273da5a0SThierry Reding 		else
125273da5a0SThierry Reding 			phys = NULL;
126273da5a0SThierry Reding 
127273da5a0SThierry Reding 		sgt = host1x_bo_pin(dc->dev, &bo->base, phys);
1282e8d8749SThierry Reding 		if (IS_ERR(sgt)) {
1292e8d8749SThierry Reding 			err = PTR_ERR(sgt);
1302e8d8749SThierry Reding 			goto unpin;
1312e8d8749SThierry Reding 		}
1322e8d8749SThierry Reding 
133273da5a0SThierry Reding 		if (sgt) {
134*d4fea3e6SMarek Szyprowski 			err = dma_map_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
135*d4fea3e6SMarek Szyprowski 			if (err)
1362e8d8749SThierry Reding 				goto unpin;
1372e8d8749SThierry Reding 
13849f82191SThierry Reding 			/*
13949f82191SThierry Reding 			 * The display controller needs contiguous memory, so
14049f82191SThierry Reding 			 * fail if the buffer is discontiguous and we fail to
14149f82191SThierry Reding 			 * map its SG table to a single contiguous chunk of
14249f82191SThierry Reding 			 * I/O virtual memory.
14349f82191SThierry Reding 			 */
144*d4fea3e6SMarek Szyprowski 			if (sgt->nents > 1) {
14549f82191SThierry Reding 				err = -EINVAL;
14649f82191SThierry Reding 				goto unpin;
14749f82191SThierry Reding 			}
14849f82191SThierry Reding 
1492e8d8749SThierry Reding 			state->iova[i] = sg_dma_address(sgt->sgl);
1502e8d8749SThierry Reding 			state->sgt[i] = sgt;
1512e8d8749SThierry Reding 		} else {
152273da5a0SThierry Reding 			state->iova[i] = phys_addr;
1532e8d8749SThierry Reding 		}
1542e8d8749SThierry Reding 	}
1552e8d8749SThierry Reding 
1562e8d8749SThierry Reding 	return 0;
1572e8d8749SThierry Reding 
1582e8d8749SThierry Reding unpin:
1592e8d8749SThierry Reding 	dev_err(dc->dev, "failed to map plane %u: %d\n", i, err);
1602e8d8749SThierry Reding 
1612e8d8749SThierry Reding 	while (i--) {
1622e8d8749SThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
1632e8d8749SThierry Reding 		struct sg_table *sgt = state->sgt[i];
1642e8d8749SThierry Reding 
165273da5a0SThierry Reding 		if (sgt)
166*d4fea3e6SMarek Szyprowski 			dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
1672e8d8749SThierry Reding 
168273da5a0SThierry Reding 		host1x_bo_unpin(dc->dev, &bo->base, sgt);
1692e8d8749SThierry Reding 		state->iova[i] = DMA_MAPPING_ERROR;
1702e8d8749SThierry Reding 		state->sgt[i] = NULL;
1712e8d8749SThierry Reding 	}
1722e8d8749SThierry Reding 
1732e8d8749SThierry Reding 	return err;
1742e8d8749SThierry Reding }
1752e8d8749SThierry Reding 
1762e8d8749SThierry Reding static void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state)
1772e8d8749SThierry Reding {
1782e8d8749SThierry Reding 	unsigned int i;
1792e8d8749SThierry Reding 
1802e8d8749SThierry Reding 	for (i = 0; i < state->base.fb->format->num_planes; i++) {
1812e8d8749SThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
1822e8d8749SThierry Reding 		struct sg_table *sgt = state->sgt[i];
1832e8d8749SThierry Reding 
184273da5a0SThierry Reding 		if (sgt)
185*d4fea3e6SMarek Szyprowski 			dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
1862e8d8749SThierry Reding 
187273da5a0SThierry Reding 		host1x_bo_unpin(dc->dev, &bo->base, sgt);
1882e8d8749SThierry Reding 		state->iova[i] = DMA_MAPPING_ERROR;
1892e8d8749SThierry Reding 		state->sgt[i] = NULL;
1902e8d8749SThierry Reding 	}
1912e8d8749SThierry Reding }
1922e8d8749SThierry Reding 
1932e8d8749SThierry Reding int tegra_plane_prepare_fb(struct drm_plane *plane,
1942e8d8749SThierry Reding 			   struct drm_plane_state *state)
1952e8d8749SThierry Reding {
1962e8d8749SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
1972e8d8749SThierry Reding 
1982e8d8749SThierry Reding 	if (!state->fb)
1992e8d8749SThierry Reding 		return 0;
2002e8d8749SThierry Reding 
2012e8d8749SThierry Reding 	drm_gem_fb_prepare_fb(plane, state);
2022e8d8749SThierry Reding 
2032e8d8749SThierry Reding 	return tegra_dc_pin(dc, to_tegra_plane_state(state));
2042e8d8749SThierry Reding }
2052e8d8749SThierry Reding 
2062e8d8749SThierry Reding void tegra_plane_cleanup_fb(struct drm_plane *plane,
2072e8d8749SThierry Reding 			    struct drm_plane_state *state)
2082e8d8749SThierry Reding {
2092e8d8749SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
2102e8d8749SThierry Reding 
2112e8d8749SThierry Reding 	if (dc)
2122e8d8749SThierry Reding 		tegra_dc_unpin(dc, to_tegra_plane_state(state));
2132e8d8749SThierry Reding }
2142e8d8749SThierry Reding 
2155acd3514SThierry Reding int tegra_plane_state_add(struct tegra_plane *plane,
2165acd3514SThierry Reding 			  struct drm_plane_state *state)
2175acd3514SThierry Reding {
2185acd3514SThierry Reding 	struct drm_crtc_state *crtc_state;
2195acd3514SThierry Reding 	struct tegra_dc_state *tegra;
2205acd3514SThierry Reding 	int err;
2215acd3514SThierry Reding 
2225acd3514SThierry Reding 	/* Propagate errors from allocation or locking failures. */
2235acd3514SThierry Reding 	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
2245acd3514SThierry Reding 	if (IS_ERR(crtc_state))
2255acd3514SThierry Reding 		return PTR_ERR(crtc_state);
2265acd3514SThierry Reding 
2275acd3514SThierry Reding 	/* Check plane state for visibility and calculate clipping bounds */
22881af63a4SVille Syrjälä 	err = drm_atomic_helper_check_plane_state(state, crtc_state,
2295acd3514SThierry Reding 						  0, INT_MAX, true, true);
2305acd3514SThierry Reding 	if (err < 0)
2315acd3514SThierry Reding 		return err;
2325acd3514SThierry Reding 
2335acd3514SThierry Reding 	tegra = to_dc_state(crtc_state);
2345acd3514SThierry Reding 
2355acd3514SThierry Reding 	tegra->planes |= WIN_A_ACT_REQ << plane->index;
2365acd3514SThierry Reding 
2375acd3514SThierry Reding 	return 0;
2385acd3514SThierry Reding }
2395acd3514SThierry Reding 
2405acd3514SThierry Reding int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap)
2415acd3514SThierry Reding {
2425acd3514SThierry Reding 	/* assume no swapping of fetched data */
2435acd3514SThierry Reding 	if (swap)
2445acd3514SThierry Reding 		*swap = BYTE_SWAP_NOSWAP;
2455acd3514SThierry Reding 
2465acd3514SThierry Reding 	switch (fourcc) {
247511c7023SThierry Reding 	case DRM_FORMAT_ARGB4444:
248511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B4G4R4A4;
2497772fdaeSThierry Reding 		break;
2507772fdaeSThierry Reding 
251511c7023SThierry Reding 	case DRM_FORMAT_ARGB1555:
252511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B5G5R5A1;
2535acd3514SThierry Reding 		break;
2545acd3514SThierry Reding 
255511c7023SThierry Reding 	case DRM_FORMAT_RGB565:
256511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B5G6R5;
257511c7023SThierry Reding 		break;
258511c7023SThierry Reding 
259511c7023SThierry Reding 	case DRM_FORMAT_RGBA5551:
260511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_A1B5G5R5;
2617772fdaeSThierry Reding 		break;
2627772fdaeSThierry Reding 
2637772fdaeSThierry Reding 	case DRM_FORMAT_ARGB8888:
2645acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_B8G8R8A8;
2655acd3514SThierry Reding 		break;
2665acd3514SThierry Reding 
267511c7023SThierry Reding 	case DRM_FORMAT_ABGR8888:
268511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R8G8B8A8;
269511c7023SThierry Reding 		break;
270511c7023SThierry Reding 
271511c7023SThierry Reding 	case DRM_FORMAT_ABGR4444:
272511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R4G4B4A4;
273511c7023SThierry Reding 		break;
274511c7023SThierry Reding 
275511c7023SThierry Reding 	case DRM_FORMAT_ABGR1555:
276511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R5G5B5A;
277511c7023SThierry Reding 		break;
278511c7023SThierry Reding 
279511c7023SThierry Reding 	case DRM_FORMAT_BGRA5551:
280511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_AR5G5B5;
281511c7023SThierry Reding 		break;
282511c7023SThierry Reding 
283511c7023SThierry Reding 	case DRM_FORMAT_XRGB1555:
284511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B5G5R5X1;
285511c7023SThierry Reding 		break;
286511c7023SThierry Reding 
287511c7023SThierry Reding 	case DRM_FORMAT_RGBX5551:
288511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_X1B5G5R5;
289511c7023SThierry Reding 		break;
290511c7023SThierry Reding 
291511c7023SThierry Reding 	case DRM_FORMAT_XBGR1555:
292511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R5G5B5X1;
293511c7023SThierry Reding 		break;
294511c7023SThierry Reding 
295511c7023SThierry Reding 	case DRM_FORMAT_BGRX5551:
296511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_X1R5G5B5;
297511c7023SThierry Reding 		break;
298511c7023SThierry Reding 
299511c7023SThierry Reding 	case DRM_FORMAT_BGR565:
300511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R5G6B5;
301511c7023SThierry Reding 		break;
302511c7023SThierry Reding 
303511c7023SThierry Reding 	case DRM_FORMAT_BGRA8888:
304511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_A8R8G8B8;
305511c7023SThierry Reding 		break;
306511c7023SThierry Reding 
307511c7023SThierry Reding 	case DRM_FORMAT_RGBA8888:
308511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_A8B8G8R8;
309511c7023SThierry Reding 		break;
310511c7023SThierry Reding 
311511c7023SThierry Reding 	case DRM_FORMAT_XRGB8888:
312511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_B8G8R8X8;
313511c7023SThierry Reding 		break;
314511c7023SThierry Reding 
315511c7023SThierry Reding 	case DRM_FORMAT_XBGR8888:
316511c7023SThierry Reding 		*format = WIN_COLOR_DEPTH_R8G8B8X8;
3175acd3514SThierry Reding 		break;
3185acd3514SThierry Reding 
3195acd3514SThierry Reding 	case DRM_FORMAT_UYVY:
3205acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr422;
3215acd3514SThierry Reding 		break;
3225acd3514SThierry Reding 
3235acd3514SThierry Reding 	case DRM_FORMAT_YUYV:
3245acd3514SThierry Reding 		if (!swap)
3255acd3514SThierry Reding 			return -EINVAL;
3265acd3514SThierry Reding 
3275acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr422;
3285acd3514SThierry Reding 		*swap = BYTE_SWAP_SWAP2;
3295acd3514SThierry Reding 		break;
3305acd3514SThierry Reding 
3315acd3514SThierry Reding 	case DRM_FORMAT_YUV420:
3325acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr420P;
3335acd3514SThierry Reding 		break;
3345acd3514SThierry Reding 
3355acd3514SThierry Reding 	case DRM_FORMAT_YUV422:
3365acd3514SThierry Reding 		*format = WIN_COLOR_DEPTH_YCbCr422P;
3375acd3514SThierry Reding 		break;
3385acd3514SThierry Reding 
3395acd3514SThierry Reding 	default:
3405acd3514SThierry Reding 		return -EINVAL;
3415acd3514SThierry Reding 	}
3425acd3514SThierry Reding 
3435acd3514SThierry Reding 	return 0;
3445acd3514SThierry Reding }
3455acd3514SThierry Reding 
3465acd3514SThierry Reding bool tegra_plane_format_is_yuv(unsigned int format, bool *planar)
3475acd3514SThierry Reding {
3485acd3514SThierry Reding 	switch (format) {
3495acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422:
3505acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422:
3515acd3514SThierry Reding 		if (planar)
3525acd3514SThierry Reding 			*planar = false;
3535acd3514SThierry Reding 
3545acd3514SThierry Reding 		return true;
3555acd3514SThierry Reding 
3565acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr420P:
3575acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV420P:
3585acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422P:
3595acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422P:
3605acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422R:
3615acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422R:
3625acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YCbCr422RA:
3635acd3514SThierry Reding 	case WIN_COLOR_DEPTH_YUV422RA:
3645acd3514SThierry Reding 		if (planar)
3655acd3514SThierry Reding 			*planar = true;
3665acd3514SThierry Reding 
3675acd3514SThierry Reding 		return true;
3685acd3514SThierry Reding 	}
3695acd3514SThierry Reding 
3705acd3514SThierry Reding 	if (planar)
3715acd3514SThierry Reding 		*planar = false;
3725acd3514SThierry Reding 
3735acd3514SThierry Reding 	return false;
3745acd3514SThierry Reding }
375ebae8d07SThierry Reding 
376ebae8d07SThierry Reding static bool __drm_format_has_alpha(u32 format)
377ebae8d07SThierry Reding {
378ebae8d07SThierry Reding 	switch (format) {
379ebae8d07SThierry Reding 	case DRM_FORMAT_ARGB1555:
380ebae8d07SThierry Reding 	case DRM_FORMAT_RGBA5551:
381ebae8d07SThierry Reding 	case DRM_FORMAT_ABGR8888:
382ebae8d07SThierry Reding 	case DRM_FORMAT_ARGB8888:
383ebae8d07SThierry Reding 		return true;
384ebae8d07SThierry Reding 	}
385ebae8d07SThierry Reding 
386ebae8d07SThierry Reding 	return false;
387ebae8d07SThierry Reding }
388ebae8d07SThierry Reding 
3893dae08bcSDmitry Osipenko static int tegra_plane_format_get_alpha(unsigned int opaque,
3903dae08bcSDmitry Osipenko 					unsigned int *alpha)
391ebae8d07SThierry Reding {
3925467a8b8SThierry Reding 	if (tegra_plane_format_is_yuv(opaque, NULL)) {
3935467a8b8SThierry Reding 		*alpha = opaque;
3945467a8b8SThierry Reding 		return 0;
3955467a8b8SThierry Reding 	}
3965467a8b8SThierry Reding 
397ebae8d07SThierry Reding 	switch (opaque) {
398ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_B5G5R5X1:
399ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_B5G5R5A1;
400ebae8d07SThierry Reding 		return 0;
401ebae8d07SThierry Reding 
402ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_X1B5G5R5:
403ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_A1B5G5R5;
404ebae8d07SThierry Reding 		return 0;
405ebae8d07SThierry Reding 
406ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_R8G8B8X8:
407ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_R8G8B8A8;
408ebae8d07SThierry Reding 		return 0;
409ebae8d07SThierry Reding 
410ebae8d07SThierry Reding 	case WIN_COLOR_DEPTH_B8G8R8X8:
411ebae8d07SThierry Reding 		*alpha = WIN_COLOR_DEPTH_B8G8R8A8;
412ebae8d07SThierry Reding 		return 0;
4138a927d64SThierry Reding 
4148a927d64SThierry Reding 	case WIN_COLOR_DEPTH_B5G6R5:
4158a927d64SThierry Reding 		*alpha = opaque;
4168a927d64SThierry Reding 		return 0;
417ebae8d07SThierry Reding 	}
418ebae8d07SThierry Reding 
419ebae8d07SThierry Reding 	return -EINVAL;
420ebae8d07SThierry Reding }
421ebae8d07SThierry Reding 
4223dae08bcSDmitry Osipenko /*
4233dae08bcSDmitry Osipenko  * This is applicable to Tegra20 and Tegra30 only where the opaque formats can
4243dae08bcSDmitry Osipenko  * be emulated using the alpha formats and alpha blending disabled.
4253dae08bcSDmitry Osipenko  */
4263dae08bcSDmitry Osipenko static int tegra_plane_setup_opacity(struct tegra_plane *tegra,
4273dae08bcSDmitry Osipenko 				     struct tegra_plane_state *state)
4283dae08bcSDmitry Osipenko {
4293dae08bcSDmitry Osipenko 	unsigned int format;
4303dae08bcSDmitry Osipenko 	int err;
4313dae08bcSDmitry Osipenko 
4323dae08bcSDmitry Osipenko 	switch (state->format) {
4333dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_B5G5R5A1:
4343dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_A1B5G5R5:
4353dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_R8G8B8A8:
4363dae08bcSDmitry Osipenko 	case WIN_COLOR_DEPTH_B8G8R8A8:
4373dae08bcSDmitry Osipenko 		state->opaque = false;
4383dae08bcSDmitry Osipenko 		break;
4393dae08bcSDmitry Osipenko 
4403dae08bcSDmitry Osipenko 	default:
4413dae08bcSDmitry Osipenko 		err = tegra_plane_format_get_alpha(state->format, &format);
4423dae08bcSDmitry Osipenko 		if (err < 0)
4433dae08bcSDmitry Osipenko 			return err;
4443dae08bcSDmitry Osipenko 
4453dae08bcSDmitry Osipenko 		state->format = format;
4463dae08bcSDmitry Osipenko 		state->opaque = true;
4473dae08bcSDmitry Osipenko 		break;
4483dae08bcSDmitry Osipenko 	}
4493dae08bcSDmitry Osipenko 
4503dae08bcSDmitry Osipenko 	return 0;
4513dae08bcSDmitry Osipenko }
4523dae08bcSDmitry Osipenko 
4533dae08bcSDmitry Osipenko static int tegra_plane_check_transparency(struct tegra_plane *tegra,
4543dae08bcSDmitry Osipenko 					  struct tegra_plane_state *state)
4553dae08bcSDmitry Osipenko {
4563dae08bcSDmitry Osipenko 	struct drm_plane_state *old, *plane_state;
4573dae08bcSDmitry Osipenko 	struct drm_plane *plane;
4583dae08bcSDmitry Osipenko 
4593dae08bcSDmitry Osipenko 	old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
4603dae08bcSDmitry Osipenko 
4613dae08bcSDmitry Osipenko 	/* check if zpos / transparency changed */
4623dae08bcSDmitry Osipenko 	if (old->normalized_zpos == state->base.normalized_zpos &&
4633dae08bcSDmitry Osipenko 	    to_tegra_plane_state(old)->opaque == state->opaque)
4643dae08bcSDmitry Osipenko 		return 0;
4653dae08bcSDmitry Osipenko 
4663dae08bcSDmitry Osipenko 	/* include all sibling planes into this commit */
4673dae08bcSDmitry Osipenko 	drm_for_each_plane(plane, tegra->base.dev) {
4683dae08bcSDmitry Osipenko 		struct tegra_plane *p = to_tegra_plane(plane);
4693dae08bcSDmitry Osipenko 
4703dae08bcSDmitry Osipenko 		/* skip this plane and planes on different CRTCs */
4713dae08bcSDmitry Osipenko 		if (p == tegra || p->dc != tegra->dc)
4723dae08bcSDmitry Osipenko 			continue;
4733dae08bcSDmitry Osipenko 
4743dae08bcSDmitry Osipenko 		plane_state = drm_atomic_get_plane_state(state->base.state,
4753dae08bcSDmitry Osipenko 							 plane);
4763dae08bcSDmitry Osipenko 		if (IS_ERR(plane_state))
4773dae08bcSDmitry Osipenko 			return PTR_ERR(plane_state);
4783dae08bcSDmitry Osipenko 	}
4793dae08bcSDmitry Osipenko 
4803dae08bcSDmitry Osipenko 	return 1;
4813dae08bcSDmitry Osipenko }
4823dae08bcSDmitry Osipenko 
4835e2e86f1SDmitry Osipenko static unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
484ebae8d07SThierry Reding 						  struct tegra_plane *other)
485ebae8d07SThierry Reding {
486ebae8d07SThierry Reding 	unsigned int index = 0, i;
487ebae8d07SThierry Reding 
488ebae8d07SThierry Reding 	WARN_ON(plane == other);
489ebae8d07SThierry Reding 
490ebae8d07SThierry Reding 	for (i = 0; i < 3; i++) {
491ebae8d07SThierry Reding 		if (i == plane->index)
492ebae8d07SThierry Reding 			continue;
493ebae8d07SThierry Reding 
494ebae8d07SThierry Reding 		if (i == other->index)
495ebae8d07SThierry Reding 			break;
496ebae8d07SThierry Reding 
497ebae8d07SThierry Reding 		index++;
498ebae8d07SThierry Reding 	}
499ebae8d07SThierry Reding 
500ebae8d07SThierry Reding 	return index;
501ebae8d07SThierry Reding }
502ebae8d07SThierry Reding 
5033dae08bcSDmitry Osipenko static void tegra_plane_update_transparency(struct tegra_plane *tegra,
504ebae8d07SThierry Reding 					    struct tegra_plane_state *state)
505ebae8d07SThierry Reding {
5063dae08bcSDmitry Osipenko 	struct drm_plane_state *new;
507ebae8d07SThierry Reding 	struct drm_plane *plane;
508ebae8d07SThierry Reding 	unsigned int i;
509ebae8d07SThierry Reding 
5103dae08bcSDmitry Osipenko 	for_each_new_plane_in_state(state->base.state, plane, new, i) {
511ebae8d07SThierry Reding 		struct tegra_plane *p = to_tegra_plane(plane);
512ebae8d07SThierry Reding 		unsigned index;
513ebae8d07SThierry Reding 
514ebae8d07SThierry Reding 		/* skip this plane and planes on different CRTCs */
5153dae08bcSDmitry Osipenko 		if (p == tegra || p->dc != tegra->dc)
516ebae8d07SThierry Reding 			continue;
517ebae8d07SThierry Reding 
518ebae8d07SThierry Reding 		index = tegra_plane_get_overlap_index(tegra, p);
519ebae8d07SThierry Reding 
5203dae08bcSDmitry Osipenko 		if (new->fb && __drm_format_has_alpha(new->fb->format->format))
5213dae08bcSDmitry Osipenko 			state->blending[index].alpha = true;
5223dae08bcSDmitry Osipenko 		else
5233dae08bcSDmitry Osipenko 			state->blending[index].alpha = false;
5243dae08bcSDmitry Osipenko 
5253dae08bcSDmitry Osipenko 		if (new->normalized_zpos > state->base.normalized_zpos)
5263dae08bcSDmitry Osipenko 			state->blending[index].top = true;
5273dae08bcSDmitry Osipenko 		else
5283dae08bcSDmitry Osipenko 			state->blending[index].top = false;
52948519232SDmitry Osipenko 
530ebae8d07SThierry Reding 		/*
5313dae08bcSDmitry Osipenko 		 * Missing framebuffer means that plane is disabled, in this
5323dae08bcSDmitry Osipenko 		 * case mark B / C window as top to be able to differentiate
5333dae08bcSDmitry Osipenko 		 * windows indices order in regards to zPos for the middle
5343dae08bcSDmitry Osipenko 		 * window X / Y registers programming.
535ebae8d07SThierry Reding 		 */
5363dae08bcSDmitry Osipenko 		if (!new->fb)
5373dae08bcSDmitry Osipenko 			state->blending[index].top = (index == 1);
538ebae8d07SThierry Reding 	}
539ebae8d07SThierry Reding }
540ebae8d07SThierry Reding 
5413dae08bcSDmitry Osipenko static int tegra_plane_setup_transparency(struct tegra_plane *tegra,
5423dae08bcSDmitry Osipenko 					  struct tegra_plane_state *state)
5433dae08bcSDmitry Osipenko {
5443dae08bcSDmitry Osipenko 	struct tegra_plane_state *tegra_state;
5453dae08bcSDmitry Osipenko 	struct drm_plane_state *new;
5463dae08bcSDmitry Osipenko 	struct drm_plane *plane;
5473dae08bcSDmitry Osipenko 	int err;
548ebae8d07SThierry Reding 
549ebae8d07SThierry Reding 	/*
5503dae08bcSDmitry Osipenko 	 * If planes zpos / transparency changed, sibling planes blending
5513dae08bcSDmitry Osipenko 	 * state may require adjustment and in this case they will be included
5523dae08bcSDmitry Osipenko 	 * into this atom commit, otherwise blending state is unchanged.
553ebae8d07SThierry Reding 	 */
5543dae08bcSDmitry Osipenko 	err = tegra_plane_check_transparency(tegra, state);
5553dae08bcSDmitry Osipenko 	if (err <= 0)
5563dae08bcSDmitry Osipenko 		return err;
5573dae08bcSDmitry Osipenko 
5583dae08bcSDmitry Osipenko 	/*
5593dae08bcSDmitry Osipenko 	 * All planes are now in the atomic state, walk them up and update
5603dae08bcSDmitry Osipenko 	 * transparency state for each plane.
5613dae08bcSDmitry Osipenko 	 */
5623dae08bcSDmitry Osipenko 	drm_for_each_plane(plane, tegra->base.dev) {
5633dae08bcSDmitry Osipenko 		struct tegra_plane *p = to_tegra_plane(plane);
5643dae08bcSDmitry Osipenko 
5653dae08bcSDmitry Osipenko 		/* skip planes on different CRTCs */
5663dae08bcSDmitry Osipenko 		if (p->dc != tegra->dc)
5673dae08bcSDmitry Osipenko 			continue;
5683dae08bcSDmitry Osipenko 
5693dae08bcSDmitry Osipenko 		new = drm_atomic_get_new_plane_state(state->base.state, plane);
5703dae08bcSDmitry Osipenko 		tegra_state = to_tegra_plane_state(new);
5713dae08bcSDmitry Osipenko 
5723dae08bcSDmitry Osipenko 		/*
5733dae08bcSDmitry Osipenko 		 * There is no need to update blending state for the disabled
5743dae08bcSDmitry Osipenko 		 * plane.
5753dae08bcSDmitry Osipenko 		 */
5763dae08bcSDmitry Osipenko 		if (new->fb)
5773dae08bcSDmitry Osipenko 			tegra_plane_update_transparency(p, tegra_state);
578ebae8d07SThierry Reding 	}
5793dae08bcSDmitry Osipenko 
5803dae08bcSDmitry Osipenko 	return 0;
5813dae08bcSDmitry Osipenko }
5823dae08bcSDmitry Osipenko 
5833dae08bcSDmitry Osipenko int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
5843dae08bcSDmitry Osipenko 				   struct tegra_plane_state *state)
5853dae08bcSDmitry Osipenko {
5863dae08bcSDmitry Osipenko 	int err;
5873dae08bcSDmitry Osipenko 
5883dae08bcSDmitry Osipenko 	err = tegra_plane_setup_opacity(tegra, state);
5893dae08bcSDmitry Osipenko 	if (err < 0)
5903dae08bcSDmitry Osipenko 		return err;
5913dae08bcSDmitry Osipenko 
5923dae08bcSDmitry Osipenko 	err = tegra_plane_setup_transparency(tegra, state);
5933dae08bcSDmitry Osipenko 	if (err < 0)
5943dae08bcSDmitry Osipenko 		return err;
5953dae08bcSDmitry Osipenko 
5963dae08bcSDmitry Osipenko 	return 0;
597ebae8d07SThierry Reding }
598