18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2017 NVIDIA CORPORATION.  All rights reserved.
48c2ecf20Sopenharmony_ci */
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci#include <linux/iommu.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <drm/drm_atomic.h>
98c2ecf20Sopenharmony_ci#include <drm/drm_atomic_helper.h>
108c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
118c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
128c2ecf20Sopenharmony_ci#include <drm/drm_plane_helper.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "dc.h"
158c2ecf20Sopenharmony_ci#include "plane.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cistatic void tegra_plane_destroy(struct drm_plane *plane)
188c2ecf20Sopenharmony_ci{
198c2ecf20Sopenharmony_ci	struct tegra_plane *p = to_tegra_plane(plane);
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci	drm_plane_cleanup(plane);
228c2ecf20Sopenharmony_ci	kfree(p);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic void tegra_plane_reset(struct drm_plane *plane)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct tegra_plane *p = to_tegra_plane(plane);
288c2ecf20Sopenharmony_ci	struct tegra_plane_state *state;
298c2ecf20Sopenharmony_ci	unsigned int i;
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci	if (plane->state)
328c2ecf20Sopenharmony_ci		__drm_atomic_helper_plane_destroy_state(plane->state);
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	kfree(plane->state);
358c2ecf20Sopenharmony_ci	plane->state = NULL;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	state = kzalloc(sizeof(*state), GFP_KERNEL);
388c2ecf20Sopenharmony_ci	if (state) {
398c2ecf20Sopenharmony_ci		plane->state = &state->base;
408c2ecf20Sopenharmony_ci		plane->state->plane = plane;
418c2ecf20Sopenharmony_ci		plane->state->zpos = p->index;
428c2ecf20Sopenharmony_ci		plane->state->normalized_zpos = p->index;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci		for (i = 0; i < 3; i++)
458c2ecf20Sopenharmony_ci			state->iova[i] = DMA_MAPPING_ERROR;
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_cistatic struct drm_plane_state *
508c2ecf20Sopenharmony_citegra_plane_atomic_duplicate_state(struct drm_plane *plane)
518c2ecf20Sopenharmony_ci{
528c2ecf20Sopenharmony_ci	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
538c2ecf20Sopenharmony_ci	struct tegra_plane_state *copy;
548c2ecf20Sopenharmony_ci	unsigned int i;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
578c2ecf20Sopenharmony_ci	if (!copy)
588c2ecf20Sopenharmony_ci		return NULL;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	__drm_atomic_helper_plane_duplicate_state(plane, &copy->base);
618c2ecf20Sopenharmony_ci	copy->tiling = state->tiling;
628c2ecf20Sopenharmony_ci	copy->format = state->format;
638c2ecf20Sopenharmony_ci	copy->swap = state->swap;
648c2ecf20Sopenharmony_ci	copy->reflect_x = state->reflect_x;
658c2ecf20Sopenharmony_ci	copy->reflect_y = state->reflect_y;
668c2ecf20Sopenharmony_ci	copy->opaque = state->opaque;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++)
698c2ecf20Sopenharmony_ci		copy->blending[i] = state->blending[i];
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
728c2ecf20Sopenharmony_ci		copy->iova[i] = DMA_MAPPING_ERROR;
738c2ecf20Sopenharmony_ci		copy->sgt[i] = NULL;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	return &copy->base;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void tegra_plane_atomic_destroy_state(struct drm_plane *plane,
808c2ecf20Sopenharmony_ci					     struct drm_plane_state *state)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	__drm_atomic_helper_plane_destroy_state(state);
838c2ecf20Sopenharmony_ci	kfree(state);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_cistatic bool tegra_plane_format_mod_supported(struct drm_plane *plane,
878c2ecf20Sopenharmony_ci					     uint32_t format,
888c2ecf20Sopenharmony_ci					     uint64_t modifier)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	const struct drm_format_info *info = drm_format_info(format);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	if (modifier == DRM_FORMAT_MOD_LINEAR)
938c2ecf20Sopenharmony_ci		return true;
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci	if (info->num_planes == 1)
968c2ecf20Sopenharmony_ci		return true;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return false;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciconst struct drm_plane_funcs tegra_plane_funcs = {
1028c2ecf20Sopenharmony_ci	.update_plane = drm_atomic_helper_update_plane,
1038c2ecf20Sopenharmony_ci	.disable_plane = drm_atomic_helper_disable_plane,
1048c2ecf20Sopenharmony_ci	.destroy = tegra_plane_destroy,
1058c2ecf20Sopenharmony_ci	.reset = tegra_plane_reset,
1068c2ecf20Sopenharmony_ci	.atomic_duplicate_state = tegra_plane_atomic_duplicate_state,
1078c2ecf20Sopenharmony_ci	.atomic_destroy_state = tegra_plane_atomic_destroy_state,
1088c2ecf20Sopenharmony_ci	.format_mod_supported = tegra_plane_format_mod_supported,
1098c2ecf20Sopenharmony_ci};
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_cistatic int tegra_dc_pin(struct tegra_dc *dc, struct tegra_plane_state *state)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	struct iommu_domain *domain = iommu_get_domain_for_dev(dc->dev);
1148c2ecf20Sopenharmony_ci	unsigned int i;
1158c2ecf20Sopenharmony_ci	int err;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	for (i = 0; i < state->base.fb->format->num_planes; i++) {
1188c2ecf20Sopenharmony_ci		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
1198c2ecf20Sopenharmony_ci		dma_addr_t phys_addr, *phys;
1208c2ecf20Sopenharmony_ci		struct sg_table *sgt;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		if (!domain || dc->client.group)
1238c2ecf20Sopenharmony_ci			phys = &phys_addr;
1248c2ecf20Sopenharmony_ci		else
1258c2ecf20Sopenharmony_ci			phys = NULL;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		sgt = host1x_bo_pin(dc->dev, &bo->base, phys);
1288c2ecf20Sopenharmony_ci		if (IS_ERR(sgt)) {
1298c2ecf20Sopenharmony_ci			err = PTR_ERR(sgt);
1308c2ecf20Sopenharmony_ci			goto unpin;
1318c2ecf20Sopenharmony_ci		}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		if (sgt) {
1348c2ecf20Sopenharmony_ci			err = dma_map_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
1358c2ecf20Sopenharmony_ci			if (err)
1368c2ecf20Sopenharmony_ci				goto unpin;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci			/*
1398c2ecf20Sopenharmony_ci			 * The display controller needs contiguous memory, so
1408c2ecf20Sopenharmony_ci			 * fail if the buffer is discontiguous and we fail to
1418c2ecf20Sopenharmony_ci			 * map its SG table to a single contiguous chunk of
1428c2ecf20Sopenharmony_ci			 * I/O virtual memory.
1438c2ecf20Sopenharmony_ci			 */
1448c2ecf20Sopenharmony_ci			if (sgt->nents > 1) {
1458c2ecf20Sopenharmony_ci				err = -EINVAL;
1468c2ecf20Sopenharmony_ci				goto unpin;
1478c2ecf20Sopenharmony_ci			}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci			state->iova[i] = sg_dma_address(sgt->sgl);
1508c2ecf20Sopenharmony_ci			state->sgt[i] = sgt;
1518c2ecf20Sopenharmony_ci		} else {
1528c2ecf20Sopenharmony_ci			state->iova[i] = phys_addr;
1538c2ecf20Sopenharmony_ci		}
1548c2ecf20Sopenharmony_ci	}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ciunpin:
1598c2ecf20Sopenharmony_ci	dev_err(dc->dev, "failed to map plane %u: %d\n", i, err);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	while (i--) {
1628c2ecf20Sopenharmony_ci		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
1638c2ecf20Sopenharmony_ci		struct sg_table *sgt = state->sgt[i];
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci		if (sgt)
1668c2ecf20Sopenharmony_ci			dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci		host1x_bo_unpin(dc->dev, &bo->base, sgt);
1698c2ecf20Sopenharmony_ci		state->iova[i] = DMA_MAPPING_ERROR;
1708c2ecf20Sopenharmony_ci		state->sgt[i] = NULL;
1718c2ecf20Sopenharmony_ci	}
1728c2ecf20Sopenharmony_ci
1738c2ecf20Sopenharmony_ci	return err;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void tegra_dc_unpin(struct tegra_dc *dc, struct tegra_plane_state *state)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	unsigned int i;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	for (i = 0; i < state->base.fb->format->num_planes; i++) {
1818c2ecf20Sopenharmony_ci		struct tegra_bo *bo = tegra_fb_get_plane(state->base.fb, i);
1828c2ecf20Sopenharmony_ci		struct sg_table *sgt = state->sgt[i];
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci		if (sgt)
1858c2ecf20Sopenharmony_ci			dma_unmap_sgtable(dc->dev, sgt, DMA_TO_DEVICE, 0);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci		host1x_bo_unpin(dc->dev, &bo->base, sgt);
1888c2ecf20Sopenharmony_ci		state->iova[i] = DMA_MAPPING_ERROR;
1898c2ecf20Sopenharmony_ci		state->sgt[i] = NULL;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci}
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ciint tegra_plane_prepare_fb(struct drm_plane *plane,
1948c2ecf20Sopenharmony_ci			   struct drm_plane_state *state)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(state->crtc);
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	if (!state->fb)
1998c2ecf20Sopenharmony_ci		return 0;
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci	drm_gem_fb_prepare_fb(plane, state);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	return tegra_dc_pin(dc, to_tegra_plane_state(state));
2048c2ecf20Sopenharmony_ci}
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_civoid tegra_plane_cleanup_fb(struct drm_plane *plane,
2078c2ecf20Sopenharmony_ci			    struct drm_plane_state *state)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(state->crtc);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if (dc)
2128c2ecf20Sopenharmony_ci		tegra_dc_unpin(dc, to_tegra_plane_state(state));
2138c2ecf20Sopenharmony_ci}
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ciint tegra_plane_state_add(struct tegra_plane *plane,
2168c2ecf20Sopenharmony_ci			  struct drm_plane_state *state)
2178c2ecf20Sopenharmony_ci{
2188c2ecf20Sopenharmony_ci	struct drm_crtc_state *crtc_state;
2198c2ecf20Sopenharmony_ci	struct tegra_dc_state *tegra;
2208c2ecf20Sopenharmony_ci	int err;
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci	/* Propagate errors from allocation or locking failures. */
2238c2ecf20Sopenharmony_ci	crtc_state = drm_atomic_get_crtc_state(state->state, state->crtc);
2248c2ecf20Sopenharmony_ci	if (IS_ERR(crtc_state))
2258c2ecf20Sopenharmony_ci		return PTR_ERR(crtc_state);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* Check plane state for visibility and calculate clipping bounds */
2288c2ecf20Sopenharmony_ci	err = drm_atomic_helper_check_plane_state(state, crtc_state,
2298c2ecf20Sopenharmony_ci						  0, INT_MAX, true, true);
2308c2ecf20Sopenharmony_ci	if (err < 0)
2318c2ecf20Sopenharmony_ci		return err;
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	tegra = to_dc_state(crtc_state);
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	tegra->planes |= WIN_A_ACT_REQ << plane->index;
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	return 0;
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ciint tegra_plane_format(u32 fourcc, u32 *format, u32 *swap)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	/* assume no swapping of fetched data */
2438c2ecf20Sopenharmony_ci	if (swap)
2448c2ecf20Sopenharmony_ci		*swap = BYTE_SWAP_NOSWAP;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	switch (fourcc) {
2478c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB4444:
2488c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_B4G4R4A4;
2498c2ecf20Sopenharmony_ci		break;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB1555:
2528c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_B5G5R5A1;
2538c2ecf20Sopenharmony_ci		break;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGB565:
2568c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_B5G6R5;
2578c2ecf20Sopenharmony_ci		break;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGBA5551:
2608c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_A1B5G5R5;
2618c2ecf20Sopenharmony_ci		break;
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
2648c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_B8G8R8A8;
2658c2ecf20Sopenharmony_ci		break;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
2688c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_R8G8B8A8;
2698c2ecf20Sopenharmony_ci		break;
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	case DRM_FORMAT_ABGR4444:
2728c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_R4G4B4A4;
2738c2ecf20Sopenharmony_ci		break;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci	case DRM_FORMAT_ABGR1555:
2768c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_R5G5B5A;
2778c2ecf20Sopenharmony_ci		break;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	case DRM_FORMAT_BGRA5551:
2808c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_AR5G5B5;
2818c2ecf20Sopenharmony_ci		break;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
2848c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_B5G5R5X1;
2858c2ecf20Sopenharmony_ci		break;
2868c2ecf20Sopenharmony_ci
2878c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGBX5551:
2888c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_X1B5G5R5;
2898c2ecf20Sopenharmony_ci		break;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	case DRM_FORMAT_XBGR1555:
2928c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_R5G5B5X1;
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci
2958c2ecf20Sopenharmony_ci	case DRM_FORMAT_BGRX5551:
2968c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_X1R5G5B5;
2978c2ecf20Sopenharmony_ci		break;
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	case DRM_FORMAT_BGR565:
3008c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_R5G6B5;
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci	case DRM_FORMAT_BGRA8888:
3048c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_A8R8G8B8;
3058c2ecf20Sopenharmony_ci		break;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGBA8888:
3088c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_A8B8G8R8;
3098c2ecf20Sopenharmony_ci		break;
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
3128c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_B8G8R8X8;
3138c2ecf20Sopenharmony_ci		break;
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
3168c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_R8G8B8X8;
3178c2ecf20Sopenharmony_ci		break;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	case DRM_FORMAT_UYVY:
3208c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_YCbCr422;
3218c2ecf20Sopenharmony_ci		break;
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	case DRM_FORMAT_YUYV:
3248c2ecf20Sopenharmony_ci		if (!swap)
3258c2ecf20Sopenharmony_ci			return -EINVAL;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_YCbCr422;
3288c2ecf20Sopenharmony_ci		*swap = BYTE_SWAP_SWAP2;
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci
3318c2ecf20Sopenharmony_ci	case DRM_FORMAT_YUV420:
3328c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_YCbCr420P;
3338c2ecf20Sopenharmony_ci		break;
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	case DRM_FORMAT_YUV422:
3368c2ecf20Sopenharmony_ci		*format = WIN_COLOR_DEPTH_YCbCr422P;
3378c2ecf20Sopenharmony_ci		break;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	default:
3408c2ecf20Sopenharmony_ci		return -EINVAL;
3418c2ecf20Sopenharmony_ci	}
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cibool tegra_plane_format_is_yuv(unsigned int format, bool *planar)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	switch (format) {
3498c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YCbCr422:
3508c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YUV422:
3518c2ecf20Sopenharmony_ci		if (planar)
3528c2ecf20Sopenharmony_ci			*planar = false;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci		return true;
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YCbCr420P:
3578c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YUV420P:
3588c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YCbCr422P:
3598c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YUV422P:
3608c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YCbCr422R:
3618c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YUV422R:
3628c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YCbCr422RA:
3638c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_YUV422RA:
3648c2ecf20Sopenharmony_ci		if (planar)
3658c2ecf20Sopenharmony_ci			*planar = true;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci		return true;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	if (planar)
3718c2ecf20Sopenharmony_ci		*planar = false;
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_ci	return false;
3748c2ecf20Sopenharmony_ci}
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_cistatic bool __drm_format_has_alpha(u32 format)
3778c2ecf20Sopenharmony_ci{
3788c2ecf20Sopenharmony_ci	switch (format) {
3798c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB1555:
3808c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGBA5551:
3818c2ecf20Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
3828c2ecf20Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
3838c2ecf20Sopenharmony_ci		return true;
3848c2ecf20Sopenharmony_ci	}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	return false;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic int tegra_plane_format_get_alpha(unsigned int opaque,
3908c2ecf20Sopenharmony_ci					unsigned int *alpha)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	if (tegra_plane_format_is_yuv(opaque, NULL)) {
3938c2ecf20Sopenharmony_ci		*alpha = opaque;
3948c2ecf20Sopenharmony_ci		return 0;
3958c2ecf20Sopenharmony_ci	}
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	switch (opaque) {
3988c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_B5G5R5X1:
3998c2ecf20Sopenharmony_ci		*alpha = WIN_COLOR_DEPTH_B5G5R5A1;
4008c2ecf20Sopenharmony_ci		return 0;
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_X1B5G5R5:
4038c2ecf20Sopenharmony_ci		*alpha = WIN_COLOR_DEPTH_A1B5G5R5;
4048c2ecf20Sopenharmony_ci		return 0;
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_R8G8B8X8:
4078c2ecf20Sopenharmony_ci		*alpha = WIN_COLOR_DEPTH_R8G8B8A8;
4088c2ecf20Sopenharmony_ci		return 0;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_B8G8R8X8:
4118c2ecf20Sopenharmony_ci		*alpha = WIN_COLOR_DEPTH_B8G8R8A8;
4128c2ecf20Sopenharmony_ci		return 0;
4138c2ecf20Sopenharmony_ci
4148c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_B5G6R5:
4158c2ecf20Sopenharmony_ci		*alpha = opaque;
4168c2ecf20Sopenharmony_ci		return 0;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	return -EINVAL;
4208c2ecf20Sopenharmony_ci}
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci/*
4238c2ecf20Sopenharmony_ci * This is applicable to Tegra20 and Tegra30 only where the opaque formats can
4248c2ecf20Sopenharmony_ci * be emulated using the alpha formats and alpha blending disabled.
4258c2ecf20Sopenharmony_ci */
4268c2ecf20Sopenharmony_cistatic int tegra_plane_setup_opacity(struct tegra_plane *tegra,
4278c2ecf20Sopenharmony_ci				     struct tegra_plane_state *state)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	unsigned int format;
4308c2ecf20Sopenharmony_ci	int err;
4318c2ecf20Sopenharmony_ci
4328c2ecf20Sopenharmony_ci	switch (state->format) {
4338c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_B5G5R5A1:
4348c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_A1B5G5R5:
4358c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_R8G8B8A8:
4368c2ecf20Sopenharmony_ci	case WIN_COLOR_DEPTH_B8G8R8A8:
4378c2ecf20Sopenharmony_ci		state->opaque = false;
4388c2ecf20Sopenharmony_ci		break;
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	default:
4418c2ecf20Sopenharmony_ci		err = tegra_plane_format_get_alpha(state->format, &format);
4428c2ecf20Sopenharmony_ci		if (err < 0)
4438c2ecf20Sopenharmony_ci			return err;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci		state->format = format;
4468c2ecf20Sopenharmony_ci		state->opaque = true;
4478c2ecf20Sopenharmony_ci		break;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	return 0;
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistatic int tegra_plane_check_transparency(struct tegra_plane *tegra,
4548c2ecf20Sopenharmony_ci					  struct tegra_plane_state *state)
4558c2ecf20Sopenharmony_ci{
4568c2ecf20Sopenharmony_ci	struct drm_plane_state *old, *plane_state;
4578c2ecf20Sopenharmony_ci	struct drm_plane *plane;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	old = drm_atomic_get_old_plane_state(state->base.state, &tegra->base);
4608c2ecf20Sopenharmony_ci
4618c2ecf20Sopenharmony_ci	/* check if zpos / transparency changed */
4628c2ecf20Sopenharmony_ci	if (old->normalized_zpos == state->base.normalized_zpos &&
4638c2ecf20Sopenharmony_ci	    to_tegra_plane_state(old)->opaque == state->opaque)
4648c2ecf20Sopenharmony_ci		return 0;
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* include all sibling planes into this commit */
4678c2ecf20Sopenharmony_ci	drm_for_each_plane(plane, tegra->base.dev) {
4688c2ecf20Sopenharmony_ci		struct tegra_plane *p = to_tegra_plane(plane);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci		/* skip this plane and planes on different CRTCs */
4718c2ecf20Sopenharmony_ci		if (p == tegra || p->dc != tegra->dc)
4728c2ecf20Sopenharmony_ci			continue;
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci		plane_state = drm_atomic_get_plane_state(state->base.state,
4758c2ecf20Sopenharmony_ci							 plane);
4768c2ecf20Sopenharmony_ci		if (IS_ERR(plane_state))
4778c2ecf20Sopenharmony_ci			return PTR_ERR(plane_state);
4788c2ecf20Sopenharmony_ci	}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	return 1;
4818c2ecf20Sopenharmony_ci}
4828c2ecf20Sopenharmony_ci
4838c2ecf20Sopenharmony_cistatic unsigned int tegra_plane_get_overlap_index(struct tegra_plane *plane,
4848c2ecf20Sopenharmony_ci						  struct tegra_plane *other)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	unsigned int index = 0, i;
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	WARN_ON(plane == other);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	for (i = 0; i < 3; i++) {
4918c2ecf20Sopenharmony_ci		if (i == plane->index)
4928c2ecf20Sopenharmony_ci			continue;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_ci		if (i == other->index)
4958c2ecf20Sopenharmony_ci			break;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci		index++;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	return index;
5018c2ecf20Sopenharmony_ci}
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cistatic void tegra_plane_update_transparency(struct tegra_plane *tegra,
5048c2ecf20Sopenharmony_ci					    struct tegra_plane_state *state)
5058c2ecf20Sopenharmony_ci{
5068c2ecf20Sopenharmony_ci	struct drm_plane_state *new;
5078c2ecf20Sopenharmony_ci	struct drm_plane *plane;
5088c2ecf20Sopenharmony_ci	unsigned int i;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	for_each_new_plane_in_state(state->base.state, plane, new, i) {
5118c2ecf20Sopenharmony_ci		struct tegra_plane *p = to_tegra_plane(plane);
5128c2ecf20Sopenharmony_ci		unsigned index;
5138c2ecf20Sopenharmony_ci
5148c2ecf20Sopenharmony_ci		/* skip this plane and planes on different CRTCs */
5158c2ecf20Sopenharmony_ci		if (p == tegra || p->dc != tegra->dc)
5168c2ecf20Sopenharmony_ci			continue;
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci		index = tegra_plane_get_overlap_index(tegra, p);
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci		if (new->fb && __drm_format_has_alpha(new->fb->format->format))
5218c2ecf20Sopenharmony_ci			state->blending[index].alpha = true;
5228c2ecf20Sopenharmony_ci		else
5238c2ecf20Sopenharmony_ci			state->blending[index].alpha = false;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ci		if (new->normalized_zpos > state->base.normalized_zpos)
5268c2ecf20Sopenharmony_ci			state->blending[index].top = true;
5278c2ecf20Sopenharmony_ci		else
5288c2ecf20Sopenharmony_ci			state->blending[index].top = false;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci		/*
5318c2ecf20Sopenharmony_ci		 * Missing framebuffer means that plane is disabled, in this
5328c2ecf20Sopenharmony_ci		 * case mark B / C window as top to be able to differentiate
5338c2ecf20Sopenharmony_ci		 * windows indices order in regards to zPos for the middle
5348c2ecf20Sopenharmony_ci		 * window X / Y registers programming.
5358c2ecf20Sopenharmony_ci		 */
5368c2ecf20Sopenharmony_ci		if (!new->fb)
5378c2ecf20Sopenharmony_ci			state->blending[index].top = (index == 1);
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic int tegra_plane_setup_transparency(struct tegra_plane *tegra,
5428c2ecf20Sopenharmony_ci					  struct tegra_plane_state *state)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	struct tegra_plane_state *tegra_state;
5458c2ecf20Sopenharmony_ci	struct drm_plane_state *new;
5468c2ecf20Sopenharmony_ci	struct drm_plane *plane;
5478c2ecf20Sopenharmony_ci	int err;
5488c2ecf20Sopenharmony_ci
5498c2ecf20Sopenharmony_ci	/*
5508c2ecf20Sopenharmony_ci	 * If planes zpos / transparency changed, sibling planes blending
5518c2ecf20Sopenharmony_ci	 * state may require adjustment and in this case they will be included
5528c2ecf20Sopenharmony_ci	 * into this atom commit, otherwise blending state is unchanged.
5538c2ecf20Sopenharmony_ci	 */
5548c2ecf20Sopenharmony_ci	err = tegra_plane_check_transparency(tegra, state);
5558c2ecf20Sopenharmony_ci	if (err <= 0)
5568c2ecf20Sopenharmony_ci		return err;
5578c2ecf20Sopenharmony_ci
5588c2ecf20Sopenharmony_ci	/*
5598c2ecf20Sopenharmony_ci	 * All planes are now in the atomic state, walk them up and update
5608c2ecf20Sopenharmony_ci	 * transparency state for each plane.
5618c2ecf20Sopenharmony_ci	 */
5628c2ecf20Sopenharmony_ci	drm_for_each_plane(plane, tegra->base.dev) {
5638c2ecf20Sopenharmony_ci		struct tegra_plane *p = to_tegra_plane(plane);
5648c2ecf20Sopenharmony_ci
5658c2ecf20Sopenharmony_ci		/* skip planes on different CRTCs */
5668c2ecf20Sopenharmony_ci		if (p->dc != tegra->dc)
5678c2ecf20Sopenharmony_ci			continue;
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci		new = drm_atomic_get_new_plane_state(state->base.state, plane);
5708c2ecf20Sopenharmony_ci		tegra_state = to_tegra_plane_state(new);
5718c2ecf20Sopenharmony_ci
5728c2ecf20Sopenharmony_ci		/*
5738c2ecf20Sopenharmony_ci		 * There is no need to update blending state for the disabled
5748c2ecf20Sopenharmony_ci		 * plane.
5758c2ecf20Sopenharmony_ci		 */
5768c2ecf20Sopenharmony_ci		if (new->fb)
5778c2ecf20Sopenharmony_ci			tegra_plane_update_transparency(p, tegra_state);
5788c2ecf20Sopenharmony_ci	}
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	return 0;
5818c2ecf20Sopenharmony_ci}
5828c2ecf20Sopenharmony_ci
5838c2ecf20Sopenharmony_ciint tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
5848c2ecf20Sopenharmony_ci				   struct tegra_plane_state *state)
5858c2ecf20Sopenharmony_ci{
5868c2ecf20Sopenharmony_ci	int err;
5878c2ecf20Sopenharmony_ci
5888c2ecf20Sopenharmony_ci	err = tegra_plane_setup_opacity(tegra, state);
5898c2ecf20Sopenharmony_ci	if (err < 0)
5908c2ecf20Sopenharmony_ci		return err;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	err = tegra_plane_setup_transparency(tegra, state);
5938c2ecf20Sopenharmony_ci	if (err < 0)
5948c2ecf20Sopenharmony_ci		return err;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	return 0;
5978c2ecf20Sopenharmony_ci}
598