162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2015 Broadcom
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci/**
762306a36Sopenharmony_ci * DOC: VC4 plane module
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci * Each DRM plane is a layer of pixels being scanned out by the HVS.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * At atomic modeset check time, we compute the HVS display element
1262306a36Sopenharmony_ci * state that would be necessary for displaying the plane (giving us a
1362306a36Sopenharmony_ci * chance to figure out if a plane configuration is invalid), then at
1462306a36Sopenharmony_ci * atomic flush time the CRTC will ask us to write our element state
1562306a36Sopenharmony_ci * into the region of the HVS that it has allocated for us.
1662306a36Sopenharmony_ci */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <drm/drm_atomic.h>
1962306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
2062306a36Sopenharmony_ci#include <drm/drm_atomic_uapi.h>
2162306a36Sopenharmony_ci#include <drm/drm_blend.h>
2262306a36Sopenharmony_ci#include <drm/drm_drv.h>
2362306a36Sopenharmony_ci#include <drm/drm_fb_dma_helper.h>
2462306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2562306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
2662306a36Sopenharmony_ci#include <drm/drm_gem_atomic_helper.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "uapi/drm/vc4_drm.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#include "vc4_drv.h"
3162306a36Sopenharmony_ci#include "vc4_regs.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct hvs_format {
3462306a36Sopenharmony_ci	u32 drm; /* DRM_FORMAT_* */
3562306a36Sopenharmony_ci	u32 hvs; /* HVS_FORMAT_* */
3662306a36Sopenharmony_ci	u32 pixel_order;
3762306a36Sopenharmony_ci	u32 pixel_order_hvs5;
3862306a36Sopenharmony_ci	bool hvs5_only;
3962306a36Sopenharmony_ci} hvs_formats[] = {
4062306a36Sopenharmony_ci	{
4162306a36Sopenharmony_ci		.drm = DRM_FORMAT_XRGB8888,
4262306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA8888,
4362306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ABGR,
4462306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
4562306a36Sopenharmony_ci	},
4662306a36Sopenharmony_ci	{
4762306a36Sopenharmony_ci		.drm = DRM_FORMAT_ARGB8888,
4862306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA8888,
4962306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ABGR,
5062306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
5162306a36Sopenharmony_ci	},
5262306a36Sopenharmony_ci	{
5362306a36Sopenharmony_ci		.drm = DRM_FORMAT_ABGR8888,
5462306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA8888,
5562306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ARGB,
5662306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
5762306a36Sopenharmony_ci	},
5862306a36Sopenharmony_ci	{
5962306a36Sopenharmony_ci		.drm = DRM_FORMAT_XBGR8888,
6062306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA8888,
6162306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ARGB,
6262306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
6362306a36Sopenharmony_ci	},
6462306a36Sopenharmony_ci	{
6562306a36Sopenharmony_ci		.drm = DRM_FORMAT_RGB565,
6662306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGB565,
6762306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XRGB,
6862306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XRGB,
6962306a36Sopenharmony_ci	},
7062306a36Sopenharmony_ci	{
7162306a36Sopenharmony_ci		.drm = DRM_FORMAT_BGR565,
7262306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGB565,
7362306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XBGR,
7462306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XBGR,
7562306a36Sopenharmony_ci	},
7662306a36Sopenharmony_ci	{
7762306a36Sopenharmony_ci		.drm = DRM_FORMAT_ARGB1555,
7862306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA5551,
7962306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ABGR,
8062306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
8162306a36Sopenharmony_ci	},
8262306a36Sopenharmony_ci	{
8362306a36Sopenharmony_ci		.drm = DRM_FORMAT_XRGB1555,
8462306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA5551,
8562306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ABGR,
8662306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
8762306a36Sopenharmony_ci	},
8862306a36Sopenharmony_ci	{
8962306a36Sopenharmony_ci		.drm = DRM_FORMAT_RGB888,
9062306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGB888,
9162306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XRGB,
9262306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XRGB,
9362306a36Sopenharmony_ci	},
9462306a36Sopenharmony_ci	{
9562306a36Sopenharmony_ci		.drm = DRM_FORMAT_BGR888,
9662306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGB888,
9762306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XBGR,
9862306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XBGR,
9962306a36Sopenharmony_ci	},
10062306a36Sopenharmony_ci	{
10162306a36Sopenharmony_ci		.drm = DRM_FORMAT_YUV422,
10262306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
10362306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
10462306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
10562306a36Sopenharmony_ci	},
10662306a36Sopenharmony_ci	{
10762306a36Sopenharmony_ci		.drm = DRM_FORMAT_YVU422,
10862306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_3PLANE,
10962306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
11062306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
11162306a36Sopenharmony_ci	},
11262306a36Sopenharmony_ci	{
11362306a36Sopenharmony_ci		.drm = DRM_FORMAT_YUV420,
11462306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
11562306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
11662306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
11762306a36Sopenharmony_ci	},
11862306a36Sopenharmony_ci	{
11962306a36Sopenharmony_ci		.drm = DRM_FORMAT_YVU420,
12062306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_3PLANE,
12162306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
12262306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
12362306a36Sopenharmony_ci	},
12462306a36Sopenharmony_ci	{
12562306a36Sopenharmony_ci		.drm = DRM_FORMAT_NV12,
12662306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
12762306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
12862306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
12962306a36Sopenharmony_ci	},
13062306a36Sopenharmony_ci	{
13162306a36Sopenharmony_ci		.drm = DRM_FORMAT_NV21,
13262306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV420_2PLANE,
13362306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
13462306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
13562306a36Sopenharmony_ci	},
13662306a36Sopenharmony_ci	{
13762306a36Sopenharmony_ci		.drm = DRM_FORMAT_NV16,
13862306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
13962306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCBCR,
14062306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
14162306a36Sopenharmony_ci	},
14262306a36Sopenharmony_ci	{
14362306a36Sopenharmony_ci		.drm = DRM_FORMAT_NV61,
14462306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_YUV422_2PLANE,
14562306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_XYCRCB,
14662306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCRCB,
14762306a36Sopenharmony_ci	},
14862306a36Sopenharmony_ci	{
14962306a36Sopenharmony_ci		.drm = DRM_FORMAT_P030,
15062306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_YCBCR_10BIT,
15162306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_XYCBCR,
15262306a36Sopenharmony_ci		.hvs5_only = true,
15362306a36Sopenharmony_ci	},
15462306a36Sopenharmony_ci	{
15562306a36Sopenharmony_ci		.drm = DRM_FORMAT_XRGB2101010,
15662306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
15762306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
15862306a36Sopenharmony_ci		.hvs5_only = true,
15962306a36Sopenharmony_ci	},
16062306a36Sopenharmony_ci	{
16162306a36Sopenharmony_ci		.drm = DRM_FORMAT_ARGB2101010,
16262306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
16362306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
16462306a36Sopenharmony_ci		.hvs5_only = true,
16562306a36Sopenharmony_ci	},
16662306a36Sopenharmony_ci	{
16762306a36Sopenharmony_ci		.drm = DRM_FORMAT_ABGR2101010,
16862306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
16962306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
17062306a36Sopenharmony_ci		.hvs5_only = true,
17162306a36Sopenharmony_ci	},
17262306a36Sopenharmony_ci	{
17362306a36Sopenharmony_ci		.drm = DRM_FORMAT_XBGR2101010,
17462306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA1010102,
17562306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
17662306a36Sopenharmony_ci		.hvs5_only = true,
17762306a36Sopenharmony_ci	},
17862306a36Sopenharmony_ci	{
17962306a36Sopenharmony_ci		.drm = DRM_FORMAT_RGB332,
18062306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGB332,
18162306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ARGB,
18262306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
18362306a36Sopenharmony_ci	},
18462306a36Sopenharmony_ci	{
18562306a36Sopenharmony_ci		.drm = DRM_FORMAT_BGR233,
18662306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGB332,
18762306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ABGR,
18862306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
18962306a36Sopenharmony_ci	},
19062306a36Sopenharmony_ci	{
19162306a36Sopenharmony_ci		.drm = DRM_FORMAT_XRGB4444,
19262306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
19362306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ABGR,
19462306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
19562306a36Sopenharmony_ci	},
19662306a36Sopenharmony_ci	{
19762306a36Sopenharmony_ci		.drm = DRM_FORMAT_ARGB4444,
19862306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
19962306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ABGR,
20062306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ARGB,
20162306a36Sopenharmony_ci	},
20262306a36Sopenharmony_ci	{
20362306a36Sopenharmony_ci		.drm = DRM_FORMAT_XBGR4444,
20462306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
20562306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ARGB,
20662306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
20762306a36Sopenharmony_ci	},
20862306a36Sopenharmony_ci	{
20962306a36Sopenharmony_ci		.drm = DRM_FORMAT_ABGR4444,
21062306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
21162306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_ARGB,
21262306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_ABGR,
21362306a36Sopenharmony_ci	},
21462306a36Sopenharmony_ci	{
21562306a36Sopenharmony_ci		.drm = DRM_FORMAT_BGRX4444,
21662306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
21762306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_RGBA,
21862306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_BGRA,
21962306a36Sopenharmony_ci	},
22062306a36Sopenharmony_ci	{
22162306a36Sopenharmony_ci		.drm = DRM_FORMAT_BGRA4444,
22262306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
22362306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_RGBA,
22462306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_BGRA,
22562306a36Sopenharmony_ci	},
22662306a36Sopenharmony_ci	{
22762306a36Sopenharmony_ci		.drm = DRM_FORMAT_RGBX4444,
22862306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
22962306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_BGRA,
23062306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_RGBA,
23162306a36Sopenharmony_ci	},
23262306a36Sopenharmony_ci	{
23362306a36Sopenharmony_ci		.drm = DRM_FORMAT_RGBA4444,
23462306a36Sopenharmony_ci		.hvs = HVS_PIXEL_FORMAT_RGBA4444,
23562306a36Sopenharmony_ci		.pixel_order = HVS_PIXEL_ORDER_BGRA,
23662306a36Sopenharmony_ci		.pixel_order_hvs5 = HVS_PIXEL_ORDER_RGBA,
23762306a36Sopenharmony_ci	},
23862306a36Sopenharmony_ci};
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_cistatic const struct hvs_format *vc4_get_hvs_format(u32 drm_format)
24162306a36Sopenharmony_ci{
24262306a36Sopenharmony_ci	unsigned i;
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
24562306a36Sopenharmony_ci		if (hvs_formats[i].drm == drm_format)
24662306a36Sopenharmony_ci			return &hvs_formats[i];
24762306a36Sopenharmony_ci	}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	return NULL;
25062306a36Sopenharmony_ci}
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_cistatic enum vc4_scaling_mode vc4_get_scaling_mode(u32 src, u32 dst)
25362306a36Sopenharmony_ci{
25462306a36Sopenharmony_ci	if (dst == src)
25562306a36Sopenharmony_ci		return VC4_SCALING_NONE;
25662306a36Sopenharmony_ci	if (3 * dst >= 2 * src)
25762306a36Sopenharmony_ci		return VC4_SCALING_PPF;
25862306a36Sopenharmony_ci	else
25962306a36Sopenharmony_ci		return VC4_SCALING_TPZ;
26062306a36Sopenharmony_ci}
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_cistatic bool plane_enabled(struct drm_plane_state *state)
26362306a36Sopenharmony_ci{
26462306a36Sopenharmony_ci	return state->fb && !WARN_ON(!state->crtc);
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic struct drm_plane_state *vc4_plane_duplicate_state(struct drm_plane *plane)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state;
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	if (WARN_ON(!plane->state))
27262306a36Sopenharmony_ci		return NULL;
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci	vc4_state = kmemdup(plane->state, sizeof(*vc4_state), GFP_KERNEL);
27562306a36Sopenharmony_ci	if (!vc4_state)
27662306a36Sopenharmony_ci		return NULL;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	memset(&vc4_state->lbm, 0, sizeof(vc4_state->lbm));
27962306a36Sopenharmony_ci	vc4_state->dlist_initialized = 0;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	__drm_atomic_helper_plane_duplicate_state(plane, &vc4_state->base);
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_ci	if (vc4_state->dlist) {
28462306a36Sopenharmony_ci		vc4_state->dlist = kmemdup(vc4_state->dlist,
28562306a36Sopenharmony_ci					   vc4_state->dlist_count * 4,
28662306a36Sopenharmony_ci					   GFP_KERNEL);
28762306a36Sopenharmony_ci		if (!vc4_state->dlist) {
28862306a36Sopenharmony_ci			kfree(vc4_state);
28962306a36Sopenharmony_ci			return NULL;
29062306a36Sopenharmony_ci		}
29162306a36Sopenharmony_ci		vc4_state->dlist_size = vc4_state->dlist_count;
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	return &vc4_state->base;
29562306a36Sopenharmony_ci}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_cistatic void vc4_plane_destroy_state(struct drm_plane *plane,
29862306a36Sopenharmony_ci				    struct drm_plane_state *state)
29962306a36Sopenharmony_ci{
30062306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
30162306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (drm_mm_node_allocated(&vc4_state->lbm)) {
30462306a36Sopenharmony_ci		unsigned long irqflags;
30562306a36Sopenharmony_ci
30662306a36Sopenharmony_ci		spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
30762306a36Sopenharmony_ci		drm_mm_remove_node(&vc4_state->lbm);
30862306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
30962306a36Sopenharmony_ci	}
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	kfree(vc4_state->dlist);
31262306a36Sopenharmony_ci	__drm_atomic_helper_plane_destroy_state(&vc4_state->base);
31362306a36Sopenharmony_ci	kfree(state);
31462306a36Sopenharmony_ci}
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci/* Called during init to allocate the plane's atomic state. */
31762306a36Sopenharmony_cistatic void vc4_plane_reset(struct drm_plane *plane)
31862306a36Sopenharmony_ci{
31962306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state;
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	WARN_ON(plane->state);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	vc4_state = kzalloc(sizeof(*vc4_state), GFP_KERNEL);
32462306a36Sopenharmony_ci	if (!vc4_state)
32562306a36Sopenharmony_ci		return;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	__drm_atomic_helper_plane_reset(plane, &vc4_state->base);
32862306a36Sopenharmony_ci}
32962306a36Sopenharmony_ci
33062306a36Sopenharmony_cistatic void vc4_dlist_counter_increment(struct vc4_plane_state *vc4_state)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	if (vc4_state->dlist_count == vc4_state->dlist_size) {
33362306a36Sopenharmony_ci		u32 new_size = max(4u, vc4_state->dlist_count * 2);
33462306a36Sopenharmony_ci		u32 *new_dlist = kmalloc_array(new_size, 4, GFP_KERNEL);
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_ci		if (!new_dlist)
33762306a36Sopenharmony_ci			return;
33862306a36Sopenharmony_ci		memcpy(new_dlist, vc4_state->dlist, vc4_state->dlist_count * 4);
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci		kfree(vc4_state->dlist);
34162306a36Sopenharmony_ci		vc4_state->dlist = new_dlist;
34262306a36Sopenharmony_ci		vc4_state->dlist_size = new_size;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	vc4_state->dlist_count++;
34662306a36Sopenharmony_ci}
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_cistatic void vc4_dlist_write(struct vc4_plane_state *vc4_state, u32 val)
34962306a36Sopenharmony_ci{
35062306a36Sopenharmony_ci	unsigned int idx = vc4_state->dlist_count;
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	vc4_dlist_counter_increment(vc4_state);
35362306a36Sopenharmony_ci	vc4_state->dlist[idx] = val;
35462306a36Sopenharmony_ci}
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci/* Returns the scl0/scl1 field based on whether the dimensions need to
35762306a36Sopenharmony_ci * be up/down/non-scaled.
35862306a36Sopenharmony_ci *
35962306a36Sopenharmony_ci * This is a replication of a table from the spec.
36062306a36Sopenharmony_ci */
36162306a36Sopenharmony_cistatic u32 vc4_get_scl_field(struct drm_plane_state *state, int plane)
36262306a36Sopenharmony_ci{
36362306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	switch (vc4_state->x_scaling[plane] << 2 | vc4_state->y_scaling[plane]) {
36662306a36Sopenharmony_ci	case VC4_SCALING_PPF << 2 | VC4_SCALING_PPF:
36762306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_PPF_V_PPF;
36862306a36Sopenharmony_ci	case VC4_SCALING_TPZ << 2 | VC4_SCALING_PPF:
36962306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_TPZ_V_PPF;
37062306a36Sopenharmony_ci	case VC4_SCALING_PPF << 2 | VC4_SCALING_TPZ:
37162306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_PPF_V_TPZ;
37262306a36Sopenharmony_ci	case VC4_SCALING_TPZ << 2 | VC4_SCALING_TPZ:
37362306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_TPZ_V_TPZ;
37462306a36Sopenharmony_ci	case VC4_SCALING_PPF << 2 | VC4_SCALING_NONE:
37562306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_PPF_V_NONE;
37662306a36Sopenharmony_ci	case VC4_SCALING_NONE << 2 | VC4_SCALING_PPF:
37762306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_NONE_V_PPF;
37862306a36Sopenharmony_ci	case VC4_SCALING_NONE << 2 | VC4_SCALING_TPZ:
37962306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_NONE_V_TPZ;
38062306a36Sopenharmony_ci	case VC4_SCALING_TPZ << 2 | VC4_SCALING_NONE:
38162306a36Sopenharmony_ci		return SCALER_CTL0_SCL_H_TPZ_V_NONE;
38262306a36Sopenharmony_ci	default:
38362306a36Sopenharmony_ci	case VC4_SCALING_NONE << 2 | VC4_SCALING_NONE:
38462306a36Sopenharmony_ci		/* The unity case is independently handled by
38562306a36Sopenharmony_ci		 * SCALER_CTL0_UNITY.
38662306a36Sopenharmony_ci		 */
38762306a36Sopenharmony_ci		return 0;
38862306a36Sopenharmony_ci	}
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_cistatic int vc4_plane_margins_adj(struct drm_plane_state *pstate)
39262306a36Sopenharmony_ci{
39362306a36Sopenharmony_ci	struct vc4_plane_state *vc4_pstate = to_vc4_plane_state(pstate);
39462306a36Sopenharmony_ci	unsigned int left, right, top, bottom, adjhdisplay, adjvdisplay;
39562306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	crtc_state = drm_atomic_get_new_crtc_state(pstate->state,
39862306a36Sopenharmony_ci						   pstate->crtc);
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	vc4_crtc_get_margins(crtc_state, &left, &right, &top, &bottom);
40162306a36Sopenharmony_ci	if (!left && !right && !top && !bottom)
40262306a36Sopenharmony_ci		return 0;
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	if (left + right >= crtc_state->mode.hdisplay ||
40562306a36Sopenharmony_ci	    top + bottom >= crtc_state->mode.vdisplay)
40662306a36Sopenharmony_ci		return -EINVAL;
40762306a36Sopenharmony_ci
40862306a36Sopenharmony_ci	adjhdisplay = crtc_state->mode.hdisplay - (left + right);
40962306a36Sopenharmony_ci	vc4_pstate->crtc_x = DIV_ROUND_CLOSEST(vc4_pstate->crtc_x *
41062306a36Sopenharmony_ci					       adjhdisplay,
41162306a36Sopenharmony_ci					       crtc_state->mode.hdisplay);
41262306a36Sopenharmony_ci	vc4_pstate->crtc_x += left;
41362306a36Sopenharmony_ci	if (vc4_pstate->crtc_x > crtc_state->mode.hdisplay - right)
41462306a36Sopenharmony_ci		vc4_pstate->crtc_x = crtc_state->mode.hdisplay - right;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	adjvdisplay = crtc_state->mode.vdisplay - (top + bottom);
41762306a36Sopenharmony_ci	vc4_pstate->crtc_y = DIV_ROUND_CLOSEST(vc4_pstate->crtc_y *
41862306a36Sopenharmony_ci					       adjvdisplay,
41962306a36Sopenharmony_ci					       crtc_state->mode.vdisplay);
42062306a36Sopenharmony_ci	vc4_pstate->crtc_y += top;
42162306a36Sopenharmony_ci	if (vc4_pstate->crtc_y > crtc_state->mode.vdisplay - bottom)
42262306a36Sopenharmony_ci		vc4_pstate->crtc_y = crtc_state->mode.vdisplay - bottom;
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	vc4_pstate->crtc_w = DIV_ROUND_CLOSEST(vc4_pstate->crtc_w *
42562306a36Sopenharmony_ci					       adjhdisplay,
42662306a36Sopenharmony_ci					       crtc_state->mode.hdisplay);
42762306a36Sopenharmony_ci	vc4_pstate->crtc_h = DIV_ROUND_CLOSEST(vc4_pstate->crtc_h *
42862306a36Sopenharmony_ci					       adjvdisplay,
42962306a36Sopenharmony_ci					       crtc_state->mode.vdisplay);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	if (!vc4_pstate->crtc_w || !vc4_pstate->crtc_h)
43262306a36Sopenharmony_ci		return -EINVAL;
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci	return 0;
43562306a36Sopenharmony_ci}
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_cistatic int vc4_plane_setup_clipping_and_scaling(struct drm_plane_state *state)
43862306a36Sopenharmony_ci{
43962306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
44062306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->fb;
44162306a36Sopenharmony_ci	struct drm_gem_dma_object *bo;
44262306a36Sopenharmony_ci	int num_planes = fb->format->num_planes;
44362306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
44462306a36Sopenharmony_ci	u32 h_subsample = fb->format->hsub;
44562306a36Sopenharmony_ci	u32 v_subsample = fb->format->vsub;
44662306a36Sopenharmony_ci	int i, ret;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	crtc_state = drm_atomic_get_existing_crtc_state(state->state,
44962306a36Sopenharmony_ci							state->crtc);
45062306a36Sopenharmony_ci	if (!crtc_state) {
45162306a36Sopenharmony_ci		DRM_DEBUG_KMS("Invalid crtc state\n");
45262306a36Sopenharmony_ci		return -EINVAL;
45362306a36Sopenharmony_ci	}
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	ret = drm_atomic_helper_check_plane_state(state, crtc_state, 1,
45662306a36Sopenharmony_ci						  INT_MAX, true, true);
45762306a36Sopenharmony_ci	if (ret)
45862306a36Sopenharmony_ci		return ret;
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	for (i = 0; i < num_planes; i++) {
46162306a36Sopenharmony_ci		bo = drm_fb_dma_get_gem_obj(fb, i);
46262306a36Sopenharmony_ci		vc4_state->offsets[i] = bo->dma_addr + fb->offsets[i];
46362306a36Sopenharmony_ci	}
46462306a36Sopenharmony_ci
46562306a36Sopenharmony_ci	/*
46662306a36Sopenharmony_ci	 * We don't support subpixel source positioning for scaling,
46762306a36Sopenharmony_ci	 * but fractional coordinates can be generated by clipping
46862306a36Sopenharmony_ci	 * so just round for now
46962306a36Sopenharmony_ci	 */
47062306a36Sopenharmony_ci	vc4_state->src_x = DIV_ROUND_CLOSEST(state->src.x1, 1 << 16);
47162306a36Sopenharmony_ci	vc4_state->src_y = DIV_ROUND_CLOSEST(state->src.y1, 1 << 16);
47262306a36Sopenharmony_ci	vc4_state->src_w[0] = DIV_ROUND_CLOSEST(state->src.x2, 1 << 16) - vc4_state->src_x;
47362306a36Sopenharmony_ci	vc4_state->src_h[0] = DIV_ROUND_CLOSEST(state->src.y2, 1 << 16) - vc4_state->src_y;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	vc4_state->crtc_x = state->dst.x1;
47662306a36Sopenharmony_ci	vc4_state->crtc_y = state->dst.y1;
47762306a36Sopenharmony_ci	vc4_state->crtc_w = state->dst.x2 - state->dst.x1;
47862306a36Sopenharmony_ci	vc4_state->crtc_h = state->dst.y2 - state->dst.y1;
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_ci	ret = vc4_plane_margins_adj(state);
48162306a36Sopenharmony_ci	if (ret)
48262306a36Sopenharmony_ci		return ret;
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_ci	vc4_state->x_scaling[0] = vc4_get_scaling_mode(vc4_state->src_w[0],
48562306a36Sopenharmony_ci						       vc4_state->crtc_w);
48662306a36Sopenharmony_ci	vc4_state->y_scaling[0] = vc4_get_scaling_mode(vc4_state->src_h[0],
48762306a36Sopenharmony_ci						       vc4_state->crtc_h);
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	vc4_state->is_unity = (vc4_state->x_scaling[0] == VC4_SCALING_NONE &&
49062306a36Sopenharmony_ci			       vc4_state->y_scaling[0] == VC4_SCALING_NONE);
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	if (num_planes > 1) {
49362306a36Sopenharmony_ci		vc4_state->is_yuv = true;
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci		vc4_state->src_w[1] = vc4_state->src_w[0] / h_subsample;
49662306a36Sopenharmony_ci		vc4_state->src_h[1] = vc4_state->src_h[0] / v_subsample;
49762306a36Sopenharmony_ci
49862306a36Sopenharmony_ci		vc4_state->x_scaling[1] =
49962306a36Sopenharmony_ci			vc4_get_scaling_mode(vc4_state->src_w[1],
50062306a36Sopenharmony_ci					     vc4_state->crtc_w);
50162306a36Sopenharmony_ci		vc4_state->y_scaling[1] =
50262306a36Sopenharmony_ci			vc4_get_scaling_mode(vc4_state->src_h[1],
50362306a36Sopenharmony_ci					     vc4_state->crtc_h);
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_ci		/* YUV conversion requires that horizontal scaling be enabled
50662306a36Sopenharmony_ci		 * on the UV plane even if vc4_get_scaling_mode() returned
50762306a36Sopenharmony_ci		 * VC4_SCALING_NONE (which can happen when the down-scaling
50862306a36Sopenharmony_ci		 * ratio is 0.5). Let's force it to VC4_SCALING_PPF in this
50962306a36Sopenharmony_ci		 * case.
51062306a36Sopenharmony_ci		 */
51162306a36Sopenharmony_ci		if (vc4_state->x_scaling[1] == VC4_SCALING_NONE)
51262306a36Sopenharmony_ci			vc4_state->x_scaling[1] = VC4_SCALING_PPF;
51362306a36Sopenharmony_ci	} else {
51462306a36Sopenharmony_ci		vc4_state->is_yuv = false;
51562306a36Sopenharmony_ci		vc4_state->x_scaling[1] = VC4_SCALING_NONE;
51662306a36Sopenharmony_ci		vc4_state->y_scaling[1] = VC4_SCALING_NONE;
51762306a36Sopenharmony_ci	}
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	return 0;
52062306a36Sopenharmony_ci}
52162306a36Sopenharmony_ci
52262306a36Sopenharmony_cistatic void vc4_write_tpz(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
52362306a36Sopenharmony_ci{
52462306a36Sopenharmony_ci	u32 scale, recip;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	scale = (1 << 16) * src / dst;
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci	/* The specs note that while the reciprocal would be defined
52962306a36Sopenharmony_ci	 * as (1<<32)/scale, ~0 is close enough.
53062306a36Sopenharmony_ci	 */
53162306a36Sopenharmony_ci	recip = ~0 / scale;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	vc4_dlist_write(vc4_state,
53462306a36Sopenharmony_ci			VC4_SET_FIELD(scale, SCALER_TPZ0_SCALE) |
53562306a36Sopenharmony_ci			VC4_SET_FIELD(0, SCALER_TPZ0_IPHASE));
53662306a36Sopenharmony_ci	vc4_dlist_write(vc4_state,
53762306a36Sopenharmony_ci			VC4_SET_FIELD(recip, SCALER_TPZ1_RECIP));
53862306a36Sopenharmony_ci}
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_cistatic void vc4_write_ppf(struct vc4_plane_state *vc4_state, u32 src, u32 dst)
54162306a36Sopenharmony_ci{
54262306a36Sopenharmony_ci	u32 scale = (1 << 16) * src / dst;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	vc4_dlist_write(vc4_state,
54562306a36Sopenharmony_ci			SCALER_PPF_AGC |
54662306a36Sopenharmony_ci			VC4_SET_FIELD(scale, SCALER_PPF_SCALE) |
54762306a36Sopenharmony_ci			VC4_SET_FIELD(0, SCALER_PPF_IPHASE));
54862306a36Sopenharmony_ci}
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_cistatic u32 vc4_lbm_size(struct drm_plane_state *state)
55162306a36Sopenharmony_ci{
55262306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
55362306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
55462306a36Sopenharmony_ci	u32 pix_per_line;
55562306a36Sopenharmony_ci	u32 lbm;
55662306a36Sopenharmony_ci
55762306a36Sopenharmony_ci	/* LBM is not needed when there's no vertical scaling. */
55862306a36Sopenharmony_ci	if (vc4_state->y_scaling[0] == VC4_SCALING_NONE &&
55962306a36Sopenharmony_ci	    vc4_state->y_scaling[1] == VC4_SCALING_NONE)
56062306a36Sopenharmony_ci		return 0;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	/*
56362306a36Sopenharmony_ci	 * This can be further optimized in the RGB/YUV444 case if the PPF
56462306a36Sopenharmony_ci	 * decimation factor is between 0.5 and 1.0 by using crtc_w.
56562306a36Sopenharmony_ci	 *
56662306a36Sopenharmony_ci	 * It's not an issue though, since in that case since src_w[0] is going
56762306a36Sopenharmony_ci	 * to be greater than or equal to crtc_w.
56862306a36Sopenharmony_ci	 */
56962306a36Sopenharmony_ci	if (vc4_state->x_scaling[0] == VC4_SCALING_TPZ)
57062306a36Sopenharmony_ci		pix_per_line = vc4_state->crtc_w;
57162306a36Sopenharmony_ci	else
57262306a36Sopenharmony_ci		pix_per_line = vc4_state->src_w[0];
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	if (!vc4_state->is_yuv) {
57562306a36Sopenharmony_ci		if (vc4_state->y_scaling[0] == VC4_SCALING_TPZ)
57662306a36Sopenharmony_ci			lbm = pix_per_line * 8;
57762306a36Sopenharmony_ci		else {
57862306a36Sopenharmony_ci			/* In special cases, this multiplier might be 12. */
57962306a36Sopenharmony_ci			lbm = pix_per_line * 16;
58062306a36Sopenharmony_ci		}
58162306a36Sopenharmony_ci	} else {
58262306a36Sopenharmony_ci		/* There are cases for this going down to a multiplier
58362306a36Sopenharmony_ci		 * of 2, but according to the firmware source, the
58462306a36Sopenharmony_ci		 * table in the docs is somewhat wrong.
58562306a36Sopenharmony_ci		 */
58662306a36Sopenharmony_ci		lbm = pix_per_line * 16;
58762306a36Sopenharmony_ci	}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	/* Align it to 64 or 128 (hvs5) bytes */
59062306a36Sopenharmony_ci	lbm = roundup(lbm, vc4->is_vc5 ? 128 : 64);
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	/* Each "word" of the LBM memory contains 2 or 4 (hvs5) pixels */
59362306a36Sopenharmony_ci	lbm /= vc4->is_vc5 ? 4 : 2;
59462306a36Sopenharmony_ci
59562306a36Sopenharmony_ci	return lbm;
59662306a36Sopenharmony_ci}
59762306a36Sopenharmony_ci
59862306a36Sopenharmony_cistatic void vc4_write_scaling_parameters(struct drm_plane_state *state,
59962306a36Sopenharmony_ci					 int channel)
60062306a36Sopenharmony_ci{
60162306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* Ch0 H-PPF Word 0: Scaling Parameters */
60462306a36Sopenharmony_ci	if (vc4_state->x_scaling[channel] == VC4_SCALING_PPF) {
60562306a36Sopenharmony_ci		vc4_write_ppf(vc4_state,
60662306a36Sopenharmony_ci			      vc4_state->src_w[channel], vc4_state->crtc_w);
60762306a36Sopenharmony_ci	}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_ci	/* Ch0 V-PPF Words 0-1: Scaling Parameters, Context */
61062306a36Sopenharmony_ci	if (vc4_state->y_scaling[channel] == VC4_SCALING_PPF) {
61162306a36Sopenharmony_ci		vc4_write_ppf(vc4_state,
61262306a36Sopenharmony_ci			      vc4_state->src_h[channel], vc4_state->crtc_h);
61362306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
61462306a36Sopenharmony_ci	}
61562306a36Sopenharmony_ci
61662306a36Sopenharmony_ci	/* Ch0 H-TPZ Words 0-1: Scaling Parameters, Recip */
61762306a36Sopenharmony_ci	if (vc4_state->x_scaling[channel] == VC4_SCALING_TPZ) {
61862306a36Sopenharmony_ci		vc4_write_tpz(vc4_state,
61962306a36Sopenharmony_ci			      vc4_state->src_w[channel], vc4_state->crtc_w);
62062306a36Sopenharmony_ci	}
62162306a36Sopenharmony_ci
62262306a36Sopenharmony_ci	/* Ch0 V-TPZ Words 0-2: Scaling Parameters, Recip, Context */
62362306a36Sopenharmony_ci	if (vc4_state->y_scaling[channel] == VC4_SCALING_TPZ) {
62462306a36Sopenharmony_ci		vc4_write_tpz(vc4_state,
62562306a36Sopenharmony_ci			      vc4_state->src_h[channel], vc4_state->crtc_h);
62662306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
62762306a36Sopenharmony_ci	}
62862306a36Sopenharmony_ci}
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_cistatic void vc4_plane_calc_load(struct drm_plane_state *state)
63162306a36Sopenharmony_ci{
63262306a36Sopenharmony_ci	unsigned int hvs_load_shift, vrefresh, i;
63362306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->fb;
63462306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state;
63562306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
63662306a36Sopenharmony_ci	unsigned int vscale_factor;
63762306a36Sopenharmony_ci
63862306a36Sopenharmony_ci	vc4_state = to_vc4_plane_state(state);
63962306a36Sopenharmony_ci	crtc_state = drm_atomic_get_existing_crtc_state(state->state,
64062306a36Sopenharmony_ci							state->crtc);
64162306a36Sopenharmony_ci	vrefresh = drm_mode_vrefresh(&crtc_state->adjusted_mode);
64262306a36Sopenharmony_ci
64362306a36Sopenharmony_ci	/* The HVS is able to process 2 pixels/cycle when scaling the source,
64462306a36Sopenharmony_ci	 * 4 pixels/cycle otherwise.
64562306a36Sopenharmony_ci	 * Alpha blending step seems to be pipelined and it's always operating
64662306a36Sopenharmony_ci	 * at 4 pixels/cycle, so the limiting aspect here seems to be the
64762306a36Sopenharmony_ci	 * scaler block.
64862306a36Sopenharmony_ci	 * HVS load is expressed in clk-cycles/sec (AKA Hz).
64962306a36Sopenharmony_ci	 */
65062306a36Sopenharmony_ci	if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
65162306a36Sopenharmony_ci	    vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
65262306a36Sopenharmony_ci	    vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
65362306a36Sopenharmony_ci	    vc4_state->y_scaling[1] != VC4_SCALING_NONE)
65462306a36Sopenharmony_ci		hvs_load_shift = 1;
65562306a36Sopenharmony_ci	else
65662306a36Sopenharmony_ci		hvs_load_shift = 2;
65762306a36Sopenharmony_ci
65862306a36Sopenharmony_ci	vc4_state->membus_load = 0;
65962306a36Sopenharmony_ci	vc4_state->hvs_load = 0;
66062306a36Sopenharmony_ci	for (i = 0; i < fb->format->num_planes; i++) {
66162306a36Sopenharmony_ci		/* Even if the bandwidth/plane required for a single frame is
66262306a36Sopenharmony_ci		 *
66362306a36Sopenharmony_ci		 * vc4_state->src_w[i] * vc4_state->src_h[i] * cpp * vrefresh
66462306a36Sopenharmony_ci		 *
66562306a36Sopenharmony_ci		 * when downscaling, we have to read more pixels per line in
66662306a36Sopenharmony_ci		 * the time frame reserved for a single line, so the bandwidth
66762306a36Sopenharmony_ci		 * demand can be punctually higher. To account for that, we
66862306a36Sopenharmony_ci		 * calculate the down-scaling factor and multiply the plane
66962306a36Sopenharmony_ci		 * load by this number. We're likely over-estimating the read
67062306a36Sopenharmony_ci		 * demand, but that's better than under-estimating it.
67162306a36Sopenharmony_ci		 */
67262306a36Sopenharmony_ci		vscale_factor = DIV_ROUND_UP(vc4_state->src_h[i],
67362306a36Sopenharmony_ci					     vc4_state->crtc_h);
67462306a36Sopenharmony_ci		vc4_state->membus_load += vc4_state->src_w[i] *
67562306a36Sopenharmony_ci					  vc4_state->src_h[i] * vscale_factor *
67662306a36Sopenharmony_ci					  fb->format->cpp[i];
67762306a36Sopenharmony_ci		vc4_state->hvs_load += vc4_state->crtc_h * vc4_state->crtc_w;
67862306a36Sopenharmony_ci	}
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	vc4_state->hvs_load *= vrefresh;
68162306a36Sopenharmony_ci	vc4_state->hvs_load >>= hvs_load_shift;
68262306a36Sopenharmony_ci	vc4_state->membus_load *= vrefresh;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_cistatic int vc4_plane_allocate_lbm(struct drm_plane_state *state)
68662306a36Sopenharmony_ci{
68762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(state->plane->dev);
68862306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
68962306a36Sopenharmony_ci	unsigned long irqflags;
69062306a36Sopenharmony_ci	u32 lbm_size;
69162306a36Sopenharmony_ci
69262306a36Sopenharmony_ci	lbm_size = vc4_lbm_size(state);
69362306a36Sopenharmony_ci	if (!lbm_size)
69462306a36Sopenharmony_ci		return 0;
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci	if (WARN_ON(!vc4_state->lbm_offset))
69762306a36Sopenharmony_ci		return -EINVAL;
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	/* Allocate the LBM memory that the HVS will use for temporary
70062306a36Sopenharmony_ci	 * storage due to our scaling/format conversion.
70162306a36Sopenharmony_ci	 */
70262306a36Sopenharmony_ci	if (!drm_mm_node_allocated(&vc4_state->lbm)) {
70362306a36Sopenharmony_ci		int ret;
70462306a36Sopenharmony_ci
70562306a36Sopenharmony_ci		spin_lock_irqsave(&vc4->hvs->mm_lock, irqflags);
70662306a36Sopenharmony_ci		ret = drm_mm_insert_node_generic(&vc4->hvs->lbm_mm,
70762306a36Sopenharmony_ci						 &vc4_state->lbm,
70862306a36Sopenharmony_ci						 lbm_size,
70962306a36Sopenharmony_ci						 vc4->is_vc5 ? 64 : 32,
71062306a36Sopenharmony_ci						 0, 0);
71162306a36Sopenharmony_ci		spin_unlock_irqrestore(&vc4->hvs->mm_lock, irqflags);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci		if (ret)
71462306a36Sopenharmony_ci			return ret;
71562306a36Sopenharmony_ci	} else {
71662306a36Sopenharmony_ci		WARN_ON_ONCE(lbm_size != vc4_state->lbm.size);
71762306a36Sopenharmony_ci	}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_ci	vc4_state->dlist[vc4_state->lbm_offset] = vc4_state->lbm.start;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	return 0;
72262306a36Sopenharmony_ci}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci/*
72562306a36Sopenharmony_ci * The colorspace conversion matrices are held in 3 entries in the dlist.
72662306a36Sopenharmony_ci * Create an array of them, with entries for each full and limited mode, and
72762306a36Sopenharmony_ci * each supported colorspace.
72862306a36Sopenharmony_ci */
72962306a36Sopenharmony_cistatic const u32 colorspace_coeffs[2][DRM_COLOR_ENCODING_MAX][3] = {
73062306a36Sopenharmony_ci	{
73162306a36Sopenharmony_ci		/* Limited range */
73262306a36Sopenharmony_ci		{
73362306a36Sopenharmony_ci			/* BT601 */
73462306a36Sopenharmony_ci			SCALER_CSC0_ITR_R_601_5,
73562306a36Sopenharmony_ci			SCALER_CSC1_ITR_R_601_5,
73662306a36Sopenharmony_ci			SCALER_CSC2_ITR_R_601_5,
73762306a36Sopenharmony_ci		}, {
73862306a36Sopenharmony_ci			/* BT709 */
73962306a36Sopenharmony_ci			SCALER_CSC0_ITR_R_709_3,
74062306a36Sopenharmony_ci			SCALER_CSC1_ITR_R_709_3,
74162306a36Sopenharmony_ci			SCALER_CSC2_ITR_R_709_3,
74262306a36Sopenharmony_ci		}, {
74362306a36Sopenharmony_ci			/* BT2020 */
74462306a36Sopenharmony_ci			SCALER_CSC0_ITR_R_2020,
74562306a36Sopenharmony_ci			SCALER_CSC1_ITR_R_2020,
74662306a36Sopenharmony_ci			SCALER_CSC2_ITR_R_2020,
74762306a36Sopenharmony_ci		}
74862306a36Sopenharmony_ci	}, {
74962306a36Sopenharmony_ci		/* Full range */
75062306a36Sopenharmony_ci		{
75162306a36Sopenharmony_ci			/* JFIF */
75262306a36Sopenharmony_ci			SCALER_CSC0_JPEG_JFIF,
75362306a36Sopenharmony_ci			SCALER_CSC1_JPEG_JFIF,
75462306a36Sopenharmony_ci			SCALER_CSC2_JPEG_JFIF,
75562306a36Sopenharmony_ci		}, {
75662306a36Sopenharmony_ci			/* BT709 */
75762306a36Sopenharmony_ci			SCALER_CSC0_ITR_R_709_3_FR,
75862306a36Sopenharmony_ci			SCALER_CSC1_ITR_R_709_3_FR,
75962306a36Sopenharmony_ci			SCALER_CSC2_ITR_R_709_3_FR,
76062306a36Sopenharmony_ci		}, {
76162306a36Sopenharmony_ci			/* BT2020 */
76262306a36Sopenharmony_ci			SCALER_CSC0_ITR_R_2020_FR,
76362306a36Sopenharmony_ci			SCALER_CSC1_ITR_R_2020_FR,
76462306a36Sopenharmony_ci			SCALER_CSC2_ITR_R_2020_FR,
76562306a36Sopenharmony_ci		}
76662306a36Sopenharmony_ci	}
76762306a36Sopenharmony_ci};
76862306a36Sopenharmony_ci
76962306a36Sopenharmony_cistatic u32 vc4_hvs4_get_alpha_blend_mode(struct drm_plane_state *state)
77062306a36Sopenharmony_ci{
77162306a36Sopenharmony_ci	if (!state->fb->format->has_alpha)
77262306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED,
77362306a36Sopenharmony_ci				     SCALER_POS2_ALPHA_MODE);
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	switch (state->pixel_blend_mode) {
77662306a36Sopenharmony_ci	case DRM_MODE_BLEND_PIXEL_NONE:
77762306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_FIXED,
77862306a36Sopenharmony_ci				     SCALER_POS2_ALPHA_MODE);
77962306a36Sopenharmony_ci	default:
78062306a36Sopenharmony_ci	case DRM_MODE_BLEND_PREMULTI:
78162306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_PIPELINE,
78262306a36Sopenharmony_ci				     SCALER_POS2_ALPHA_MODE) |
78362306a36Sopenharmony_ci			SCALER_POS2_ALPHA_PREMULT;
78462306a36Sopenharmony_ci	case DRM_MODE_BLEND_COVERAGE:
78562306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER_POS2_ALPHA_MODE_PIPELINE,
78662306a36Sopenharmony_ci				     SCALER_POS2_ALPHA_MODE);
78762306a36Sopenharmony_ci	}
78862306a36Sopenharmony_ci}
78962306a36Sopenharmony_ci
79062306a36Sopenharmony_cistatic u32 vc4_hvs5_get_alpha_blend_mode(struct drm_plane_state *state)
79162306a36Sopenharmony_ci{
79262306a36Sopenharmony_ci	if (!state->fb->format->has_alpha)
79362306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
79462306a36Sopenharmony_ci				     SCALER5_CTL2_ALPHA_MODE);
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	switch (state->pixel_blend_mode) {
79762306a36Sopenharmony_ci	case DRM_MODE_BLEND_PIXEL_NONE:
79862306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_FIXED,
79962306a36Sopenharmony_ci				     SCALER5_CTL2_ALPHA_MODE);
80062306a36Sopenharmony_ci	default:
80162306a36Sopenharmony_ci	case DRM_MODE_BLEND_PREMULTI:
80262306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE,
80362306a36Sopenharmony_ci				     SCALER5_CTL2_ALPHA_MODE) |
80462306a36Sopenharmony_ci			SCALER5_CTL2_ALPHA_PREMULT;
80562306a36Sopenharmony_ci	case DRM_MODE_BLEND_COVERAGE:
80662306a36Sopenharmony_ci		return VC4_SET_FIELD(SCALER5_CTL2_ALPHA_MODE_PIPELINE,
80762306a36Sopenharmony_ci				     SCALER5_CTL2_ALPHA_MODE);
80862306a36Sopenharmony_ci	}
80962306a36Sopenharmony_ci}
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ci/* Writes out a full display list for an active plane to the plane's
81262306a36Sopenharmony_ci * private dlist state.
81362306a36Sopenharmony_ci */
81462306a36Sopenharmony_cistatic int vc4_plane_mode_set(struct drm_plane *plane,
81562306a36Sopenharmony_ci			      struct drm_plane_state *state)
81662306a36Sopenharmony_ci{
81762306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(plane->dev);
81862306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
81962306a36Sopenharmony_ci	struct drm_framebuffer *fb = state->fb;
82062306a36Sopenharmony_ci	u32 ctl0_offset = vc4_state->dlist_count;
82162306a36Sopenharmony_ci	const struct hvs_format *format = vc4_get_hvs_format(fb->format->format);
82262306a36Sopenharmony_ci	u64 base_format_mod = fourcc_mod_broadcom_mod(fb->modifier);
82362306a36Sopenharmony_ci	int num_planes = fb->format->num_planes;
82462306a36Sopenharmony_ci	u32 h_subsample = fb->format->hsub;
82562306a36Sopenharmony_ci	u32 v_subsample = fb->format->vsub;
82662306a36Sopenharmony_ci	bool mix_plane_alpha;
82762306a36Sopenharmony_ci	bool covers_screen;
82862306a36Sopenharmony_ci	u32 scl0, scl1, pitch0;
82962306a36Sopenharmony_ci	u32 tiling, src_y;
83062306a36Sopenharmony_ci	u32 hvs_format = format->hvs;
83162306a36Sopenharmony_ci	unsigned int rotation;
83262306a36Sopenharmony_ci	int ret, i;
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci	if (vc4_state->dlist_initialized)
83562306a36Sopenharmony_ci		return 0;
83662306a36Sopenharmony_ci
83762306a36Sopenharmony_ci	ret = vc4_plane_setup_clipping_and_scaling(state);
83862306a36Sopenharmony_ci	if (ret)
83962306a36Sopenharmony_ci		return ret;
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	/* SCL1 is used for Cb/Cr scaling of planar formats.  For RGB
84262306a36Sopenharmony_ci	 * and 4:4:4, scl1 should be set to scl0 so both channels of
84362306a36Sopenharmony_ci	 * the scaler do the same thing.  For YUV, the Y plane needs
84462306a36Sopenharmony_ci	 * to be put in channel 1 and Cb/Cr in channel 0, so we swap
84562306a36Sopenharmony_ci	 * the scl fields here.
84662306a36Sopenharmony_ci	 */
84762306a36Sopenharmony_ci	if (num_planes == 1) {
84862306a36Sopenharmony_ci		scl0 = vc4_get_scl_field(state, 0);
84962306a36Sopenharmony_ci		scl1 = scl0;
85062306a36Sopenharmony_ci	} else {
85162306a36Sopenharmony_ci		scl0 = vc4_get_scl_field(state, 1);
85262306a36Sopenharmony_ci		scl1 = vc4_get_scl_field(state, 0);
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_ci	rotation = drm_rotation_simplify(state->rotation,
85662306a36Sopenharmony_ci					 DRM_MODE_ROTATE_0 |
85762306a36Sopenharmony_ci					 DRM_MODE_REFLECT_X |
85862306a36Sopenharmony_ci					 DRM_MODE_REFLECT_Y);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/* We must point to the last line when Y reflection is enabled. */
86162306a36Sopenharmony_ci	src_y = vc4_state->src_y;
86262306a36Sopenharmony_ci	if (rotation & DRM_MODE_REFLECT_Y)
86362306a36Sopenharmony_ci		src_y += vc4_state->src_h[0] - 1;
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_ci	switch (base_format_mod) {
86662306a36Sopenharmony_ci	case DRM_FORMAT_MOD_LINEAR:
86762306a36Sopenharmony_ci		tiling = SCALER_CTL0_TILING_LINEAR;
86862306a36Sopenharmony_ci		pitch0 = VC4_SET_FIELD(fb->pitches[0], SCALER_SRC_PITCH);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci		/* Adjust the base pointer to the first pixel to be scanned
87162306a36Sopenharmony_ci		 * out.
87262306a36Sopenharmony_ci		 */
87362306a36Sopenharmony_ci		for (i = 0; i < num_planes; i++) {
87462306a36Sopenharmony_ci			vc4_state->offsets[i] += src_y /
87562306a36Sopenharmony_ci						 (i ? v_subsample : 1) *
87662306a36Sopenharmony_ci						 fb->pitches[i];
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci			vc4_state->offsets[i] += vc4_state->src_x /
87962306a36Sopenharmony_ci						 (i ? h_subsample : 1) *
88062306a36Sopenharmony_ci						 fb->format->cpp[i];
88162306a36Sopenharmony_ci		}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci		break;
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_ci	case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED: {
88662306a36Sopenharmony_ci		u32 tile_size_shift = 12; /* T tiles are 4kb */
88762306a36Sopenharmony_ci		/* Whole-tile offsets, mostly for setting the pitch. */
88862306a36Sopenharmony_ci		u32 tile_w_shift = fb->format->cpp[0] == 2 ? 6 : 5;
88962306a36Sopenharmony_ci		u32 tile_h_shift = 5; /* 16 and 32bpp are 32 pixels high */
89062306a36Sopenharmony_ci		u32 tile_w_mask = (1 << tile_w_shift) - 1;
89162306a36Sopenharmony_ci		/* The height mask on 32-bit-per-pixel tiles is 63, i.e. twice
89262306a36Sopenharmony_ci		 * the height (in pixels) of a 4k tile.
89362306a36Sopenharmony_ci		 */
89462306a36Sopenharmony_ci		u32 tile_h_mask = (2 << tile_h_shift) - 1;
89562306a36Sopenharmony_ci		/* For T-tiled, the FB pitch is "how many bytes from one row to
89662306a36Sopenharmony_ci		 * the next, such that
89762306a36Sopenharmony_ci		 *
89862306a36Sopenharmony_ci		 *	pitch * tile_h == tile_size * tiles_per_row
89962306a36Sopenharmony_ci		 */
90062306a36Sopenharmony_ci		u32 tiles_w = fb->pitches[0] >> (tile_size_shift - tile_h_shift);
90162306a36Sopenharmony_ci		u32 tiles_l = vc4_state->src_x >> tile_w_shift;
90262306a36Sopenharmony_ci		u32 tiles_r = tiles_w - tiles_l;
90362306a36Sopenharmony_ci		u32 tiles_t = src_y >> tile_h_shift;
90462306a36Sopenharmony_ci		/* Intra-tile offsets, which modify the base address (the
90562306a36Sopenharmony_ci		 * SCALER_PITCH0_TILE_Y_OFFSET tells HVS how to walk from that
90662306a36Sopenharmony_ci		 * base address).
90762306a36Sopenharmony_ci		 */
90862306a36Sopenharmony_ci		u32 tile_y = (src_y >> 4) & 1;
90962306a36Sopenharmony_ci		u32 subtile_y = (src_y >> 2) & 3;
91062306a36Sopenharmony_ci		u32 utile_y = src_y & 3;
91162306a36Sopenharmony_ci		u32 x_off = vc4_state->src_x & tile_w_mask;
91262306a36Sopenharmony_ci		u32 y_off = src_y & tile_h_mask;
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci		/* When Y reflection is requested we must set the
91562306a36Sopenharmony_ci		 * SCALER_PITCH0_TILE_LINE_DIR flag to tell HVS that all lines
91662306a36Sopenharmony_ci		 * after the initial one should be fetched in descending order,
91762306a36Sopenharmony_ci		 * which makes sense since we start from the last line and go
91862306a36Sopenharmony_ci		 * backward.
91962306a36Sopenharmony_ci		 * Don't know why we need y_off = max_y_off - y_off, but it's
92062306a36Sopenharmony_ci		 * definitely required (I guess it's also related to the "going
92162306a36Sopenharmony_ci		 * backward" situation).
92262306a36Sopenharmony_ci		 */
92362306a36Sopenharmony_ci		if (rotation & DRM_MODE_REFLECT_Y) {
92462306a36Sopenharmony_ci			y_off = tile_h_mask - y_off;
92562306a36Sopenharmony_ci			pitch0 = SCALER_PITCH0_TILE_LINE_DIR;
92662306a36Sopenharmony_ci		} else {
92762306a36Sopenharmony_ci			pitch0 = 0;
92862306a36Sopenharmony_ci		}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci		tiling = SCALER_CTL0_TILING_256B_OR_T;
93162306a36Sopenharmony_ci		pitch0 |= (VC4_SET_FIELD(x_off, SCALER_PITCH0_SINK_PIX) |
93262306a36Sopenharmony_ci			   VC4_SET_FIELD(y_off, SCALER_PITCH0_TILE_Y_OFFSET) |
93362306a36Sopenharmony_ci			   VC4_SET_FIELD(tiles_l, SCALER_PITCH0_TILE_WIDTH_L) |
93462306a36Sopenharmony_ci			   VC4_SET_FIELD(tiles_r, SCALER_PITCH0_TILE_WIDTH_R));
93562306a36Sopenharmony_ci		vc4_state->offsets[0] += tiles_t * (tiles_w << tile_size_shift);
93662306a36Sopenharmony_ci		vc4_state->offsets[0] += subtile_y << 8;
93762306a36Sopenharmony_ci		vc4_state->offsets[0] += utile_y << 4;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci		/* Rows of tiles alternate left-to-right and right-to-left. */
94062306a36Sopenharmony_ci		if (tiles_t & 1) {
94162306a36Sopenharmony_ci			pitch0 |= SCALER_PITCH0_TILE_INITIAL_LINE_DIR;
94262306a36Sopenharmony_ci			vc4_state->offsets[0] += (tiles_w - tiles_l) <<
94362306a36Sopenharmony_ci						 tile_size_shift;
94462306a36Sopenharmony_ci			vc4_state->offsets[0] -= (1 + !tile_y) << 10;
94562306a36Sopenharmony_ci		} else {
94662306a36Sopenharmony_ci			vc4_state->offsets[0] += tiles_l << tile_size_shift;
94762306a36Sopenharmony_ci			vc4_state->offsets[0] += tile_y << 10;
94862306a36Sopenharmony_ci		}
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci		break;
95162306a36Sopenharmony_ci	}
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	case DRM_FORMAT_MOD_BROADCOM_SAND64:
95462306a36Sopenharmony_ci	case DRM_FORMAT_MOD_BROADCOM_SAND128:
95562306a36Sopenharmony_ci	case DRM_FORMAT_MOD_BROADCOM_SAND256: {
95662306a36Sopenharmony_ci		uint32_t param = fourcc_mod_broadcom_param(fb->modifier);
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci		if (param > SCALER_TILE_HEIGHT_MASK) {
95962306a36Sopenharmony_ci			DRM_DEBUG_KMS("SAND height too large (%d)\n",
96062306a36Sopenharmony_ci				      param);
96162306a36Sopenharmony_ci			return -EINVAL;
96262306a36Sopenharmony_ci		}
96362306a36Sopenharmony_ci
96462306a36Sopenharmony_ci		if (fb->format->format == DRM_FORMAT_P030) {
96562306a36Sopenharmony_ci			hvs_format = HVS_PIXEL_FORMAT_YCBCR_10BIT;
96662306a36Sopenharmony_ci			tiling = SCALER_CTL0_TILING_128B;
96762306a36Sopenharmony_ci		} else {
96862306a36Sopenharmony_ci			hvs_format = HVS_PIXEL_FORMAT_H264;
96962306a36Sopenharmony_ci
97062306a36Sopenharmony_ci			switch (base_format_mod) {
97162306a36Sopenharmony_ci			case DRM_FORMAT_MOD_BROADCOM_SAND64:
97262306a36Sopenharmony_ci				tiling = SCALER_CTL0_TILING_64B;
97362306a36Sopenharmony_ci				break;
97462306a36Sopenharmony_ci			case DRM_FORMAT_MOD_BROADCOM_SAND128:
97562306a36Sopenharmony_ci				tiling = SCALER_CTL0_TILING_128B;
97662306a36Sopenharmony_ci				break;
97762306a36Sopenharmony_ci			case DRM_FORMAT_MOD_BROADCOM_SAND256:
97862306a36Sopenharmony_ci				tiling = SCALER_CTL0_TILING_256B_OR_T;
97962306a36Sopenharmony_ci				break;
98062306a36Sopenharmony_ci			default:
98162306a36Sopenharmony_ci				return -EINVAL;
98262306a36Sopenharmony_ci			}
98362306a36Sopenharmony_ci		}
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		/* Adjust the base pointer to the first pixel to be scanned
98662306a36Sopenharmony_ci		 * out.
98762306a36Sopenharmony_ci		 *
98862306a36Sopenharmony_ci		 * For P030, y_ptr [31:4] is the 128bit word for the start pixel
98962306a36Sopenharmony_ci		 * y_ptr [3:0] is the pixel (0-11) contained within that 128bit
99062306a36Sopenharmony_ci		 * word that should be taken as the first pixel.
99162306a36Sopenharmony_ci		 * Ditto uv_ptr [31:4] vs [3:0], however [3:0] contains the
99262306a36Sopenharmony_ci		 * element within the 128bit word, eg for pixel 3 the value
99362306a36Sopenharmony_ci		 * should be 6.
99462306a36Sopenharmony_ci		 */
99562306a36Sopenharmony_ci		for (i = 0; i < num_planes; i++) {
99662306a36Sopenharmony_ci			u32 tile_w, tile, x_off, pix_per_tile;
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci			if (fb->format->format == DRM_FORMAT_P030) {
99962306a36Sopenharmony_ci				/*
100062306a36Sopenharmony_ci				 * Spec says: bits [31:4] of the given address
100162306a36Sopenharmony_ci				 * should point to the 128-bit word containing
100262306a36Sopenharmony_ci				 * the desired starting pixel, and bits[3:0]
100362306a36Sopenharmony_ci				 * should be between 0 and 11, indicating which
100462306a36Sopenharmony_ci				 * of the 12-pixels in that 128-bit word is the
100562306a36Sopenharmony_ci				 * first pixel to be used
100662306a36Sopenharmony_ci				 */
100762306a36Sopenharmony_ci				u32 remaining_pixels = vc4_state->src_x % 96;
100862306a36Sopenharmony_ci				u32 aligned = remaining_pixels / 12;
100962306a36Sopenharmony_ci				u32 last_bits = remaining_pixels % 12;
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci				x_off = aligned * 16 + last_bits;
101262306a36Sopenharmony_ci				tile_w = 128;
101362306a36Sopenharmony_ci				pix_per_tile = 96;
101462306a36Sopenharmony_ci			} else {
101562306a36Sopenharmony_ci				switch (base_format_mod) {
101662306a36Sopenharmony_ci				case DRM_FORMAT_MOD_BROADCOM_SAND64:
101762306a36Sopenharmony_ci					tile_w = 64;
101862306a36Sopenharmony_ci					break;
101962306a36Sopenharmony_ci				case DRM_FORMAT_MOD_BROADCOM_SAND128:
102062306a36Sopenharmony_ci					tile_w = 128;
102162306a36Sopenharmony_ci					break;
102262306a36Sopenharmony_ci				case DRM_FORMAT_MOD_BROADCOM_SAND256:
102362306a36Sopenharmony_ci					tile_w = 256;
102462306a36Sopenharmony_ci					break;
102562306a36Sopenharmony_ci				default:
102662306a36Sopenharmony_ci					return -EINVAL;
102762306a36Sopenharmony_ci				}
102862306a36Sopenharmony_ci				pix_per_tile = tile_w / fb->format->cpp[0];
102962306a36Sopenharmony_ci				x_off = (vc4_state->src_x % pix_per_tile) /
103062306a36Sopenharmony_ci					(i ? h_subsample : 1) *
103162306a36Sopenharmony_ci					fb->format->cpp[i];
103262306a36Sopenharmony_ci			}
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci			tile = vc4_state->src_x / pix_per_tile;
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_ci			vc4_state->offsets[i] += param * tile_w * tile;
103762306a36Sopenharmony_ci			vc4_state->offsets[i] += src_y /
103862306a36Sopenharmony_ci						 (i ? v_subsample : 1) *
103962306a36Sopenharmony_ci						 tile_w;
104062306a36Sopenharmony_ci			vc4_state->offsets[i] += x_off & ~(i ? 1 : 0);
104162306a36Sopenharmony_ci		}
104262306a36Sopenharmony_ci
104362306a36Sopenharmony_ci		pitch0 = VC4_SET_FIELD(param, SCALER_TILE_HEIGHT);
104462306a36Sopenharmony_ci		break;
104562306a36Sopenharmony_ci	}
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	default:
104862306a36Sopenharmony_ci		DRM_DEBUG_KMS("Unsupported FB tiling flag 0x%16llx",
104962306a36Sopenharmony_ci			      (long long)fb->modifier);
105062306a36Sopenharmony_ci		return -EINVAL;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* Don't waste cycles mixing with plane alpha if the set alpha
105462306a36Sopenharmony_ci	 * is opaque or there is no per-pixel alpha information.
105562306a36Sopenharmony_ci	 * In any case we use the alpha property value as the fixed alpha.
105662306a36Sopenharmony_ci	 */
105762306a36Sopenharmony_ci	mix_plane_alpha = state->alpha != DRM_BLEND_ALPHA_OPAQUE &&
105862306a36Sopenharmony_ci			  fb->format->has_alpha;
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	if (!vc4->is_vc5) {
106162306a36Sopenharmony_ci	/* Control word */
106262306a36Sopenharmony_ci		vc4_dlist_write(vc4_state,
106362306a36Sopenharmony_ci				SCALER_CTL0_VALID |
106462306a36Sopenharmony_ci				(rotation & DRM_MODE_REFLECT_X ? SCALER_CTL0_HFLIP : 0) |
106562306a36Sopenharmony_ci				(rotation & DRM_MODE_REFLECT_Y ? SCALER_CTL0_VFLIP : 0) |
106662306a36Sopenharmony_ci				VC4_SET_FIELD(SCALER_CTL0_RGBA_EXPAND_ROUND, SCALER_CTL0_RGBA_EXPAND) |
106762306a36Sopenharmony_ci				(format->pixel_order << SCALER_CTL0_ORDER_SHIFT) |
106862306a36Sopenharmony_ci				(hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
106962306a36Sopenharmony_ci				VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
107062306a36Sopenharmony_ci				(vc4_state->is_unity ? SCALER_CTL0_UNITY : 0) |
107162306a36Sopenharmony_ci				VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
107262306a36Sopenharmony_ci				VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1));
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci		/* Position Word 0: Image Positions and Alpha Value */
107562306a36Sopenharmony_ci		vc4_state->pos0_offset = vc4_state->dlist_count;
107662306a36Sopenharmony_ci		vc4_dlist_write(vc4_state,
107762306a36Sopenharmony_ci				VC4_SET_FIELD(state->alpha >> 8, SCALER_POS0_FIXED_ALPHA) |
107862306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->crtc_x, SCALER_POS0_START_X) |
107962306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->crtc_y, SCALER_POS0_START_Y));
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_ci		/* Position Word 1: Scaled Image Dimensions. */
108262306a36Sopenharmony_ci		if (!vc4_state->is_unity) {
108362306a36Sopenharmony_ci			vc4_dlist_write(vc4_state,
108462306a36Sopenharmony_ci					VC4_SET_FIELD(vc4_state->crtc_w,
108562306a36Sopenharmony_ci						      SCALER_POS1_SCL_WIDTH) |
108662306a36Sopenharmony_ci					VC4_SET_FIELD(vc4_state->crtc_h,
108762306a36Sopenharmony_ci						      SCALER_POS1_SCL_HEIGHT));
108862306a36Sopenharmony_ci		}
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci		/* Position Word 2: Source Image Size, Alpha */
109162306a36Sopenharmony_ci		vc4_state->pos2_offset = vc4_state->dlist_count;
109262306a36Sopenharmony_ci		vc4_dlist_write(vc4_state,
109362306a36Sopenharmony_ci				(mix_plane_alpha ? SCALER_POS2_ALPHA_MIX : 0) |
109462306a36Sopenharmony_ci				vc4_hvs4_get_alpha_blend_mode(state) |
109562306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->src_w[0],
109662306a36Sopenharmony_ci					      SCALER_POS2_WIDTH) |
109762306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->src_h[0],
109862306a36Sopenharmony_ci					      SCALER_POS2_HEIGHT));
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_ci		/* Position Word 3: Context.  Written by the HVS. */
110162306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
110262306a36Sopenharmony_ci
110362306a36Sopenharmony_ci	} else {
110462306a36Sopenharmony_ci		/* Control word */
110562306a36Sopenharmony_ci		vc4_dlist_write(vc4_state,
110662306a36Sopenharmony_ci				SCALER_CTL0_VALID |
110762306a36Sopenharmony_ci				(format->pixel_order_hvs5 << SCALER_CTL0_ORDER_SHIFT) |
110862306a36Sopenharmony_ci				(hvs_format << SCALER_CTL0_PIXEL_FORMAT_SHIFT) |
110962306a36Sopenharmony_ci				VC4_SET_FIELD(tiling, SCALER_CTL0_TILING) |
111062306a36Sopenharmony_ci				(vc4_state->is_unity ?
111162306a36Sopenharmony_ci						SCALER5_CTL0_UNITY : 0) |
111262306a36Sopenharmony_ci				VC4_SET_FIELD(scl0, SCALER_CTL0_SCL0) |
111362306a36Sopenharmony_ci				VC4_SET_FIELD(scl1, SCALER_CTL0_SCL1) |
111462306a36Sopenharmony_ci				SCALER5_CTL0_ALPHA_EXPAND |
111562306a36Sopenharmony_ci				SCALER5_CTL0_RGB_EXPAND);
111662306a36Sopenharmony_ci
111762306a36Sopenharmony_ci		/* Position Word 0: Image Positions and Alpha Value */
111862306a36Sopenharmony_ci		vc4_state->pos0_offset = vc4_state->dlist_count;
111962306a36Sopenharmony_ci		vc4_dlist_write(vc4_state,
112062306a36Sopenharmony_ci				(rotation & DRM_MODE_REFLECT_Y ?
112162306a36Sopenharmony_ci						SCALER5_POS0_VFLIP : 0) |
112262306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->crtc_x,
112362306a36Sopenharmony_ci					      SCALER_POS0_START_X) |
112462306a36Sopenharmony_ci				(rotation & DRM_MODE_REFLECT_X ?
112562306a36Sopenharmony_ci					      SCALER5_POS0_HFLIP : 0) |
112662306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->crtc_y,
112762306a36Sopenharmony_ci					      SCALER5_POS0_START_Y)
112862306a36Sopenharmony_ci			       );
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci		/* Control Word 2 */
113162306a36Sopenharmony_ci		vc4_dlist_write(vc4_state,
113262306a36Sopenharmony_ci				VC4_SET_FIELD(state->alpha >> 4,
113362306a36Sopenharmony_ci					      SCALER5_CTL2_ALPHA) |
113462306a36Sopenharmony_ci				vc4_hvs5_get_alpha_blend_mode(state) |
113562306a36Sopenharmony_ci				(mix_plane_alpha ?
113662306a36Sopenharmony_ci					SCALER5_CTL2_ALPHA_MIX : 0)
113762306a36Sopenharmony_ci			       );
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_ci		/* Position Word 1: Scaled Image Dimensions. */
114062306a36Sopenharmony_ci		if (!vc4_state->is_unity) {
114162306a36Sopenharmony_ci			vc4_dlist_write(vc4_state,
114262306a36Sopenharmony_ci					VC4_SET_FIELD(vc4_state->crtc_w,
114362306a36Sopenharmony_ci						      SCALER5_POS1_SCL_WIDTH) |
114462306a36Sopenharmony_ci					VC4_SET_FIELD(vc4_state->crtc_h,
114562306a36Sopenharmony_ci						      SCALER5_POS1_SCL_HEIGHT));
114662306a36Sopenharmony_ci		}
114762306a36Sopenharmony_ci
114862306a36Sopenharmony_ci		/* Position Word 2: Source Image Size */
114962306a36Sopenharmony_ci		vc4_state->pos2_offset = vc4_state->dlist_count;
115062306a36Sopenharmony_ci		vc4_dlist_write(vc4_state,
115162306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->src_w[0],
115262306a36Sopenharmony_ci					      SCALER5_POS2_WIDTH) |
115362306a36Sopenharmony_ci				VC4_SET_FIELD(vc4_state->src_h[0],
115462306a36Sopenharmony_ci					      SCALER5_POS2_HEIGHT));
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci		/* Position Word 3: Context.  Written by the HVS. */
115762306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
115862306a36Sopenharmony_ci	}
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	/* Pointer Word 0/1/2: RGB / Y / Cb / Cr Pointers
116262306a36Sopenharmony_ci	 *
116362306a36Sopenharmony_ci	 * The pointers may be any byte address.
116462306a36Sopenharmony_ci	 */
116562306a36Sopenharmony_ci	vc4_state->ptr0_offset = vc4_state->dlist_count;
116662306a36Sopenharmony_ci	for (i = 0; i < num_planes; i++)
116762306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, vc4_state->offsets[i]);
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci	/* Pointer Context Word 0/1/2: Written by the HVS */
117062306a36Sopenharmony_ci	for (i = 0; i < num_planes; i++)
117162306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, 0xc0c0c0c0);
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci	/* Pitch word 0 */
117462306a36Sopenharmony_ci	vc4_dlist_write(vc4_state, pitch0);
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	/* Pitch word 1/2 */
117762306a36Sopenharmony_ci	for (i = 1; i < num_planes; i++) {
117862306a36Sopenharmony_ci		if (hvs_format != HVS_PIXEL_FORMAT_H264 &&
117962306a36Sopenharmony_ci		    hvs_format != HVS_PIXEL_FORMAT_YCBCR_10BIT) {
118062306a36Sopenharmony_ci			vc4_dlist_write(vc4_state,
118162306a36Sopenharmony_ci					VC4_SET_FIELD(fb->pitches[i],
118262306a36Sopenharmony_ci						      SCALER_SRC_PITCH));
118362306a36Sopenharmony_ci		} else {
118462306a36Sopenharmony_ci			vc4_dlist_write(vc4_state, pitch0);
118562306a36Sopenharmony_ci		}
118662306a36Sopenharmony_ci	}
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci	/* Colorspace conversion words */
118962306a36Sopenharmony_ci	if (vc4_state->is_yuv) {
119062306a36Sopenharmony_ci		enum drm_color_encoding color_encoding = state->color_encoding;
119162306a36Sopenharmony_ci		enum drm_color_range color_range = state->color_range;
119262306a36Sopenharmony_ci		const u32 *ccm;
119362306a36Sopenharmony_ci
119462306a36Sopenharmony_ci		if (color_encoding >= DRM_COLOR_ENCODING_MAX)
119562306a36Sopenharmony_ci			color_encoding = DRM_COLOR_YCBCR_BT601;
119662306a36Sopenharmony_ci		if (color_range >= DRM_COLOR_RANGE_MAX)
119762306a36Sopenharmony_ci			color_range = DRM_COLOR_YCBCR_LIMITED_RANGE;
119862306a36Sopenharmony_ci
119962306a36Sopenharmony_ci		ccm = colorspace_coeffs[color_range][color_encoding];
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, ccm[0]);
120262306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, ccm[1]);
120362306a36Sopenharmony_ci		vc4_dlist_write(vc4_state, ccm[2]);
120462306a36Sopenharmony_ci	}
120562306a36Sopenharmony_ci
120662306a36Sopenharmony_ci	vc4_state->lbm_offset = 0;
120762306a36Sopenharmony_ci
120862306a36Sopenharmony_ci	if (vc4_state->x_scaling[0] != VC4_SCALING_NONE ||
120962306a36Sopenharmony_ci	    vc4_state->x_scaling[1] != VC4_SCALING_NONE ||
121062306a36Sopenharmony_ci	    vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
121162306a36Sopenharmony_ci	    vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
121262306a36Sopenharmony_ci		/* Reserve a slot for the LBM Base Address. The real value will
121362306a36Sopenharmony_ci		 * be set when calling vc4_plane_allocate_lbm().
121462306a36Sopenharmony_ci		 */
121562306a36Sopenharmony_ci		if (vc4_state->y_scaling[0] != VC4_SCALING_NONE ||
121662306a36Sopenharmony_ci		    vc4_state->y_scaling[1] != VC4_SCALING_NONE) {
121762306a36Sopenharmony_ci			vc4_state->lbm_offset = vc4_state->dlist_count;
121862306a36Sopenharmony_ci			vc4_dlist_counter_increment(vc4_state);
121962306a36Sopenharmony_ci		}
122062306a36Sopenharmony_ci
122162306a36Sopenharmony_ci		if (num_planes > 1) {
122262306a36Sopenharmony_ci			/* Emit Cb/Cr as channel 0 and Y as channel
122362306a36Sopenharmony_ci			 * 1. This matches how we set up scl0/scl1
122462306a36Sopenharmony_ci			 * above.
122562306a36Sopenharmony_ci			 */
122662306a36Sopenharmony_ci			vc4_write_scaling_parameters(state, 1);
122762306a36Sopenharmony_ci		}
122862306a36Sopenharmony_ci		vc4_write_scaling_parameters(state, 0);
122962306a36Sopenharmony_ci
123062306a36Sopenharmony_ci		/* If any PPF setup was done, then all the kernel
123162306a36Sopenharmony_ci		 * pointers get uploaded.
123262306a36Sopenharmony_ci		 */
123362306a36Sopenharmony_ci		if (vc4_state->x_scaling[0] == VC4_SCALING_PPF ||
123462306a36Sopenharmony_ci		    vc4_state->y_scaling[0] == VC4_SCALING_PPF ||
123562306a36Sopenharmony_ci		    vc4_state->x_scaling[1] == VC4_SCALING_PPF ||
123662306a36Sopenharmony_ci		    vc4_state->y_scaling[1] == VC4_SCALING_PPF) {
123762306a36Sopenharmony_ci			u32 kernel = VC4_SET_FIELD(vc4->hvs->mitchell_netravali_filter.start,
123862306a36Sopenharmony_ci						   SCALER_PPF_KERNEL_OFFSET);
123962306a36Sopenharmony_ci
124062306a36Sopenharmony_ci			/* HPPF plane 0 */
124162306a36Sopenharmony_ci			vc4_dlist_write(vc4_state, kernel);
124262306a36Sopenharmony_ci			/* VPPF plane 0 */
124362306a36Sopenharmony_ci			vc4_dlist_write(vc4_state, kernel);
124462306a36Sopenharmony_ci			/* HPPF plane 1 */
124562306a36Sopenharmony_ci			vc4_dlist_write(vc4_state, kernel);
124662306a36Sopenharmony_ci			/* VPPF plane 1 */
124762306a36Sopenharmony_ci			vc4_dlist_write(vc4_state, kernel);
124862306a36Sopenharmony_ci		}
124962306a36Sopenharmony_ci	}
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	vc4_state->dlist[ctl0_offset] |=
125262306a36Sopenharmony_ci		VC4_SET_FIELD(vc4_state->dlist_count, SCALER_CTL0_SIZE);
125362306a36Sopenharmony_ci
125462306a36Sopenharmony_ci	/* crtc_* are already clipped coordinates. */
125562306a36Sopenharmony_ci	covers_screen = vc4_state->crtc_x == 0 && vc4_state->crtc_y == 0 &&
125662306a36Sopenharmony_ci			vc4_state->crtc_w == state->crtc->mode.hdisplay &&
125762306a36Sopenharmony_ci			vc4_state->crtc_h == state->crtc->mode.vdisplay;
125862306a36Sopenharmony_ci	/* Background fill might be necessary when the plane has per-pixel
125962306a36Sopenharmony_ci	 * alpha content or a non-opaque plane alpha and could blend from the
126062306a36Sopenharmony_ci	 * background or does not cover the entire screen.
126162306a36Sopenharmony_ci	 */
126262306a36Sopenharmony_ci	vc4_state->needs_bg_fill = fb->format->has_alpha || !covers_screen ||
126362306a36Sopenharmony_ci				   state->alpha != DRM_BLEND_ALPHA_OPAQUE;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	/* Flag the dlist as initialized to avoid checking it twice in case
126662306a36Sopenharmony_ci	 * the async update check already called vc4_plane_mode_set() and
126762306a36Sopenharmony_ci	 * decided to fallback to sync update because async update was not
126862306a36Sopenharmony_ci	 * possible.
126962306a36Sopenharmony_ci	 */
127062306a36Sopenharmony_ci	vc4_state->dlist_initialized = 1;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	vc4_plane_calc_load(state);
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	return 0;
127562306a36Sopenharmony_ci}
127662306a36Sopenharmony_ci
127762306a36Sopenharmony_ci/* If a modeset involves changing the setup of a plane, the atomic
127862306a36Sopenharmony_ci * infrastructure will call this to validate a proposed plane setup.
127962306a36Sopenharmony_ci * However, if a plane isn't getting updated, this (and the
128062306a36Sopenharmony_ci * corresponding vc4_plane_atomic_update) won't get called.  Thus, we
128162306a36Sopenharmony_ci * compute the dlist here and have all active plane dlists get updated
128262306a36Sopenharmony_ci * in the CRTC's flush.
128362306a36Sopenharmony_ci */
128462306a36Sopenharmony_cistatic int vc4_plane_atomic_check(struct drm_plane *plane,
128562306a36Sopenharmony_ci				  struct drm_atomic_state *state)
128662306a36Sopenharmony_ci{
128762306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
128862306a36Sopenharmony_ci										 plane);
128962306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(new_plane_state);
129062306a36Sopenharmony_ci	int ret;
129162306a36Sopenharmony_ci
129262306a36Sopenharmony_ci	vc4_state->dlist_count = 0;
129362306a36Sopenharmony_ci
129462306a36Sopenharmony_ci	if (!plane_enabled(new_plane_state))
129562306a36Sopenharmony_ci		return 0;
129662306a36Sopenharmony_ci
129762306a36Sopenharmony_ci	ret = vc4_plane_mode_set(plane, new_plane_state);
129862306a36Sopenharmony_ci	if (ret)
129962306a36Sopenharmony_ci		return ret;
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	return vc4_plane_allocate_lbm(new_plane_state);
130262306a36Sopenharmony_ci}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_cistatic void vc4_plane_atomic_update(struct drm_plane *plane,
130562306a36Sopenharmony_ci				    struct drm_atomic_state *state)
130662306a36Sopenharmony_ci{
130762306a36Sopenharmony_ci	/* No contents here.  Since we don't know where in the CRTC's
130862306a36Sopenharmony_ci	 * dlist we should be stored, our dlist is uploaded to the
130962306a36Sopenharmony_ci	 * hardware with vc4_plane_write_dlist() at CRTC atomic_flush
131062306a36Sopenharmony_ci	 * time.
131162306a36Sopenharmony_ci	 */
131262306a36Sopenharmony_ci}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ciu32 vc4_plane_write_dlist(struct drm_plane *plane, u32 __iomem *dlist)
131562306a36Sopenharmony_ci{
131662306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
131762306a36Sopenharmony_ci	int i;
131862306a36Sopenharmony_ci	int idx;
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	if (!drm_dev_enter(plane->dev, &idx))
132162306a36Sopenharmony_ci		goto out;
132262306a36Sopenharmony_ci
132362306a36Sopenharmony_ci	vc4_state->hw_dlist = dlist;
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	/* Can't memcpy_toio() because it needs to be 32-bit writes. */
132662306a36Sopenharmony_ci	for (i = 0; i < vc4_state->dlist_count; i++)
132762306a36Sopenharmony_ci		writel(vc4_state->dlist[i], &dlist[i]);
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	drm_dev_exit(idx);
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_ciout:
133262306a36Sopenharmony_ci	return vc4_state->dlist_count;
133362306a36Sopenharmony_ci}
133462306a36Sopenharmony_ci
133562306a36Sopenharmony_ciu32 vc4_plane_dlist_size(const struct drm_plane_state *state)
133662306a36Sopenharmony_ci{
133762306a36Sopenharmony_ci	const struct vc4_plane_state *vc4_state = to_vc4_plane_state(state);
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	return vc4_state->dlist_count;
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci/* Updates the plane to immediately (well, once the FIFO needs
134362306a36Sopenharmony_ci * refilling) scan out from at a new framebuffer.
134462306a36Sopenharmony_ci */
134562306a36Sopenharmony_civoid vc4_plane_async_set_fb(struct drm_plane *plane, struct drm_framebuffer *fb)
134662306a36Sopenharmony_ci{
134762306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state = to_vc4_plane_state(plane->state);
134862306a36Sopenharmony_ci	struct drm_gem_dma_object *bo = drm_fb_dma_get_gem_obj(fb, 0);
134962306a36Sopenharmony_ci	uint32_t addr;
135062306a36Sopenharmony_ci	int idx;
135162306a36Sopenharmony_ci
135262306a36Sopenharmony_ci	if (!drm_dev_enter(plane->dev, &idx))
135362306a36Sopenharmony_ci		return;
135462306a36Sopenharmony_ci
135562306a36Sopenharmony_ci	/* We're skipping the address adjustment for negative origin,
135662306a36Sopenharmony_ci	 * because this is only called on the primary plane.
135762306a36Sopenharmony_ci	 */
135862306a36Sopenharmony_ci	WARN_ON_ONCE(plane->state->crtc_x < 0 || plane->state->crtc_y < 0);
135962306a36Sopenharmony_ci	addr = bo->dma_addr + fb->offsets[0];
136062306a36Sopenharmony_ci
136162306a36Sopenharmony_ci	/* Write the new address into the hardware immediately.  The
136262306a36Sopenharmony_ci	 * scanout will start from this address as soon as the FIFO
136362306a36Sopenharmony_ci	 * needs to refill with pixels.
136462306a36Sopenharmony_ci	 */
136562306a36Sopenharmony_ci	writel(addr, &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	/* Also update the CPU-side dlist copy, so that any later
136862306a36Sopenharmony_ci	 * atomic updates that don't do a new modeset on our plane
136962306a36Sopenharmony_ci	 * also use our updated address.
137062306a36Sopenharmony_ci	 */
137162306a36Sopenharmony_ci	vc4_state->dlist[vc4_state->ptr0_offset] = addr;
137262306a36Sopenharmony_ci
137362306a36Sopenharmony_ci	drm_dev_exit(idx);
137462306a36Sopenharmony_ci}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_cistatic void vc4_plane_atomic_async_update(struct drm_plane *plane,
137762306a36Sopenharmony_ci					  struct drm_atomic_state *state)
137862306a36Sopenharmony_ci{
137962306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
138062306a36Sopenharmony_ci										 plane);
138162306a36Sopenharmony_ci	struct vc4_plane_state *vc4_state, *new_vc4_state;
138262306a36Sopenharmony_ci	int idx;
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_ci	if (!drm_dev_enter(plane->dev, &idx))
138562306a36Sopenharmony_ci		return;
138662306a36Sopenharmony_ci
138762306a36Sopenharmony_ci	swap(plane->state->fb, new_plane_state->fb);
138862306a36Sopenharmony_ci	plane->state->crtc_x = new_plane_state->crtc_x;
138962306a36Sopenharmony_ci	plane->state->crtc_y = new_plane_state->crtc_y;
139062306a36Sopenharmony_ci	plane->state->crtc_w = new_plane_state->crtc_w;
139162306a36Sopenharmony_ci	plane->state->crtc_h = new_plane_state->crtc_h;
139262306a36Sopenharmony_ci	plane->state->src_x = new_plane_state->src_x;
139362306a36Sopenharmony_ci	plane->state->src_y = new_plane_state->src_y;
139462306a36Sopenharmony_ci	plane->state->src_w = new_plane_state->src_w;
139562306a36Sopenharmony_ci	plane->state->src_h = new_plane_state->src_h;
139662306a36Sopenharmony_ci	plane->state->alpha = new_plane_state->alpha;
139762306a36Sopenharmony_ci	plane->state->pixel_blend_mode = new_plane_state->pixel_blend_mode;
139862306a36Sopenharmony_ci	plane->state->rotation = new_plane_state->rotation;
139962306a36Sopenharmony_ci	plane->state->zpos = new_plane_state->zpos;
140062306a36Sopenharmony_ci	plane->state->normalized_zpos = new_plane_state->normalized_zpos;
140162306a36Sopenharmony_ci	plane->state->color_encoding = new_plane_state->color_encoding;
140262306a36Sopenharmony_ci	plane->state->color_range = new_plane_state->color_range;
140362306a36Sopenharmony_ci	plane->state->src = new_plane_state->src;
140462306a36Sopenharmony_ci	plane->state->dst = new_plane_state->dst;
140562306a36Sopenharmony_ci	plane->state->visible = new_plane_state->visible;
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_ci	new_vc4_state = to_vc4_plane_state(new_plane_state);
140862306a36Sopenharmony_ci	vc4_state = to_vc4_plane_state(plane->state);
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	vc4_state->crtc_x = new_vc4_state->crtc_x;
141162306a36Sopenharmony_ci	vc4_state->crtc_y = new_vc4_state->crtc_y;
141262306a36Sopenharmony_ci	vc4_state->crtc_h = new_vc4_state->crtc_h;
141362306a36Sopenharmony_ci	vc4_state->crtc_w = new_vc4_state->crtc_w;
141462306a36Sopenharmony_ci	vc4_state->src_x = new_vc4_state->src_x;
141562306a36Sopenharmony_ci	vc4_state->src_y = new_vc4_state->src_y;
141662306a36Sopenharmony_ci	memcpy(vc4_state->src_w, new_vc4_state->src_w,
141762306a36Sopenharmony_ci	       sizeof(vc4_state->src_w));
141862306a36Sopenharmony_ci	memcpy(vc4_state->src_h, new_vc4_state->src_h,
141962306a36Sopenharmony_ci	       sizeof(vc4_state->src_h));
142062306a36Sopenharmony_ci	memcpy(vc4_state->x_scaling, new_vc4_state->x_scaling,
142162306a36Sopenharmony_ci	       sizeof(vc4_state->x_scaling));
142262306a36Sopenharmony_ci	memcpy(vc4_state->y_scaling, new_vc4_state->y_scaling,
142362306a36Sopenharmony_ci	       sizeof(vc4_state->y_scaling));
142462306a36Sopenharmony_ci	vc4_state->is_unity = new_vc4_state->is_unity;
142562306a36Sopenharmony_ci	vc4_state->is_yuv = new_vc4_state->is_yuv;
142662306a36Sopenharmony_ci	memcpy(vc4_state->offsets, new_vc4_state->offsets,
142762306a36Sopenharmony_ci	       sizeof(vc4_state->offsets));
142862306a36Sopenharmony_ci	vc4_state->needs_bg_fill = new_vc4_state->needs_bg_fill;
142962306a36Sopenharmony_ci
143062306a36Sopenharmony_ci	/* Update the current vc4_state pos0, pos2 and ptr0 dlist entries. */
143162306a36Sopenharmony_ci	vc4_state->dlist[vc4_state->pos0_offset] =
143262306a36Sopenharmony_ci		new_vc4_state->dlist[vc4_state->pos0_offset];
143362306a36Sopenharmony_ci	vc4_state->dlist[vc4_state->pos2_offset] =
143462306a36Sopenharmony_ci		new_vc4_state->dlist[vc4_state->pos2_offset];
143562306a36Sopenharmony_ci	vc4_state->dlist[vc4_state->ptr0_offset] =
143662306a36Sopenharmony_ci		new_vc4_state->dlist[vc4_state->ptr0_offset];
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_ci	/* Note that we can't just call vc4_plane_write_dlist()
143962306a36Sopenharmony_ci	 * because that would smash the context data that the HVS is
144062306a36Sopenharmony_ci	 * currently using.
144162306a36Sopenharmony_ci	 */
144262306a36Sopenharmony_ci	writel(vc4_state->dlist[vc4_state->pos0_offset],
144362306a36Sopenharmony_ci	       &vc4_state->hw_dlist[vc4_state->pos0_offset]);
144462306a36Sopenharmony_ci	writel(vc4_state->dlist[vc4_state->pos2_offset],
144562306a36Sopenharmony_ci	       &vc4_state->hw_dlist[vc4_state->pos2_offset]);
144662306a36Sopenharmony_ci	writel(vc4_state->dlist[vc4_state->ptr0_offset],
144762306a36Sopenharmony_ci	       &vc4_state->hw_dlist[vc4_state->ptr0_offset]);
144862306a36Sopenharmony_ci
144962306a36Sopenharmony_ci	drm_dev_exit(idx);
145062306a36Sopenharmony_ci}
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_cistatic int vc4_plane_atomic_async_check(struct drm_plane *plane,
145362306a36Sopenharmony_ci					struct drm_atomic_state *state)
145462306a36Sopenharmony_ci{
145562306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
145662306a36Sopenharmony_ci										 plane);
145762306a36Sopenharmony_ci	struct vc4_plane_state *old_vc4_state, *new_vc4_state;
145862306a36Sopenharmony_ci	int ret;
145962306a36Sopenharmony_ci	u32 i;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	ret = vc4_plane_mode_set(plane, new_plane_state);
146262306a36Sopenharmony_ci	if (ret)
146362306a36Sopenharmony_ci		return ret;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	old_vc4_state = to_vc4_plane_state(plane->state);
146662306a36Sopenharmony_ci	new_vc4_state = to_vc4_plane_state(new_plane_state);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	if (!new_vc4_state->hw_dlist)
146962306a36Sopenharmony_ci		return -EINVAL;
147062306a36Sopenharmony_ci
147162306a36Sopenharmony_ci	if (old_vc4_state->dlist_count != new_vc4_state->dlist_count ||
147262306a36Sopenharmony_ci	    old_vc4_state->pos0_offset != new_vc4_state->pos0_offset ||
147362306a36Sopenharmony_ci	    old_vc4_state->pos2_offset != new_vc4_state->pos2_offset ||
147462306a36Sopenharmony_ci	    old_vc4_state->ptr0_offset != new_vc4_state->ptr0_offset ||
147562306a36Sopenharmony_ci	    vc4_lbm_size(plane->state) != vc4_lbm_size(new_plane_state))
147662306a36Sopenharmony_ci		return -EINVAL;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/* Only pos0, pos2 and ptr0 DWORDS can be updated in an async update
147962306a36Sopenharmony_ci	 * if anything else has changed, fallback to a sync update.
148062306a36Sopenharmony_ci	 */
148162306a36Sopenharmony_ci	for (i = 0; i < new_vc4_state->dlist_count; i++) {
148262306a36Sopenharmony_ci		if (i == new_vc4_state->pos0_offset ||
148362306a36Sopenharmony_ci		    i == new_vc4_state->pos2_offset ||
148462306a36Sopenharmony_ci		    i == new_vc4_state->ptr0_offset ||
148562306a36Sopenharmony_ci		    (new_vc4_state->lbm_offset &&
148662306a36Sopenharmony_ci		     i == new_vc4_state->lbm_offset))
148762306a36Sopenharmony_ci			continue;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci		if (new_vc4_state->dlist[i] != old_vc4_state->dlist[i])
149062306a36Sopenharmony_ci			return -EINVAL;
149162306a36Sopenharmony_ci	}
149262306a36Sopenharmony_ci
149362306a36Sopenharmony_ci	return 0;
149462306a36Sopenharmony_ci}
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_cistatic int vc4_prepare_fb(struct drm_plane *plane,
149762306a36Sopenharmony_ci			  struct drm_plane_state *state)
149862306a36Sopenharmony_ci{
149962306a36Sopenharmony_ci	struct vc4_bo *bo;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	if (!state->fb)
150262306a36Sopenharmony_ci		return 0;
150362306a36Sopenharmony_ci
150462306a36Sopenharmony_ci	bo = to_vc4_bo(&drm_fb_dma_get_gem_obj(state->fb, 0)->base);
150562306a36Sopenharmony_ci
150662306a36Sopenharmony_ci	drm_gem_plane_helper_prepare_fb(plane, state);
150762306a36Sopenharmony_ci
150862306a36Sopenharmony_ci	if (plane->state->fb == state->fb)
150962306a36Sopenharmony_ci		return 0;
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	return vc4_bo_inc_usecnt(bo);
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_cistatic void vc4_cleanup_fb(struct drm_plane *plane,
151562306a36Sopenharmony_ci			   struct drm_plane_state *state)
151662306a36Sopenharmony_ci{
151762306a36Sopenharmony_ci	struct vc4_bo *bo;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	if (plane->state->fb == state->fb || !state->fb)
152062306a36Sopenharmony_ci		return;
152162306a36Sopenharmony_ci
152262306a36Sopenharmony_ci	bo = to_vc4_bo(&drm_fb_dma_get_gem_obj(state->fb, 0)->base);
152362306a36Sopenharmony_ci	vc4_bo_dec_usecnt(bo);
152462306a36Sopenharmony_ci}
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs vc4_plane_helper_funcs = {
152762306a36Sopenharmony_ci	.atomic_check = vc4_plane_atomic_check,
152862306a36Sopenharmony_ci	.atomic_update = vc4_plane_atomic_update,
152962306a36Sopenharmony_ci	.prepare_fb = vc4_prepare_fb,
153062306a36Sopenharmony_ci	.cleanup_fb = vc4_cleanup_fb,
153162306a36Sopenharmony_ci	.atomic_async_check = vc4_plane_atomic_async_check,
153262306a36Sopenharmony_ci	.atomic_async_update = vc4_plane_atomic_async_update,
153362306a36Sopenharmony_ci};
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs vc5_plane_helper_funcs = {
153662306a36Sopenharmony_ci	.atomic_check = vc4_plane_atomic_check,
153762306a36Sopenharmony_ci	.atomic_update = vc4_plane_atomic_update,
153862306a36Sopenharmony_ci	.atomic_async_check = vc4_plane_atomic_async_check,
153962306a36Sopenharmony_ci	.atomic_async_update = vc4_plane_atomic_async_update,
154062306a36Sopenharmony_ci};
154162306a36Sopenharmony_ci
154262306a36Sopenharmony_cistatic bool vc4_format_mod_supported(struct drm_plane *plane,
154362306a36Sopenharmony_ci				     uint32_t format,
154462306a36Sopenharmony_ci				     uint64_t modifier)
154562306a36Sopenharmony_ci{
154662306a36Sopenharmony_ci	/* Support T_TILING for RGB formats only. */
154762306a36Sopenharmony_ci	switch (format) {
154862306a36Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
154962306a36Sopenharmony_ci	case DRM_FORMAT_ARGB8888:
155062306a36Sopenharmony_ci	case DRM_FORMAT_ABGR8888:
155162306a36Sopenharmony_ci	case DRM_FORMAT_XBGR8888:
155262306a36Sopenharmony_ci	case DRM_FORMAT_RGB565:
155362306a36Sopenharmony_ci	case DRM_FORMAT_BGR565:
155462306a36Sopenharmony_ci	case DRM_FORMAT_ARGB1555:
155562306a36Sopenharmony_ci	case DRM_FORMAT_XRGB1555:
155662306a36Sopenharmony_ci		switch (fourcc_mod_broadcom_mod(modifier)) {
155762306a36Sopenharmony_ci		case DRM_FORMAT_MOD_LINEAR:
155862306a36Sopenharmony_ci		case DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED:
155962306a36Sopenharmony_ci			return true;
156062306a36Sopenharmony_ci		default:
156162306a36Sopenharmony_ci			return false;
156262306a36Sopenharmony_ci		}
156362306a36Sopenharmony_ci	case DRM_FORMAT_NV12:
156462306a36Sopenharmony_ci	case DRM_FORMAT_NV21:
156562306a36Sopenharmony_ci		switch (fourcc_mod_broadcom_mod(modifier)) {
156662306a36Sopenharmony_ci		case DRM_FORMAT_MOD_LINEAR:
156762306a36Sopenharmony_ci		case DRM_FORMAT_MOD_BROADCOM_SAND64:
156862306a36Sopenharmony_ci		case DRM_FORMAT_MOD_BROADCOM_SAND128:
156962306a36Sopenharmony_ci		case DRM_FORMAT_MOD_BROADCOM_SAND256:
157062306a36Sopenharmony_ci			return true;
157162306a36Sopenharmony_ci		default:
157262306a36Sopenharmony_ci			return false;
157362306a36Sopenharmony_ci		}
157462306a36Sopenharmony_ci	case DRM_FORMAT_P030:
157562306a36Sopenharmony_ci		switch (fourcc_mod_broadcom_mod(modifier)) {
157662306a36Sopenharmony_ci		case DRM_FORMAT_MOD_BROADCOM_SAND128:
157762306a36Sopenharmony_ci			return true;
157862306a36Sopenharmony_ci		default:
157962306a36Sopenharmony_ci			return false;
158062306a36Sopenharmony_ci		}
158162306a36Sopenharmony_ci	case DRM_FORMAT_RGBX1010102:
158262306a36Sopenharmony_ci	case DRM_FORMAT_BGRX1010102:
158362306a36Sopenharmony_ci	case DRM_FORMAT_RGBA1010102:
158462306a36Sopenharmony_ci	case DRM_FORMAT_BGRA1010102:
158562306a36Sopenharmony_ci	case DRM_FORMAT_XRGB4444:
158662306a36Sopenharmony_ci	case DRM_FORMAT_ARGB4444:
158762306a36Sopenharmony_ci	case DRM_FORMAT_XBGR4444:
158862306a36Sopenharmony_ci	case DRM_FORMAT_ABGR4444:
158962306a36Sopenharmony_ci	case DRM_FORMAT_RGBX4444:
159062306a36Sopenharmony_ci	case DRM_FORMAT_RGBA4444:
159162306a36Sopenharmony_ci	case DRM_FORMAT_BGRX4444:
159262306a36Sopenharmony_ci	case DRM_FORMAT_BGRA4444:
159362306a36Sopenharmony_ci	case DRM_FORMAT_RGB332:
159462306a36Sopenharmony_ci	case DRM_FORMAT_BGR233:
159562306a36Sopenharmony_ci	case DRM_FORMAT_YUV422:
159662306a36Sopenharmony_ci	case DRM_FORMAT_YVU422:
159762306a36Sopenharmony_ci	case DRM_FORMAT_YUV420:
159862306a36Sopenharmony_ci	case DRM_FORMAT_YVU420:
159962306a36Sopenharmony_ci	case DRM_FORMAT_NV16:
160062306a36Sopenharmony_ci	case DRM_FORMAT_NV61:
160162306a36Sopenharmony_ci	default:
160262306a36Sopenharmony_ci		return (modifier == DRM_FORMAT_MOD_LINEAR);
160362306a36Sopenharmony_ci	}
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_cistatic const struct drm_plane_funcs vc4_plane_funcs = {
160762306a36Sopenharmony_ci	.update_plane = drm_atomic_helper_update_plane,
160862306a36Sopenharmony_ci	.disable_plane = drm_atomic_helper_disable_plane,
160962306a36Sopenharmony_ci	.reset = vc4_plane_reset,
161062306a36Sopenharmony_ci	.atomic_duplicate_state = vc4_plane_duplicate_state,
161162306a36Sopenharmony_ci	.atomic_destroy_state = vc4_plane_destroy_state,
161262306a36Sopenharmony_ci	.format_mod_supported = vc4_format_mod_supported,
161362306a36Sopenharmony_ci};
161462306a36Sopenharmony_ci
161562306a36Sopenharmony_cistruct drm_plane *vc4_plane_init(struct drm_device *dev,
161662306a36Sopenharmony_ci				 enum drm_plane_type type,
161762306a36Sopenharmony_ci				 uint32_t possible_crtcs)
161862306a36Sopenharmony_ci{
161962306a36Sopenharmony_ci	struct vc4_dev *vc4 = to_vc4_dev(dev);
162062306a36Sopenharmony_ci	struct drm_plane *plane;
162162306a36Sopenharmony_ci	struct vc4_plane *vc4_plane;
162262306a36Sopenharmony_ci	u32 formats[ARRAY_SIZE(hvs_formats)];
162362306a36Sopenharmony_ci	int num_formats = 0;
162462306a36Sopenharmony_ci	unsigned i;
162562306a36Sopenharmony_ci	static const uint64_t modifiers[] = {
162662306a36Sopenharmony_ci		DRM_FORMAT_MOD_BROADCOM_VC4_T_TILED,
162762306a36Sopenharmony_ci		DRM_FORMAT_MOD_BROADCOM_SAND128,
162862306a36Sopenharmony_ci		DRM_FORMAT_MOD_BROADCOM_SAND64,
162962306a36Sopenharmony_ci		DRM_FORMAT_MOD_BROADCOM_SAND256,
163062306a36Sopenharmony_ci		DRM_FORMAT_MOD_LINEAR,
163162306a36Sopenharmony_ci		DRM_FORMAT_MOD_INVALID
163262306a36Sopenharmony_ci	};
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(hvs_formats); i++) {
163562306a36Sopenharmony_ci		if (!hvs_formats[i].hvs5_only || vc4->is_vc5) {
163662306a36Sopenharmony_ci			formats[num_formats] = hvs_formats[i].drm;
163762306a36Sopenharmony_ci			num_formats++;
163862306a36Sopenharmony_ci		}
163962306a36Sopenharmony_ci	}
164062306a36Sopenharmony_ci
164162306a36Sopenharmony_ci	vc4_plane = drmm_universal_plane_alloc(dev, struct vc4_plane, base,
164262306a36Sopenharmony_ci					       possible_crtcs,
164362306a36Sopenharmony_ci					       &vc4_plane_funcs,
164462306a36Sopenharmony_ci					       formats, num_formats,
164562306a36Sopenharmony_ci					       modifiers, type, NULL);
164662306a36Sopenharmony_ci	if (IS_ERR(vc4_plane))
164762306a36Sopenharmony_ci		return ERR_CAST(vc4_plane);
164862306a36Sopenharmony_ci	plane = &vc4_plane->base;
164962306a36Sopenharmony_ci
165062306a36Sopenharmony_ci	if (vc4->is_vc5)
165162306a36Sopenharmony_ci		drm_plane_helper_add(plane, &vc5_plane_helper_funcs);
165262306a36Sopenharmony_ci	else
165362306a36Sopenharmony_ci		drm_plane_helper_add(plane, &vc4_plane_helper_funcs);
165462306a36Sopenharmony_ci
165562306a36Sopenharmony_ci	drm_plane_create_alpha_property(plane);
165662306a36Sopenharmony_ci	drm_plane_create_blend_mode_property(plane,
165762306a36Sopenharmony_ci					     BIT(DRM_MODE_BLEND_PIXEL_NONE) |
165862306a36Sopenharmony_ci					     BIT(DRM_MODE_BLEND_PREMULTI) |
165962306a36Sopenharmony_ci					     BIT(DRM_MODE_BLEND_COVERAGE));
166062306a36Sopenharmony_ci	drm_plane_create_rotation_property(plane, DRM_MODE_ROTATE_0,
166162306a36Sopenharmony_ci					   DRM_MODE_ROTATE_0 |
166262306a36Sopenharmony_ci					   DRM_MODE_ROTATE_180 |
166362306a36Sopenharmony_ci					   DRM_MODE_REFLECT_X |
166462306a36Sopenharmony_ci					   DRM_MODE_REFLECT_Y);
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	drm_plane_create_color_properties(plane,
166762306a36Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_BT601) |
166862306a36Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_BT709) |
166962306a36Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_BT2020),
167062306a36Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_LIMITED_RANGE) |
167162306a36Sopenharmony_ci					  BIT(DRM_COLOR_YCBCR_FULL_RANGE),
167262306a36Sopenharmony_ci					  DRM_COLOR_YCBCR_BT709,
167362306a36Sopenharmony_ci					  DRM_COLOR_YCBCR_LIMITED_RANGE);
167462306a36Sopenharmony_ci
167562306a36Sopenharmony_ci	if (type == DRM_PLANE_TYPE_PRIMARY)
167662306a36Sopenharmony_ci		drm_plane_create_zpos_immutable_property(plane, 0);
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_ci	return plane;
167962306a36Sopenharmony_ci}
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci#define VC4_NUM_OVERLAY_PLANES	16
168262306a36Sopenharmony_ci
168362306a36Sopenharmony_ciint vc4_plane_create_additional_planes(struct drm_device *drm)
168462306a36Sopenharmony_ci{
168562306a36Sopenharmony_ci	struct drm_plane *cursor_plane;
168662306a36Sopenharmony_ci	struct drm_crtc *crtc;
168762306a36Sopenharmony_ci	unsigned int i;
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ci	/* Set up some arbitrary number of planes.  We're not limited
169062306a36Sopenharmony_ci	 * by a set number of physical registers, just the space in
169162306a36Sopenharmony_ci	 * the HVS (16k) and how small an plane can be (28 bytes).
169262306a36Sopenharmony_ci	 * However, each plane we set up takes up some memory, and
169362306a36Sopenharmony_ci	 * increases the cost of looping over planes, which atomic
169462306a36Sopenharmony_ci	 * modesetting does quite a bit.  As a result, we pick a
169562306a36Sopenharmony_ci	 * modest number of planes to expose, that should hopefully
169662306a36Sopenharmony_ci	 * still cover any sane usecase.
169762306a36Sopenharmony_ci	 */
169862306a36Sopenharmony_ci	for (i = 0; i < VC4_NUM_OVERLAY_PLANES; i++) {
169962306a36Sopenharmony_ci		struct drm_plane *plane =
170062306a36Sopenharmony_ci			vc4_plane_init(drm, DRM_PLANE_TYPE_OVERLAY,
170162306a36Sopenharmony_ci				       GENMASK(drm->mode_config.num_crtc - 1, 0));
170262306a36Sopenharmony_ci
170362306a36Sopenharmony_ci		if (IS_ERR(plane))
170462306a36Sopenharmony_ci			continue;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci		/* Create zpos property. Max of all the overlays + 1 primary +
170762306a36Sopenharmony_ci		 * 1 cursor plane on a crtc.
170862306a36Sopenharmony_ci		 */
170962306a36Sopenharmony_ci		drm_plane_create_zpos_property(plane, i + 1, 1,
171062306a36Sopenharmony_ci					       VC4_NUM_OVERLAY_PLANES + 1);
171162306a36Sopenharmony_ci	}
171262306a36Sopenharmony_ci
171362306a36Sopenharmony_ci	drm_for_each_crtc(crtc, drm) {
171462306a36Sopenharmony_ci		/* Set up the legacy cursor after overlay initialization,
171562306a36Sopenharmony_ci		 * since the zpos fallback is that planes are rendered by plane
171662306a36Sopenharmony_ci		 * ID order, and that then puts the cursor on top.
171762306a36Sopenharmony_ci		 */
171862306a36Sopenharmony_ci		cursor_plane = vc4_plane_init(drm, DRM_PLANE_TYPE_CURSOR,
171962306a36Sopenharmony_ci					      drm_crtc_mask(crtc));
172062306a36Sopenharmony_ci		if (!IS_ERR(cursor_plane)) {
172162306a36Sopenharmony_ci			crtc->cursor = cursor_plane;
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_ci			drm_plane_create_zpos_property(cursor_plane,
172462306a36Sopenharmony_ci						       VC4_NUM_OVERLAY_PLANES + 1,
172562306a36Sopenharmony_ci						       1,
172662306a36Sopenharmony_ci						       VC4_NUM_OVERLAY_PLANES + 1);
172762306a36Sopenharmony_ci		}
172862306a36Sopenharmony_ci	}
172962306a36Sopenharmony_ci
173062306a36Sopenharmony_ci	return 0;
173162306a36Sopenharmony_ci}
1732