162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2012 Avionic Design GmbH
462306a36Sopenharmony_ci * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/clk.h>
862306a36Sopenharmony_ci#include <linux/debugfs.h>
962306a36Sopenharmony_ci#include <linux/delay.h>
1062306a36Sopenharmony_ci#include <linux/dma-mapping.h>
1162306a36Sopenharmony_ci#include <linux/iommu.h>
1262306a36Sopenharmony_ci#include <linux/interconnect.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/of.h>
1562306a36Sopenharmony_ci#include <linux/platform_device.h>
1662306a36Sopenharmony_ci#include <linux/pm_domain.h>
1762306a36Sopenharmony_ci#include <linux/pm_opp.h>
1862306a36Sopenharmony_ci#include <linux/pm_runtime.h>
1962306a36Sopenharmony_ci#include <linux/reset.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include <soc/tegra/common.h>
2262306a36Sopenharmony_ci#include <soc/tegra/pmc.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <drm/drm_atomic.h>
2562306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
2662306a36Sopenharmony_ci#include <drm/drm_blend.h>
2762306a36Sopenharmony_ci#include <drm/drm_debugfs.h>
2862306a36Sopenharmony_ci#include <drm/drm_fourcc.h>
2962306a36Sopenharmony_ci#include <drm/drm_framebuffer.h>
3062306a36Sopenharmony_ci#include <drm/drm_vblank.h>
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci#include "dc.h"
3362306a36Sopenharmony_ci#include "drm.h"
3462306a36Sopenharmony_ci#include "gem.h"
3562306a36Sopenharmony_ci#include "hub.h"
3662306a36Sopenharmony_ci#include "plane.h"
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistatic void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
3962306a36Sopenharmony_ci					    struct drm_crtc_state *state);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	stats->frames = 0;
4462306a36Sopenharmony_ci	stats->vblank = 0;
4562306a36Sopenharmony_ci	stats->underflow = 0;
4662306a36Sopenharmony_ci	stats->overflow = 0;
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci/* Reads the active copy of a register. */
5062306a36Sopenharmony_cistatic u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci	u32 value;
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
5562306a36Sopenharmony_ci	value = tegra_dc_readl(dc, offset);
5662306a36Sopenharmony_ci	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return value;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
6262306a36Sopenharmony_ci					      unsigned int offset)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	if (offset >= 0x500 && offset <= 0x638) {
6562306a36Sopenharmony_ci		offset = 0x000 + (offset - 0x500);
6662306a36Sopenharmony_ci		return plane->offset + offset;
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	if (offset >= 0x700 && offset <= 0x719) {
7062306a36Sopenharmony_ci		offset = 0x180 + (offset - 0x700);
7162306a36Sopenharmony_ci		return plane->offset + offset;
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	if (offset >= 0x800 && offset <= 0x839) {
7562306a36Sopenharmony_ci		offset = 0x1c0 + (offset - 0x800);
7662306a36Sopenharmony_ci		return plane->offset + offset;
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	return plane->offset + offset;
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic inline u32 tegra_plane_readl(struct tegra_plane *plane,
8562306a36Sopenharmony_ci				    unsigned int offset)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
9162306a36Sopenharmony_ci				      unsigned int offset)
9262306a36Sopenharmony_ci{
9362306a36Sopenharmony_ci	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
9462306a36Sopenharmony_ci}
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cibool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
9762306a36Sopenharmony_ci{
9862306a36Sopenharmony_ci	struct device_node *np = dc->dev->of_node;
9962306a36Sopenharmony_ci	struct of_phandle_iterator it;
10062306a36Sopenharmony_ci	int err;
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci	of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
10362306a36Sopenharmony_ci		if (it.node == dev->of_node)
10462306a36Sopenharmony_ci			return true;
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return false;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci/*
11062306a36Sopenharmony_ci * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
11162306a36Sopenharmony_ci * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
11262306a36Sopenharmony_ci * Latching happens mmediately if the display controller is in STOP mode or
11362306a36Sopenharmony_ci * on the next frame boundary otherwise.
11462306a36Sopenharmony_ci *
11562306a36Sopenharmony_ci * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The
11662306a36Sopenharmony_ci * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits
11762306a36Sopenharmony_ci * are written. When the *_ACT_REQ bits are written, the ARM copy is latched
11862306a36Sopenharmony_ci * into the ACTIVE copy, either immediately if the display controller is in
11962306a36Sopenharmony_ci * STOP mode, or at the next frame boundary otherwise.
12062306a36Sopenharmony_ci */
12162306a36Sopenharmony_civoid tegra_dc_commit(struct tegra_dc *dc)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
12462306a36Sopenharmony_ci	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_cistatic inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
12862306a36Sopenharmony_ci				  unsigned int bpp)
12962306a36Sopenharmony_ci{
13062306a36Sopenharmony_ci	fixed20_12 outf = dfixed_init(out);
13162306a36Sopenharmony_ci	fixed20_12 inf = dfixed_init(in);
13262306a36Sopenharmony_ci	u32 dda_inc;
13362306a36Sopenharmony_ci	int max;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	if (v)
13662306a36Sopenharmony_ci		max = 15;
13762306a36Sopenharmony_ci	else {
13862306a36Sopenharmony_ci		switch (bpp) {
13962306a36Sopenharmony_ci		case 2:
14062306a36Sopenharmony_ci			max = 8;
14162306a36Sopenharmony_ci			break;
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		default:
14462306a36Sopenharmony_ci			WARN_ON_ONCE(1);
14562306a36Sopenharmony_ci			fallthrough;
14662306a36Sopenharmony_ci		case 4:
14762306a36Sopenharmony_ci			max = 4;
14862306a36Sopenharmony_ci			break;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
15362306a36Sopenharmony_ci	inf.full -= dfixed_const(1);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	dda_inc = dfixed_div(inf, outf);
15662306a36Sopenharmony_ci	dda_inc = min_t(u32, dda_inc, dfixed_const(max));
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return dda_inc;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic inline u32 compute_initial_dda(unsigned int in)
16262306a36Sopenharmony_ci{
16362306a36Sopenharmony_ci	fixed20_12 inf = dfixed_init(in);
16462306a36Sopenharmony_ci	return dfixed_frac(inf);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_cistatic void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
16862306a36Sopenharmony_ci{
16962306a36Sopenharmony_ci	u32 background[3] = {
17062306a36Sopenharmony_ci		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
17162306a36Sopenharmony_ci		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
17262306a36Sopenharmony_ci		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
17362306a36Sopenharmony_ci	};
17462306a36Sopenharmony_ci	u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
17562306a36Sopenharmony_ci			 BLEND_COLOR_KEY_NONE;
17662306a36Sopenharmony_ci	u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
17762306a36Sopenharmony_ci	struct tegra_plane_state *state;
17862306a36Sopenharmony_ci	u32 blending[2];
17962306a36Sopenharmony_ci	unsigned int i;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	/* disable blending for non-overlapping case */
18262306a36Sopenharmony_ci	tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
18362306a36Sopenharmony_ci	tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci	state = to_tegra_plane_state(plane->base.state);
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	if (state->opaque) {
18862306a36Sopenharmony_ci		/*
18962306a36Sopenharmony_ci		 * Since custom fix-weight blending isn't utilized and weight
19062306a36Sopenharmony_ci		 * of top window is set to max, we can enforce dependent
19162306a36Sopenharmony_ci		 * blending which in this case results in transparent bottom
19262306a36Sopenharmony_ci		 * window if top window is opaque and if top window enables
19362306a36Sopenharmony_ci		 * alpha blending, then bottom window is getting alpha value
19462306a36Sopenharmony_ci		 * of 1 minus the sum of alpha components of the overlapping
19562306a36Sopenharmony_ci		 * plane.
19662306a36Sopenharmony_ci		 */
19762306a36Sopenharmony_ci		background[0] |= BLEND_CONTROL_DEPENDENT;
19862306a36Sopenharmony_ci		background[1] |= BLEND_CONTROL_DEPENDENT;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci		/*
20162306a36Sopenharmony_ci		 * The region where three windows overlap is the intersection
20262306a36Sopenharmony_ci		 * of the two regions where two windows overlap. It contributes
20362306a36Sopenharmony_ci		 * to the area if all of the windows on top of it have an alpha
20462306a36Sopenharmony_ci		 * component.
20562306a36Sopenharmony_ci		 */
20662306a36Sopenharmony_ci		switch (state->base.normalized_zpos) {
20762306a36Sopenharmony_ci		case 0:
20862306a36Sopenharmony_ci			if (state->blending[0].alpha &&
20962306a36Sopenharmony_ci			    state->blending[1].alpha)
21062306a36Sopenharmony_ci				background[2] |= BLEND_CONTROL_DEPENDENT;
21162306a36Sopenharmony_ci			break;
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		case 1:
21462306a36Sopenharmony_ci			background[2] |= BLEND_CONTROL_DEPENDENT;
21562306a36Sopenharmony_ci			break;
21662306a36Sopenharmony_ci		}
21762306a36Sopenharmony_ci	} else {
21862306a36Sopenharmony_ci		/*
21962306a36Sopenharmony_ci		 * Enable alpha blending if pixel format has an alpha
22062306a36Sopenharmony_ci		 * component.
22162306a36Sopenharmony_ci		 */
22262306a36Sopenharmony_ci		foreground |= BLEND_CONTROL_ALPHA;
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci		/*
22562306a36Sopenharmony_ci		 * If any of the windows on top of this window is opaque, it
22662306a36Sopenharmony_ci		 * will completely conceal this window within that area. If
22762306a36Sopenharmony_ci		 * top window has an alpha component, it is blended over the
22862306a36Sopenharmony_ci		 * bottom window.
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		for (i = 0; i < 2; i++) {
23162306a36Sopenharmony_ci			if (state->blending[i].alpha &&
23262306a36Sopenharmony_ci			    state->blending[i].top)
23362306a36Sopenharmony_ci				background[i] |= BLEND_CONTROL_DEPENDENT;
23462306a36Sopenharmony_ci		}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci		switch (state->base.normalized_zpos) {
23762306a36Sopenharmony_ci		case 0:
23862306a36Sopenharmony_ci			if (state->blending[0].alpha &&
23962306a36Sopenharmony_ci			    state->blending[1].alpha)
24062306a36Sopenharmony_ci				background[2] |= BLEND_CONTROL_DEPENDENT;
24162306a36Sopenharmony_ci			break;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci		case 1:
24462306a36Sopenharmony_ci			/*
24562306a36Sopenharmony_ci			 * When both middle and topmost windows have an alpha,
24662306a36Sopenharmony_ci			 * these windows a mixed together and then the result
24762306a36Sopenharmony_ci			 * is blended over the bottom window.
24862306a36Sopenharmony_ci			 */
24962306a36Sopenharmony_ci			if (state->blending[0].alpha &&
25062306a36Sopenharmony_ci			    state->blending[0].top)
25162306a36Sopenharmony_ci				background[2] |= BLEND_CONTROL_ALPHA;
25262306a36Sopenharmony_ci
25362306a36Sopenharmony_ci			if (state->blending[1].alpha &&
25462306a36Sopenharmony_ci			    state->blending[1].top)
25562306a36Sopenharmony_ci				background[2] |= BLEND_CONTROL_ALPHA;
25662306a36Sopenharmony_ci			break;
25762306a36Sopenharmony_ci		}
25862306a36Sopenharmony_ci	}
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci	switch (state->base.normalized_zpos) {
26162306a36Sopenharmony_ci	case 0:
26262306a36Sopenharmony_ci		tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
26362306a36Sopenharmony_ci		tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
26462306a36Sopenharmony_ci		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
26562306a36Sopenharmony_ci		break;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	case 1:
26862306a36Sopenharmony_ci		/*
26962306a36Sopenharmony_ci		 * If window B / C is topmost, then X / Y registers are
27062306a36Sopenharmony_ci		 * matching the order of blending[...] state indices,
27162306a36Sopenharmony_ci		 * otherwise a swap is required.
27262306a36Sopenharmony_ci		 */
27362306a36Sopenharmony_ci		if (!state->blending[0].top && state->blending[1].top) {
27462306a36Sopenharmony_ci			blending[0] = foreground;
27562306a36Sopenharmony_ci			blending[1] = background[1];
27662306a36Sopenharmony_ci		} else {
27762306a36Sopenharmony_ci			blending[0] = background[0];
27862306a36Sopenharmony_ci			blending[1] = foreground;
27962306a36Sopenharmony_ci		}
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci		tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
28262306a36Sopenharmony_ci		tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
28362306a36Sopenharmony_ci		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
28462306a36Sopenharmony_ci		break;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	case 2:
28762306a36Sopenharmony_ci		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
28862306a36Sopenharmony_ci		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
28962306a36Sopenharmony_ci		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
29062306a36Sopenharmony_ci		break;
29162306a36Sopenharmony_ci	}
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cistatic void tegra_plane_setup_blending(struct tegra_plane *plane,
29562306a36Sopenharmony_ci				       const struct tegra_dc_window *window)
29662306a36Sopenharmony_ci{
29762306a36Sopenharmony_ci	u32 value;
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_ci	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
30062306a36Sopenharmony_ci		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
30162306a36Sopenharmony_ci		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
30262306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT);
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
30562306a36Sopenharmony_ci		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
30662306a36Sopenharmony_ci		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
30762306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT);
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos);
31062306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
31162306a36Sopenharmony_ci}
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic bool
31462306a36Sopenharmony_citegra_plane_use_horizontal_filtering(struct tegra_plane *plane,
31562306a36Sopenharmony_ci				     const struct tegra_dc_window *window)
31662306a36Sopenharmony_ci{
31762306a36Sopenharmony_ci	struct tegra_dc *dc = plane->dc;
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ci	if (window->src.w == window->dst.w)
32062306a36Sopenharmony_ci		return false;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
32362306a36Sopenharmony_ci		return false;
32462306a36Sopenharmony_ci
32562306a36Sopenharmony_ci	return true;
32662306a36Sopenharmony_ci}
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_cistatic bool
32962306a36Sopenharmony_citegra_plane_use_vertical_filtering(struct tegra_plane *plane,
33062306a36Sopenharmony_ci				   const struct tegra_dc_window *window)
33162306a36Sopenharmony_ci{
33262306a36Sopenharmony_ci	struct tegra_dc *dc = plane->dc;
33362306a36Sopenharmony_ci
33462306a36Sopenharmony_ci	if (window->src.h == window->dst.h)
33562306a36Sopenharmony_ci		return false;
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
33862306a36Sopenharmony_ci		return false;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter)
34162306a36Sopenharmony_ci		return false;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	return true;
34462306a36Sopenharmony_ci}
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_cistatic void tegra_dc_setup_window(struct tegra_plane *plane,
34762306a36Sopenharmony_ci				  const struct tegra_dc_window *window)
34862306a36Sopenharmony_ci{
34962306a36Sopenharmony_ci	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
35062306a36Sopenharmony_ci	struct tegra_dc *dc = plane->dc;
35162306a36Sopenharmony_ci	unsigned int planes;
35262306a36Sopenharmony_ci	u32 value;
35362306a36Sopenharmony_ci	bool yuv;
35462306a36Sopenharmony_ci
35562306a36Sopenharmony_ci	/*
35662306a36Sopenharmony_ci	 * For YUV planar modes, the number of bytes per pixel takes into
35762306a36Sopenharmony_ci	 * account only the luma component and therefore is 1.
35862306a36Sopenharmony_ci	 */
35962306a36Sopenharmony_ci	yuv = tegra_plane_format_is_yuv(window->format, &planes, NULL);
36062306a36Sopenharmony_ci	if (!yuv)
36162306a36Sopenharmony_ci		bpp = window->bits_per_pixel / 8;
36262306a36Sopenharmony_ci	else
36362306a36Sopenharmony_ci		bpp = (planes > 1) ? 1 : 2;
36462306a36Sopenharmony_ci
36562306a36Sopenharmony_ci	tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH);
36662306a36Sopenharmony_ci	tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP);
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ci	value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
36962306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_POSITION);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
37262306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_SIZE);
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	h_offset = window->src.x * bpp;
37562306a36Sopenharmony_ci	v_offset = window->src.y;
37662306a36Sopenharmony_ci	h_size = window->src.w * bpp;
37762306a36Sopenharmony_ci	v_size = window->src.h;
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_ci	if (window->reflect_x)
38062306a36Sopenharmony_ci		h_offset += (window->src.w - 1) * bpp;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	if (window->reflect_y)
38362306a36Sopenharmony_ci		v_offset += window->src.h - 1;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
38662306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE);
38762306a36Sopenharmony_ci
38862306a36Sopenharmony_ci	/*
38962306a36Sopenharmony_ci	 * For DDA computations the number of bytes per pixel for YUV planar
39062306a36Sopenharmony_ci	 * modes needs to take into account all Y, U and V components.
39162306a36Sopenharmony_ci	 */
39262306a36Sopenharmony_ci	if (yuv && planes > 1)
39362306a36Sopenharmony_ci		bpp = 2;
39462306a36Sopenharmony_ci
39562306a36Sopenharmony_ci	h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
39662306a36Sopenharmony_ci	v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
39962306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_DDA_INC);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	h_dda = compute_initial_dda(window->src.x);
40262306a36Sopenharmony_ci	v_dda = compute_initial_dda(window->src.y);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci	tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA);
40562306a36Sopenharmony_ci	tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA);
40662306a36Sopenharmony_ci
40762306a36Sopenharmony_ci	tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE);
40862306a36Sopenharmony_ci	tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE);
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	if (yuv && planes > 1) {
41362306a36Sopenharmony_ci		tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		if (planes > 2)
41662306a36Sopenharmony_ci			tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci		value = window->stride[1] << 16 | window->stride[0];
41962306a36Sopenharmony_ci		tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE);
42062306a36Sopenharmony_ci	} else {
42162306a36Sopenharmony_ci		tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE);
42262306a36Sopenharmony_ci	}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_ci	tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET);
42562306a36Sopenharmony_ci	tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci	if (dc->soc->supports_block_linear) {
42862306a36Sopenharmony_ci		unsigned long height = window->tiling.value;
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci		switch (window->tiling.mode) {
43162306a36Sopenharmony_ci		case TEGRA_BO_TILING_MODE_PITCH:
43262306a36Sopenharmony_ci			value = DC_WINBUF_SURFACE_KIND_PITCH;
43362306a36Sopenharmony_ci			break;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci		case TEGRA_BO_TILING_MODE_TILED:
43662306a36Sopenharmony_ci			value = DC_WINBUF_SURFACE_KIND_TILED;
43762306a36Sopenharmony_ci			break;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci		case TEGRA_BO_TILING_MODE_BLOCK:
44062306a36Sopenharmony_ci			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
44162306a36Sopenharmony_ci				DC_WINBUF_SURFACE_KIND_BLOCK;
44262306a36Sopenharmony_ci			break;
44362306a36Sopenharmony_ci		}
44462306a36Sopenharmony_ci
44562306a36Sopenharmony_ci		tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND);
44662306a36Sopenharmony_ci	} else {
44762306a36Sopenharmony_ci		switch (window->tiling.mode) {
44862306a36Sopenharmony_ci		case TEGRA_BO_TILING_MODE_PITCH:
44962306a36Sopenharmony_ci			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
45062306a36Sopenharmony_ci				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
45162306a36Sopenharmony_ci			break;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci		case TEGRA_BO_TILING_MODE_TILED:
45462306a36Sopenharmony_ci			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
45562306a36Sopenharmony_ci				DC_WIN_BUFFER_ADDR_MODE_TILE;
45662306a36Sopenharmony_ci			break;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		case TEGRA_BO_TILING_MODE_BLOCK:
45962306a36Sopenharmony_ci			/*
46062306a36Sopenharmony_ci			 * No need to handle this here because ->atomic_check
46162306a36Sopenharmony_ci			 * will already have filtered it out.
46262306a36Sopenharmony_ci			 */
46362306a36Sopenharmony_ci			break;
46462306a36Sopenharmony_ci		}
46562306a36Sopenharmony_ci
46662306a36Sopenharmony_ci		tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE);
46762306a36Sopenharmony_ci	}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	value = WIN_ENABLE;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	if (yuv) {
47262306a36Sopenharmony_ci		/* setup default colorspace conversion coefficients */
47362306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF);
47462306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB);
47562306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR);
47662306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR);
47762306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG);
47862306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG);
47962306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB);
48062306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB);
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci		value |= CSC_ENABLE;
48362306a36Sopenharmony_ci	} else if (window->bits_per_pixel < 24) {
48462306a36Sopenharmony_ci		value |= COLOR_EXPAND;
48562306a36Sopenharmony_ci	}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci	if (window->reflect_x)
48862306a36Sopenharmony_ci		value |= H_DIRECTION;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (window->reflect_y)
49162306a36Sopenharmony_ci		value |= V_DIRECTION;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	if (tegra_plane_use_horizontal_filtering(plane, window)) {
49462306a36Sopenharmony_ci		/*
49562306a36Sopenharmony_ci		 * Enable horizontal 6-tap filter and set filtering
49662306a36Sopenharmony_ci		 * coefficients to the default values defined in TRM.
49762306a36Sopenharmony_ci		 */
49862306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0));
49962306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1));
50062306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2));
50162306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3));
50262306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4));
50362306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5));
50462306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6));
50562306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7));
50662306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8));
50762306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9));
50862306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10));
50962306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11));
51062306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12));
51162306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13));
51262306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14));
51362306a36Sopenharmony_ci		tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15));
51462306a36Sopenharmony_ci
51562306a36Sopenharmony_ci		value |= H_FILTER;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci
51862306a36Sopenharmony_ci	if (tegra_plane_use_vertical_filtering(plane, window)) {
51962306a36Sopenharmony_ci		unsigned int i, k;
52062306a36Sopenharmony_ci
52162306a36Sopenharmony_ci		/*
52262306a36Sopenharmony_ci		 * Enable vertical 2-tap filter and set filtering
52362306a36Sopenharmony_ci		 * coefficients to the default values defined in TRM.
52462306a36Sopenharmony_ci		 */
52562306a36Sopenharmony_ci		for (i = 0, k = 128; i < 16; i++, k -= 8)
52662306a36Sopenharmony_ci			tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i));
52762306a36Sopenharmony_ci
52862306a36Sopenharmony_ci		value |= V_FILTER;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	if (dc->soc->has_legacy_blending)
53462306a36Sopenharmony_ci		tegra_plane_setup_blending_legacy(plane);
53562306a36Sopenharmony_ci	else
53662306a36Sopenharmony_ci		tegra_plane_setup_blending(plane, window);
53762306a36Sopenharmony_ci}
53862306a36Sopenharmony_ci
53962306a36Sopenharmony_cistatic const u32 tegra20_primary_formats[] = {
54062306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
54162306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
54262306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
54362306a36Sopenharmony_ci	DRM_FORMAT_RGBA5551,
54462306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
54562306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
54662306a36Sopenharmony_ci	/* non-native formats */
54762306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
54862306a36Sopenharmony_ci	DRM_FORMAT_RGBX5551,
54962306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
55062306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
55162306a36Sopenharmony_ci};
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_cistatic const u64 tegra20_modifiers[] = {
55462306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
55562306a36Sopenharmony_ci	DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED,
55662306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
55762306a36Sopenharmony_ci};
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_cistatic const u32 tegra114_primary_formats[] = {
56062306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
56162306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
56262306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
56362306a36Sopenharmony_ci	DRM_FORMAT_RGBA5551,
56462306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
56562306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
56662306a36Sopenharmony_ci	/* new on Tegra114 */
56762306a36Sopenharmony_ci	DRM_FORMAT_ABGR4444,
56862306a36Sopenharmony_ci	DRM_FORMAT_ABGR1555,
56962306a36Sopenharmony_ci	DRM_FORMAT_BGRA5551,
57062306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
57162306a36Sopenharmony_ci	DRM_FORMAT_RGBX5551,
57262306a36Sopenharmony_ci	DRM_FORMAT_XBGR1555,
57362306a36Sopenharmony_ci	DRM_FORMAT_BGRX5551,
57462306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
57562306a36Sopenharmony_ci	DRM_FORMAT_BGRA8888,
57662306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
57762306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
57862306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
57962306a36Sopenharmony_ci};
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_cistatic const u32 tegra124_primary_formats[] = {
58262306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
58362306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
58462306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
58562306a36Sopenharmony_ci	DRM_FORMAT_RGBA5551,
58662306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
58762306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
58862306a36Sopenharmony_ci	/* new on Tegra114 */
58962306a36Sopenharmony_ci	DRM_FORMAT_ABGR4444,
59062306a36Sopenharmony_ci	DRM_FORMAT_ABGR1555,
59162306a36Sopenharmony_ci	DRM_FORMAT_BGRA5551,
59262306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
59362306a36Sopenharmony_ci	DRM_FORMAT_RGBX5551,
59462306a36Sopenharmony_ci	DRM_FORMAT_XBGR1555,
59562306a36Sopenharmony_ci	DRM_FORMAT_BGRX5551,
59662306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
59762306a36Sopenharmony_ci	DRM_FORMAT_BGRA8888,
59862306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
59962306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
60062306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
60162306a36Sopenharmony_ci	/* new on Tegra124 */
60262306a36Sopenharmony_ci	DRM_FORMAT_RGBX8888,
60362306a36Sopenharmony_ci	DRM_FORMAT_BGRX8888,
60462306a36Sopenharmony_ci};
60562306a36Sopenharmony_ci
60662306a36Sopenharmony_cistatic const u64 tegra124_modifiers[] = {
60762306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
60862306a36Sopenharmony_ci	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
60962306a36Sopenharmony_ci	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
61062306a36Sopenharmony_ci	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
61162306a36Sopenharmony_ci	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
61262306a36Sopenharmony_ci	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
61362306a36Sopenharmony_ci	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
61462306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
61562306a36Sopenharmony_ci};
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_cistatic int tegra_plane_atomic_check(struct drm_plane *plane,
61862306a36Sopenharmony_ci				    struct drm_atomic_state *state)
61962306a36Sopenharmony_ci{
62062306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
62162306a36Sopenharmony_ci										 plane);
62262306a36Sopenharmony_ci	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
62362306a36Sopenharmony_ci	unsigned int supported_rotation = DRM_MODE_ROTATE_0 |
62462306a36Sopenharmony_ci					  DRM_MODE_REFLECT_X |
62562306a36Sopenharmony_ci					  DRM_MODE_REFLECT_Y;
62662306a36Sopenharmony_ci	unsigned int rotation = new_plane_state->rotation;
62762306a36Sopenharmony_ci	struct tegra_bo_tiling *tiling = &plane_state->tiling;
62862306a36Sopenharmony_ci	struct tegra_plane *tegra = to_tegra_plane(plane);
62962306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
63062306a36Sopenharmony_ci	int err;
63162306a36Sopenharmony_ci
63262306a36Sopenharmony_ci	plane_state->peak_memory_bandwidth = 0;
63362306a36Sopenharmony_ci	plane_state->avg_memory_bandwidth = 0;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	/* no need for further checks if the plane is being disabled */
63662306a36Sopenharmony_ci	if (!new_plane_state->crtc) {
63762306a36Sopenharmony_ci		plane_state->total_peak_memory_bandwidth = 0;
63862306a36Sopenharmony_ci		return 0;
63962306a36Sopenharmony_ci	}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_ci	err = tegra_plane_format(new_plane_state->fb->format->format,
64262306a36Sopenharmony_ci				 &plane_state->format,
64362306a36Sopenharmony_ci				 &plane_state->swap);
64462306a36Sopenharmony_ci	if (err < 0)
64562306a36Sopenharmony_ci		return err;
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	/*
64862306a36Sopenharmony_ci	 * Tegra20 and Tegra30 are special cases here because they support
64962306a36Sopenharmony_ci	 * only variants of specific formats with an alpha component, but not
65062306a36Sopenharmony_ci	 * the corresponding opaque formats. However, the opaque formats can
65162306a36Sopenharmony_ci	 * be emulated by disabling alpha blending for the plane.
65262306a36Sopenharmony_ci	 */
65362306a36Sopenharmony_ci	if (dc->soc->has_legacy_blending) {
65462306a36Sopenharmony_ci		err = tegra_plane_setup_legacy_state(tegra, plane_state);
65562306a36Sopenharmony_ci		if (err < 0)
65662306a36Sopenharmony_ci			return err;
65762306a36Sopenharmony_ci	}
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
66062306a36Sopenharmony_ci	if (err < 0)
66162306a36Sopenharmony_ci		return err;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
66462306a36Sopenharmony_ci	    !dc->soc->supports_block_linear) {
66562306a36Sopenharmony_ci		DRM_ERROR("hardware doesn't support block linear mode\n");
66662306a36Sopenharmony_ci		return -EINVAL;
66762306a36Sopenharmony_ci	}
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	/*
67062306a36Sopenharmony_ci	 * Older userspace used custom BO flag in order to specify the Y
67162306a36Sopenharmony_ci	 * reflection, while modern userspace uses the generic DRM rotation
67262306a36Sopenharmony_ci	 * property in order to achieve the same result.  The legacy BO flag
67362306a36Sopenharmony_ci	 * duplicates the DRM rotation property when both are set.
67462306a36Sopenharmony_ci	 */
67562306a36Sopenharmony_ci	if (tegra_fb_is_bottom_up(new_plane_state->fb))
67662306a36Sopenharmony_ci		rotation |= DRM_MODE_REFLECT_Y;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	rotation = drm_rotation_simplify(rotation, supported_rotation);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	if (rotation & DRM_MODE_REFLECT_X)
68162306a36Sopenharmony_ci		plane_state->reflect_x = true;
68262306a36Sopenharmony_ci	else
68362306a36Sopenharmony_ci		plane_state->reflect_x = false;
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci	if (rotation & DRM_MODE_REFLECT_Y)
68662306a36Sopenharmony_ci		plane_state->reflect_y = true;
68762306a36Sopenharmony_ci	else
68862306a36Sopenharmony_ci		plane_state->reflect_y = false;
68962306a36Sopenharmony_ci
69062306a36Sopenharmony_ci	/*
69162306a36Sopenharmony_ci	 * Tegra doesn't support different strides for U and V planes so we
69262306a36Sopenharmony_ci	 * error out if the user tries to display a framebuffer with such a
69362306a36Sopenharmony_ci	 * configuration.
69462306a36Sopenharmony_ci	 */
69562306a36Sopenharmony_ci	if (new_plane_state->fb->format->num_planes > 2) {
69662306a36Sopenharmony_ci		if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
69762306a36Sopenharmony_ci			DRM_ERROR("unsupported UV-plane configuration\n");
69862306a36Sopenharmony_ci			return -EINVAL;
69962306a36Sopenharmony_ci		}
70062306a36Sopenharmony_ci	}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	err = tegra_plane_state_add(tegra, new_plane_state);
70362306a36Sopenharmony_ci	if (err < 0)
70462306a36Sopenharmony_ci		return err;
70562306a36Sopenharmony_ci
70662306a36Sopenharmony_ci	return 0;
70762306a36Sopenharmony_ci}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_cistatic void tegra_plane_atomic_disable(struct drm_plane *plane,
71062306a36Sopenharmony_ci				       struct drm_atomic_state *state)
71162306a36Sopenharmony_ci{
71262306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
71362306a36Sopenharmony_ci									   plane);
71462306a36Sopenharmony_ci	struct tegra_plane *p = to_tegra_plane(plane);
71562306a36Sopenharmony_ci	u32 value;
71662306a36Sopenharmony_ci
71762306a36Sopenharmony_ci	/* rien ne va plus */
71862306a36Sopenharmony_ci	if (!old_state || !old_state->crtc)
71962306a36Sopenharmony_ci		return;
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
72262306a36Sopenharmony_ci	value &= ~WIN_ENABLE;
72362306a36Sopenharmony_ci	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
72462306a36Sopenharmony_ci}
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_cistatic void tegra_plane_atomic_update(struct drm_plane *plane,
72762306a36Sopenharmony_ci				      struct drm_atomic_state *state)
72862306a36Sopenharmony_ci{
72962306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
73062306a36Sopenharmony_ci									   plane);
73162306a36Sopenharmony_ci	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
73262306a36Sopenharmony_ci	struct drm_framebuffer *fb = new_state->fb;
73362306a36Sopenharmony_ci	struct tegra_plane *p = to_tegra_plane(plane);
73462306a36Sopenharmony_ci	struct tegra_dc_window window;
73562306a36Sopenharmony_ci	unsigned int i;
73662306a36Sopenharmony_ci
73762306a36Sopenharmony_ci	/* rien ne va plus */
73862306a36Sopenharmony_ci	if (!new_state->crtc || !new_state->fb)
73962306a36Sopenharmony_ci		return;
74062306a36Sopenharmony_ci
74162306a36Sopenharmony_ci	if (!new_state->visible)
74262306a36Sopenharmony_ci		return tegra_plane_atomic_disable(plane, state);
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	memset(&window, 0, sizeof(window));
74562306a36Sopenharmony_ci	window.src.x = new_state->src.x1 >> 16;
74662306a36Sopenharmony_ci	window.src.y = new_state->src.y1 >> 16;
74762306a36Sopenharmony_ci	window.src.w = drm_rect_width(&new_state->src) >> 16;
74862306a36Sopenharmony_ci	window.src.h = drm_rect_height(&new_state->src) >> 16;
74962306a36Sopenharmony_ci	window.dst.x = new_state->dst.x1;
75062306a36Sopenharmony_ci	window.dst.y = new_state->dst.y1;
75162306a36Sopenharmony_ci	window.dst.w = drm_rect_width(&new_state->dst);
75262306a36Sopenharmony_ci	window.dst.h = drm_rect_height(&new_state->dst);
75362306a36Sopenharmony_ci	window.bits_per_pixel = fb->format->cpp[0] * 8;
75462306a36Sopenharmony_ci	window.reflect_x = tegra_plane_state->reflect_x;
75562306a36Sopenharmony_ci	window.reflect_y = tegra_plane_state->reflect_y;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	/* copy from state */
75862306a36Sopenharmony_ci	window.zpos = new_state->normalized_zpos;
75962306a36Sopenharmony_ci	window.tiling = tegra_plane_state->tiling;
76062306a36Sopenharmony_ci	window.format = tegra_plane_state->format;
76162306a36Sopenharmony_ci	window.swap = tegra_plane_state->swap;
76262306a36Sopenharmony_ci
76362306a36Sopenharmony_ci	for (i = 0; i < fb->format->num_planes; i++) {
76462306a36Sopenharmony_ci		window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i];
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci		/*
76762306a36Sopenharmony_ci		 * Tegra uses a shared stride for UV planes. Framebuffers are
76862306a36Sopenharmony_ci		 * already checked for this in the tegra_plane_atomic_check()
76962306a36Sopenharmony_ci		 * function, so it's safe to ignore the V-plane pitch here.
77062306a36Sopenharmony_ci		 */
77162306a36Sopenharmony_ci		if (i < 2)
77262306a36Sopenharmony_ci			window.stride[i] = fb->pitches[i];
77362306a36Sopenharmony_ci	}
77462306a36Sopenharmony_ci
77562306a36Sopenharmony_ci	tegra_dc_setup_window(p, &window);
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
77962306a36Sopenharmony_ci	.prepare_fb = tegra_plane_prepare_fb,
78062306a36Sopenharmony_ci	.cleanup_fb = tegra_plane_cleanup_fb,
78162306a36Sopenharmony_ci	.atomic_check = tegra_plane_atomic_check,
78262306a36Sopenharmony_ci	.atomic_disable = tegra_plane_atomic_disable,
78362306a36Sopenharmony_ci	.atomic_update = tegra_plane_atomic_update,
78462306a36Sopenharmony_ci};
78562306a36Sopenharmony_ci
78662306a36Sopenharmony_cistatic unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm)
78762306a36Sopenharmony_ci{
78862306a36Sopenharmony_ci	/*
78962306a36Sopenharmony_ci	 * Ideally this would use drm_crtc_mask(), but that would require the
79062306a36Sopenharmony_ci	 * CRTC to already be in the mode_config's list of CRTCs. However, it
79162306a36Sopenharmony_ci	 * will only be added to that list in the drm_crtc_init_with_planes()
79262306a36Sopenharmony_ci	 * (in tegra_dc_init()), which in turn requires registration of these
79362306a36Sopenharmony_ci	 * planes. So we have ourselves a nice little chicken and egg problem
79462306a36Sopenharmony_ci	 * here.
79562306a36Sopenharmony_ci	 *
79662306a36Sopenharmony_ci	 * We work around this by manually creating the mask from the number
79762306a36Sopenharmony_ci	 * of CRTCs that have been registered, and should therefore always be
79862306a36Sopenharmony_ci	 * the same as drm_crtc_index() after registration.
79962306a36Sopenharmony_ci	 */
80062306a36Sopenharmony_ci	return 1 << drm->mode_config.num_crtc;
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
80462306a36Sopenharmony_ci						    struct tegra_dc *dc)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
80762306a36Sopenharmony_ci	enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
80862306a36Sopenharmony_ci	struct tegra_plane *plane;
80962306a36Sopenharmony_ci	unsigned int num_formats;
81062306a36Sopenharmony_ci	const u64 *modifiers;
81162306a36Sopenharmony_ci	const u32 *formats;
81262306a36Sopenharmony_ci	int err;
81362306a36Sopenharmony_ci
81462306a36Sopenharmony_ci	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
81562306a36Sopenharmony_ci	if (!plane)
81662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
81762306a36Sopenharmony_ci
81862306a36Sopenharmony_ci	/* Always use window A as primary window */
81962306a36Sopenharmony_ci	plane->offset = 0xa00;
82062306a36Sopenharmony_ci	plane->index = 0;
82162306a36Sopenharmony_ci	plane->dc = dc;
82262306a36Sopenharmony_ci
82362306a36Sopenharmony_ci	num_formats = dc->soc->num_primary_formats;
82462306a36Sopenharmony_ci	formats = dc->soc->primary_formats;
82562306a36Sopenharmony_ci	modifiers = dc->soc->modifiers;
82662306a36Sopenharmony_ci
82762306a36Sopenharmony_ci	err = tegra_plane_interconnect_init(plane);
82862306a36Sopenharmony_ci	if (err) {
82962306a36Sopenharmony_ci		kfree(plane);
83062306a36Sopenharmony_ci		return ERR_PTR(err);
83162306a36Sopenharmony_ci	}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
83462306a36Sopenharmony_ci				       &tegra_plane_funcs, formats,
83562306a36Sopenharmony_ci				       num_formats, modifiers, type, NULL);
83662306a36Sopenharmony_ci	if (err < 0) {
83762306a36Sopenharmony_ci		kfree(plane);
83862306a36Sopenharmony_ci		return ERR_PTR(err);
83962306a36Sopenharmony_ci	}
84062306a36Sopenharmony_ci
84162306a36Sopenharmony_ci	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
84262306a36Sopenharmony_ci	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
84362306a36Sopenharmony_ci
84462306a36Sopenharmony_ci	err = drm_plane_create_rotation_property(&plane->base,
84562306a36Sopenharmony_ci						 DRM_MODE_ROTATE_0,
84662306a36Sopenharmony_ci						 DRM_MODE_ROTATE_0 |
84762306a36Sopenharmony_ci						 DRM_MODE_ROTATE_180 |
84862306a36Sopenharmony_ci						 DRM_MODE_REFLECT_X |
84962306a36Sopenharmony_ci						 DRM_MODE_REFLECT_Y);
85062306a36Sopenharmony_ci	if (err < 0)
85162306a36Sopenharmony_ci		dev_err(dc->dev, "failed to create rotation property: %d\n",
85262306a36Sopenharmony_ci			err);
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	return &plane->base;
85562306a36Sopenharmony_ci}
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_cistatic const u32 tegra_legacy_cursor_plane_formats[] = {
85862306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
85962306a36Sopenharmony_ci};
86062306a36Sopenharmony_ci
86162306a36Sopenharmony_cistatic const u32 tegra_cursor_plane_formats[] = {
86262306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
86362306a36Sopenharmony_ci};
86462306a36Sopenharmony_ci
86562306a36Sopenharmony_cistatic int tegra_cursor_atomic_check(struct drm_plane *plane,
86662306a36Sopenharmony_ci				     struct drm_atomic_state *state)
86762306a36Sopenharmony_ci{
86862306a36Sopenharmony_ci	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
86962306a36Sopenharmony_ci										 plane);
87062306a36Sopenharmony_ci	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
87162306a36Sopenharmony_ci	struct tegra_plane *tegra = to_tegra_plane(plane);
87262306a36Sopenharmony_ci	int err;
87362306a36Sopenharmony_ci
87462306a36Sopenharmony_ci	plane_state->peak_memory_bandwidth = 0;
87562306a36Sopenharmony_ci	plane_state->avg_memory_bandwidth = 0;
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* no need for further checks if the plane is being disabled */
87862306a36Sopenharmony_ci	if (!new_plane_state->crtc) {
87962306a36Sopenharmony_ci		plane_state->total_peak_memory_bandwidth = 0;
88062306a36Sopenharmony_ci		return 0;
88162306a36Sopenharmony_ci	}
88262306a36Sopenharmony_ci
88362306a36Sopenharmony_ci	/* scaling not supported for cursor */
88462306a36Sopenharmony_ci	if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) ||
88562306a36Sopenharmony_ci	    (new_plane_state->src_h >> 16 != new_plane_state->crtc_h))
88662306a36Sopenharmony_ci		return -EINVAL;
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci	/* only square cursors supported */
88962306a36Sopenharmony_ci	if (new_plane_state->src_w != new_plane_state->src_h)
89062306a36Sopenharmony_ci		return -EINVAL;
89162306a36Sopenharmony_ci
89262306a36Sopenharmony_ci	if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 &&
89362306a36Sopenharmony_ci	    new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256)
89462306a36Sopenharmony_ci		return -EINVAL;
89562306a36Sopenharmony_ci
89662306a36Sopenharmony_ci	err = tegra_plane_state_add(tegra, new_plane_state);
89762306a36Sopenharmony_ci	if (err < 0)
89862306a36Sopenharmony_ci		return err;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return 0;
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic void __tegra_cursor_atomic_update(struct drm_plane *plane,
90462306a36Sopenharmony_ci					 struct drm_plane_state *new_state)
90562306a36Sopenharmony_ci{
90662306a36Sopenharmony_ci	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
90762306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
90862306a36Sopenharmony_ci	struct tegra_drm *tegra = plane->dev->dev_private;
90962306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
91062306a36Sopenharmony_ci	u64 dma_mask = *dc->dev->dma_mask;
91162306a36Sopenharmony_ci#endif
91262306a36Sopenharmony_ci	unsigned int x, y;
91362306a36Sopenharmony_ci	u32 value = 0;
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci	/* rien ne va plus */
91662306a36Sopenharmony_ci	if (!new_state->crtc || !new_state->fb)
91762306a36Sopenharmony_ci		return;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	/*
92062306a36Sopenharmony_ci	 * Legacy display supports hardware clipping of the cursor, but
92162306a36Sopenharmony_ci	 * nvdisplay relies on software to clip the cursor to the screen.
92262306a36Sopenharmony_ci	 */
92362306a36Sopenharmony_ci	if (!dc->soc->has_nvdisplay)
92462306a36Sopenharmony_ci		value |= CURSOR_CLIP_DISPLAY;
92562306a36Sopenharmony_ci
92662306a36Sopenharmony_ci	switch (new_state->crtc_w) {
92762306a36Sopenharmony_ci	case 32:
92862306a36Sopenharmony_ci		value |= CURSOR_SIZE_32x32;
92962306a36Sopenharmony_ci		break;
93062306a36Sopenharmony_ci
93162306a36Sopenharmony_ci	case 64:
93262306a36Sopenharmony_ci		value |= CURSOR_SIZE_64x64;
93362306a36Sopenharmony_ci		break;
93462306a36Sopenharmony_ci
93562306a36Sopenharmony_ci	case 128:
93662306a36Sopenharmony_ci		value |= CURSOR_SIZE_128x128;
93762306a36Sopenharmony_ci		break;
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	case 256:
94062306a36Sopenharmony_ci		value |= CURSOR_SIZE_256x256;
94162306a36Sopenharmony_ci		break;
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_ci	default:
94462306a36Sopenharmony_ci		WARN(1, "cursor size %ux%u not supported\n",
94562306a36Sopenharmony_ci		     new_state->crtc_w, new_state->crtc_h);
94662306a36Sopenharmony_ci		return;
94762306a36Sopenharmony_ci	}
94862306a36Sopenharmony_ci
94962306a36Sopenharmony_ci	value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff;
95062306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
95162306a36Sopenharmony_ci
95262306a36Sopenharmony_ci#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
95362306a36Sopenharmony_ci	value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
95462306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
95562306a36Sopenharmony_ci#endif
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	/* enable cursor and set blend mode */
95862306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
95962306a36Sopenharmony_ci	value |= CURSOR_ENABLE;
96062306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
96362306a36Sopenharmony_ci	value &= ~CURSOR_DST_BLEND_MASK;
96462306a36Sopenharmony_ci	value &= ~CURSOR_SRC_BLEND_MASK;
96562306a36Sopenharmony_ci
96662306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay)
96762306a36Sopenharmony_ci		value &= ~CURSOR_COMPOSITION_MODE_XOR;
96862306a36Sopenharmony_ci	else
96962306a36Sopenharmony_ci		value |= CURSOR_MODE_NORMAL;
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
97262306a36Sopenharmony_ci	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
97362306a36Sopenharmony_ci	value |= CURSOR_ALPHA;
97462306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
97562306a36Sopenharmony_ci
97662306a36Sopenharmony_ci	/* nvdisplay relies on software for clipping */
97762306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay) {
97862306a36Sopenharmony_ci		struct drm_rect src;
97962306a36Sopenharmony_ci
98062306a36Sopenharmony_ci		x = new_state->dst.x1;
98162306a36Sopenharmony_ci		y = new_state->dst.y1;
98262306a36Sopenharmony_ci
98362306a36Sopenharmony_ci		drm_rect_fp_to_int(&src, &new_state->src);
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci		value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
98662306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci		value = (drm_rect_height(&src) & tegra->vmask) << 16 |
98962306a36Sopenharmony_ci			(drm_rect_width(&src) & tegra->hmask);
99062306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
99162306a36Sopenharmony_ci	} else {
99262306a36Sopenharmony_ci		x = new_state->crtc_x;
99362306a36Sopenharmony_ci		y = new_state->crtc_y;
99462306a36Sopenharmony_ci	}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_ci	/* position the cursor */
99762306a36Sopenharmony_ci	value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
99862306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
99962306a36Sopenharmony_ci}
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_cistatic void tegra_cursor_atomic_update(struct drm_plane *plane,
100262306a36Sopenharmony_ci				       struct drm_atomic_state *state)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
100562306a36Sopenharmony_ci
100662306a36Sopenharmony_ci	__tegra_cursor_atomic_update(plane, new_state);
100762306a36Sopenharmony_ci}
100862306a36Sopenharmony_ci
100962306a36Sopenharmony_cistatic void tegra_cursor_atomic_disable(struct drm_plane *plane,
101062306a36Sopenharmony_ci					struct drm_atomic_state *state)
101162306a36Sopenharmony_ci{
101262306a36Sopenharmony_ci	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
101362306a36Sopenharmony_ci									   plane);
101462306a36Sopenharmony_ci	struct tegra_dc *dc;
101562306a36Sopenharmony_ci	u32 value;
101662306a36Sopenharmony_ci
101762306a36Sopenharmony_ci	/* rien ne va plus */
101862306a36Sopenharmony_ci	if (!old_state || !old_state->crtc)
101962306a36Sopenharmony_ci		return;
102062306a36Sopenharmony_ci
102162306a36Sopenharmony_ci	dc = to_tegra_dc(old_state->crtc);
102262306a36Sopenharmony_ci
102362306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
102462306a36Sopenharmony_ci	value &= ~CURSOR_ENABLE;
102562306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
102662306a36Sopenharmony_ci}
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_cistatic int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state)
102962306a36Sopenharmony_ci{
103062306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
103162306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state;
103262306a36Sopenharmony_ci	int min_scale, max_scale;
103362306a36Sopenharmony_ci	int err;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
103662306a36Sopenharmony_ci	if (WARN_ON(!crtc_state))
103762306a36Sopenharmony_ci		return -EINVAL;
103862306a36Sopenharmony_ci
103962306a36Sopenharmony_ci	if (!crtc_state->active)
104062306a36Sopenharmony_ci		return -EINVAL;
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	if (plane->state->crtc != new_state->crtc ||
104362306a36Sopenharmony_ci	    plane->state->src_w != new_state->src_w ||
104462306a36Sopenharmony_ci	    plane->state->src_h != new_state->src_h ||
104562306a36Sopenharmony_ci	    plane->state->crtc_w != new_state->crtc_w ||
104662306a36Sopenharmony_ci	    plane->state->crtc_h != new_state->crtc_h ||
104762306a36Sopenharmony_ci	    plane->state->fb != new_state->fb ||
104862306a36Sopenharmony_ci	    plane->state->fb == NULL)
104962306a36Sopenharmony_ci		return -EINVAL;
105062306a36Sopenharmony_ci
105162306a36Sopenharmony_ci	min_scale = (1 << 16) / 8;
105262306a36Sopenharmony_ci	max_scale = (8 << 16) / 1;
105362306a36Sopenharmony_ci
105462306a36Sopenharmony_ci	err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale,
105562306a36Sopenharmony_ci						  true, true);
105662306a36Sopenharmony_ci	if (err < 0)
105762306a36Sopenharmony_ci		return err;
105862306a36Sopenharmony_ci
105962306a36Sopenharmony_ci	if (new_state->visible != plane->state->visible)
106062306a36Sopenharmony_ci		return -EINVAL;
106162306a36Sopenharmony_ci
106262306a36Sopenharmony_ci	return 0;
106362306a36Sopenharmony_ci}
106462306a36Sopenharmony_ci
106562306a36Sopenharmony_cistatic void tegra_cursor_atomic_async_update(struct drm_plane *plane,
106662306a36Sopenharmony_ci					     struct drm_atomic_state *state)
106762306a36Sopenharmony_ci{
106862306a36Sopenharmony_ci	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
106962306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
107062306a36Sopenharmony_ci
107162306a36Sopenharmony_ci	plane->state->src_x = new_state->src_x;
107262306a36Sopenharmony_ci	plane->state->src_y = new_state->src_y;
107362306a36Sopenharmony_ci	plane->state->crtc_x = new_state->crtc_x;
107462306a36Sopenharmony_ci	plane->state->crtc_y = new_state->crtc_y;
107562306a36Sopenharmony_ci
107662306a36Sopenharmony_ci	if (new_state->visible) {
107762306a36Sopenharmony_ci		struct tegra_plane *p = to_tegra_plane(plane);
107862306a36Sopenharmony_ci		u32 value;
107962306a36Sopenharmony_ci
108062306a36Sopenharmony_ci		__tegra_cursor_atomic_update(plane, new_state);
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci		value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE;
108362306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
108462306a36Sopenharmony_ci		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci		value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ;
108762306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
108862306a36Sopenharmony_ci		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
108962306a36Sopenharmony_ci	}
109062306a36Sopenharmony_ci}
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_cistatic const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
109362306a36Sopenharmony_ci	.prepare_fb = tegra_plane_prepare_fb,
109462306a36Sopenharmony_ci	.cleanup_fb = tegra_plane_cleanup_fb,
109562306a36Sopenharmony_ci	.atomic_check = tegra_cursor_atomic_check,
109662306a36Sopenharmony_ci	.atomic_update = tegra_cursor_atomic_update,
109762306a36Sopenharmony_ci	.atomic_disable = tegra_cursor_atomic_disable,
109862306a36Sopenharmony_ci	.atomic_async_check = tegra_cursor_atomic_async_check,
109962306a36Sopenharmony_ci	.atomic_async_update = tegra_cursor_atomic_async_update,
110062306a36Sopenharmony_ci};
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_cistatic const uint64_t linear_modifiers[] = {
110362306a36Sopenharmony_ci	DRM_FORMAT_MOD_LINEAR,
110462306a36Sopenharmony_ci	DRM_FORMAT_MOD_INVALID
110562306a36Sopenharmony_ci};
110662306a36Sopenharmony_ci
110762306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
110862306a36Sopenharmony_ci						      struct tegra_dc *dc)
110962306a36Sopenharmony_ci{
111062306a36Sopenharmony_ci	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
111162306a36Sopenharmony_ci	struct tegra_plane *plane;
111262306a36Sopenharmony_ci	unsigned int num_formats;
111362306a36Sopenharmony_ci	const u32 *formats;
111462306a36Sopenharmony_ci	int err;
111562306a36Sopenharmony_ci
111662306a36Sopenharmony_ci	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
111762306a36Sopenharmony_ci	if (!plane)
111862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	/*
112162306a36Sopenharmony_ci	 * This index is kind of fake. The cursor isn't a regular plane, but
112262306a36Sopenharmony_ci	 * its update and activation request bits in DC_CMD_STATE_CONTROL do
112362306a36Sopenharmony_ci	 * use the same programming. Setting this fake index here allows the
112462306a36Sopenharmony_ci	 * code in tegra_add_plane_state() to do the right thing without the
112562306a36Sopenharmony_ci	 * need to special-casing the cursor plane.
112662306a36Sopenharmony_ci	 */
112762306a36Sopenharmony_ci	plane->index = 6;
112862306a36Sopenharmony_ci	plane->dc = dc;
112962306a36Sopenharmony_ci
113062306a36Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
113162306a36Sopenharmony_ci		num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
113262306a36Sopenharmony_ci		formats = tegra_legacy_cursor_plane_formats;
113362306a36Sopenharmony_ci
113462306a36Sopenharmony_ci		err = tegra_plane_interconnect_init(plane);
113562306a36Sopenharmony_ci		if (err) {
113662306a36Sopenharmony_ci			kfree(plane);
113762306a36Sopenharmony_ci			return ERR_PTR(err);
113862306a36Sopenharmony_ci		}
113962306a36Sopenharmony_ci	} else {
114062306a36Sopenharmony_ci		num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
114162306a36Sopenharmony_ci		formats = tegra_cursor_plane_formats;
114262306a36Sopenharmony_ci	}
114362306a36Sopenharmony_ci
114462306a36Sopenharmony_ci	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
114562306a36Sopenharmony_ci				       &tegra_plane_funcs, formats,
114662306a36Sopenharmony_ci				       num_formats, linear_modifiers,
114762306a36Sopenharmony_ci				       DRM_PLANE_TYPE_CURSOR, NULL);
114862306a36Sopenharmony_ci	if (err < 0) {
114962306a36Sopenharmony_ci		kfree(plane);
115062306a36Sopenharmony_ci		return ERR_PTR(err);
115162306a36Sopenharmony_ci	}
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
115462306a36Sopenharmony_ci	drm_plane_create_zpos_immutable_property(&plane->base, 255);
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	return &plane->base;
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cistatic const u32 tegra20_overlay_formats[] = {
116062306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
116162306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
116262306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
116362306a36Sopenharmony_ci	DRM_FORMAT_RGBA5551,
116462306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
116562306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
116662306a36Sopenharmony_ci	/* non-native formats */
116762306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
116862306a36Sopenharmony_ci	DRM_FORMAT_RGBX5551,
116962306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
117062306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
117162306a36Sopenharmony_ci	/* planar formats */
117262306a36Sopenharmony_ci	DRM_FORMAT_UYVY,
117362306a36Sopenharmony_ci	DRM_FORMAT_YUYV,
117462306a36Sopenharmony_ci	DRM_FORMAT_YUV420,
117562306a36Sopenharmony_ci	DRM_FORMAT_YUV422,
117662306a36Sopenharmony_ci};
117762306a36Sopenharmony_ci
117862306a36Sopenharmony_cistatic const u32 tegra114_overlay_formats[] = {
117962306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
118062306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
118162306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
118262306a36Sopenharmony_ci	DRM_FORMAT_RGBA5551,
118362306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
118462306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
118562306a36Sopenharmony_ci	/* new on Tegra114 */
118662306a36Sopenharmony_ci	DRM_FORMAT_ABGR4444,
118762306a36Sopenharmony_ci	DRM_FORMAT_ABGR1555,
118862306a36Sopenharmony_ci	DRM_FORMAT_BGRA5551,
118962306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
119062306a36Sopenharmony_ci	DRM_FORMAT_RGBX5551,
119162306a36Sopenharmony_ci	DRM_FORMAT_XBGR1555,
119262306a36Sopenharmony_ci	DRM_FORMAT_BGRX5551,
119362306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
119462306a36Sopenharmony_ci	DRM_FORMAT_BGRA8888,
119562306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
119662306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
119762306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
119862306a36Sopenharmony_ci	/* planar formats */
119962306a36Sopenharmony_ci	DRM_FORMAT_UYVY,
120062306a36Sopenharmony_ci	DRM_FORMAT_YUYV,
120162306a36Sopenharmony_ci	DRM_FORMAT_YUV420,
120262306a36Sopenharmony_ci	DRM_FORMAT_YUV422,
120362306a36Sopenharmony_ci	/* semi-planar formats */
120462306a36Sopenharmony_ci	DRM_FORMAT_NV12,
120562306a36Sopenharmony_ci	DRM_FORMAT_NV21,
120662306a36Sopenharmony_ci	DRM_FORMAT_NV16,
120762306a36Sopenharmony_ci	DRM_FORMAT_NV61,
120862306a36Sopenharmony_ci	DRM_FORMAT_NV24,
120962306a36Sopenharmony_ci	DRM_FORMAT_NV42,
121062306a36Sopenharmony_ci};
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_cistatic const u32 tegra124_overlay_formats[] = {
121362306a36Sopenharmony_ci	DRM_FORMAT_ARGB4444,
121462306a36Sopenharmony_ci	DRM_FORMAT_ARGB1555,
121562306a36Sopenharmony_ci	DRM_FORMAT_RGB565,
121662306a36Sopenharmony_ci	DRM_FORMAT_RGBA5551,
121762306a36Sopenharmony_ci	DRM_FORMAT_ABGR8888,
121862306a36Sopenharmony_ci	DRM_FORMAT_ARGB8888,
121962306a36Sopenharmony_ci	/* new on Tegra114 */
122062306a36Sopenharmony_ci	DRM_FORMAT_ABGR4444,
122162306a36Sopenharmony_ci	DRM_FORMAT_ABGR1555,
122262306a36Sopenharmony_ci	DRM_FORMAT_BGRA5551,
122362306a36Sopenharmony_ci	DRM_FORMAT_XRGB1555,
122462306a36Sopenharmony_ci	DRM_FORMAT_RGBX5551,
122562306a36Sopenharmony_ci	DRM_FORMAT_XBGR1555,
122662306a36Sopenharmony_ci	DRM_FORMAT_BGRX5551,
122762306a36Sopenharmony_ci	DRM_FORMAT_BGR565,
122862306a36Sopenharmony_ci	DRM_FORMAT_BGRA8888,
122962306a36Sopenharmony_ci	DRM_FORMAT_RGBA8888,
123062306a36Sopenharmony_ci	DRM_FORMAT_XRGB8888,
123162306a36Sopenharmony_ci	DRM_FORMAT_XBGR8888,
123262306a36Sopenharmony_ci	/* new on Tegra124 */
123362306a36Sopenharmony_ci	DRM_FORMAT_RGBX8888,
123462306a36Sopenharmony_ci	DRM_FORMAT_BGRX8888,
123562306a36Sopenharmony_ci	/* planar formats */
123662306a36Sopenharmony_ci	DRM_FORMAT_UYVY,
123762306a36Sopenharmony_ci	DRM_FORMAT_YUYV,
123862306a36Sopenharmony_ci	DRM_FORMAT_YVYU,
123962306a36Sopenharmony_ci	DRM_FORMAT_VYUY,
124062306a36Sopenharmony_ci	DRM_FORMAT_YUV420, /* YU12 */
124162306a36Sopenharmony_ci	DRM_FORMAT_YUV422, /* YU16 */
124262306a36Sopenharmony_ci	DRM_FORMAT_YUV444, /* YU24 */
124362306a36Sopenharmony_ci	/* semi-planar formats */
124462306a36Sopenharmony_ci	DRM_FORMAT_NV12,
124562306a36Sopenharmony_ci	DRM_FORMAT_NV21,
124662306a36Sopenharmony_ci	DRM_FORMAT_NV16,
124762306a36Sopenharmony_ci	DRM_FORMAT_NV61,
124862306a36Sopenharmony_ci	DRM_FORMAT_NV24,
124962306a36Sopenharmony_ci	DRM_FORMAT_NV42,
125062306a36Sopenharmony_ci};
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
125362306a36Sopenharmony_ci						       struct tegra_dc *dc,
125462306a36Sopenharmony_ci						       unsigned int index,
125562306a36Sopenharmony_ci						       bool cursor)
125662306a36Sopenharmony_ci{
125762306a36Sopenharmony_ci	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
125862306a36Sopenharmony_ci	struct tegra_plane *plane;
125962306a36Sopenharmony_ci	unsigned int num_formats;
126062306a36Sopenharmony_ci	enum drm_plane_type type;
126162306a36Sopenharmony_ci	const u32 *formats;
126262306a36Sopenharmony_ci	int err;
126362306a36Sopenharmony_ci
126462306a36Sopenharmony_ci	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
126562306a36Sopenharmony_ci	if (!plane)
126662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
126762306a36Sopenharmony_ci
126862306a36Sopenharmony_ci	plane->offset = 0xa00 + 0x200 * index;
126962306a36Sopenharmony_ci	plane->index = index;
127062306a36Sopenharmony_ci	plane->dc = dc;
127162306a36Sopenharmony_ci
127262306a36Sopenharmony_ci	num_formats = dc->soc->num_overlay_formats;
127362306a36Sopenharmony_ci	formats = dc->soc->overlay_formats;
127462306a36Sopenharmony_ci
127562306a36Sopenharmony_ci	err = tegra_plane_interconnect_init(plane);
127662306a36Sopenharmony_ci	if (err) {
127762306a36Sopenharmony_ci		kfree(plane);
127862306a36Sopenharmony_ci		return ERR_PTR(err);
127962306a36Sopenharmony_ci	}
128062306a36Sopenharmony_ci
128162306a36Sopenharmony_ci	if (!cursor)
128262306a36Sopenharmony_ci		type = DRM_PLANE_TYPE_OVERLAY;
128362306a36Sopenharmony_ci	else
128462306a36Sopenharmony_ci		type = DRM_PLANE_TYPE_CURSOR;
128562306a36Sopenharmony_ci
128662306a36Sopenharmony_ci	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
128762306a36Sopenharmony_ci				       &tegra_plane_funcs, formats,
128862306a36Sopenharmony_ci				       num_formats, linear_modifiers,
128962306a36Sopenharmony_ci				       type, NULL);
129062306a36Sopenharmony_ci	if (err < 0) {
129162306a36Sopenharmony_ci		kfree(plane);
129262306a36Sopenharmony_ci		return ERR_PTR(err);
129362306a36Sopenharmony_ci	}
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
129662306a36Sopenharmony_ci	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
129762306a36Sopenharmony_ci
129862306a36Sopenharmony_ci	err = drm_plane_create_rotation_property(&plane->base,
129962306a36Sopenharmony_ci						 DRM_MODE_ROTATE_0,
130062306a36Sopenharmony_ci						 DRM_MODE_ROTATE_0 |
130162306a36Sopenharmony_ci						 DRM_MODE_ROTATE_180 |
130262306a36Sopenharmony_ci						 DRM_MODE_REFLECT_X |
130362306a36Sopenharmony_ci						 DRM_MODE_REFLECT_Y);
130462306a36Sopenharmony_ci	if (err < 0)
130562306a36Sopenharmony_ci		dev_err(dc->dev, "failed to create rotation property: %d\n",
130662306a36Sopenharmony_ci			err);
130762306a36Sopenharmony_ci
130862306a36Sopenharmony_ci	return &plane->base;
130962306a36Sopenharmony_ci}
131062306a36Sopenharmony_ci
131162306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
131262306a36Sopenharmony_ci						    struct tegra_dc *dc)
131362306a36Sopenharmony_ci{
131462306a36Sopenharmony_ci	struct drm_plane *plane, *primary = NULL;
131562306a36Sopenharmony_ci	unsigned int i, j;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	for (i = 0; i < dc->soc->num_wgrps; i++) {
131862306a36Sopenharmony_ci		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci		if (wgrp->dc == dc->pipe) {
132162306a36Sopenharmony_ci			for (j = 0; j < wgrp->num_windows; j++) {
132262306a36Sopenharmony_ci				unsigned int index = wgrp->windows[j];
132362306a36Sopenharmony_ci
132462306a36Sopenharmony_ci				plane = tegra_shared_plane_create(drm, dc,
132562306a36Sopenharmony_ci								  wgrp->index,
132662306a36Sopenharmony_ci								  index);
132762306a36Sopenharmony_ci				if (IS_ERR(plane))
132862306a36Sopenharmony_ci					return plane;
132962306a36Sopenharmony_ci
133062306a36Sopenharmony_ci				/*
133162306a36Sopenharmony_ci				 * Choose the first shared plane owned by this
133262306a36Sopenharmony_ci				 * head as the primary plane.
133362306a36Sopenharmony_ci				 */
133462306a36Sopenharmony_ci				if (!primary) {
133562306a36Sopenharmony_ci					plane->type = DRM_PLANE_TYPE_PRIMARY;
133662306a36Sopenharmony_ci					primary = plane;
133762306a36Sopenharmony_ci				}
133862306a36Sopenharmony_ci			}
133962306a36Sopenharmony_ci		}
134062306a36Sopenharmony_ci	}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ci	return primary;
134362306a36Sopenharmony_ci}
134462306a36Sopenharmony_ci
134562306a36Sopenharmony_cistatic struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
134662306a36Sopenharmony_ci					     struct tegra_dc *dc)
134762306a36Sopenharmony_ci{
134862306a36Sopenharmony_ci	struct drm_plane *planes[2], *primary;
134962306a36Sopenharmony_ci	unsigned int planes_num;
135062306a36Sopenharmony_ci	unsigned int i;
135162306a36Sopenharmony_ci	int err;
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	primary = tegra_primary_plane_create(drm, dc);
135462306a36Sopenharmony_ci	if (IS_ERR(primary))
135562306a36Sopenharmony_ci		return primary;
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_ci	if (dc->soc->supports_cursor)
135862306a36Sopenharmony_ci		planes_num = 2;
135962306a36Sopenharmony_ci	else
136062306a36Sopenharmony_ci		planes_num = 1;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	for (i = 0; i < planes_num; i++) {
136362306a36Sopenharmony_ci		planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i,
136462306a36Sopenharmony_ci							  false);
136562306a36Sopenharmony_ci		if (IS_ERR(planes[i])) {
136662306a36Sopenharmony_ci			err = PTR_ERR(planes[i]);
136762306a36Sopenharmony_ci
136862306a36Sopenharmony_ci			while (i--)
136962306a36Sopenharmony_ci				planes[i]->funcs->destroy(planes[i]);
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci			primary->funcs->destroy(primary);
137262306a36Sopenharmony_ci			return ERR_PTR(err);
137362306a36Sopenharmony_ci		}
137462306a36Sopenharmony_ci	}
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	return primary;
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic void tegra_dc_destroy(struct drm_crtc *crtc)
138062306a36Sopenharmony_ci{
138162306a36Sopenharmony_ci	drm_crtc_cleanup(crtc);
138262306a36Sopenharmony_ci}
138362306a36Sopenharmony_ci
138462306a36Sopenharmony_cistatic void tegra_crtc_reset(struct drm_crtc *crtc)
138562306a36Sopenharmony_ci{
138662306a36Sopenharmony_ci	struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	if (crtc->state)
138962306a36Sopenharmony_ci		tegra_crtc_atomic_destroy_state(crtc, crtc->state);
139062306a36Sopenharmony_ci
139162306a36Sopenharmony_ci	__drm_atomic_helper_crtc_reset(crtc, &state->base);
139262306a36Sopenharmony_ci}
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_cistatic struct drm_crtc_state *
139562306a36Sopenharmony_citegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
139662306a36Sopenharmony_ci{
139762306a36Sopenharmony_ci	struct tegra_dc_state *state = to_dc_state(crtc->state);
139862306a36Sopenharmony_ci	struct tegra_dc_state *copy;
139962306a36Sopenharmony_ci
140062306a36Sopenharmony_ci	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
140162306a36Sopenharmony_ci	if (!copy)
140262306a36Sopenharmony_ci		return NULL;
140362306a36Sopenharmony_ci
140462306a36Sopenharmony_ci	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
140562306a36Sopenharmony_ci	copy->clk = state->clk;
140662306a36Sopenharmony_ci	copy->pclk = state->pclk;
140762306a36Sopenharmony_ci	copy->div = state->div;
140862306a36Sopenharmony_ci	copy->planes = state->planes;
140962306a36Sopenharmony_ci
141062306a36Sopenharmony_ci	return &copy->base;
141162306a36Sopenharmony_ci}
141262306a36Sopenharmony_ci
141362306a36Sopenharmony_cistatic void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
141462306a36Sopenharmony_ci					    struct drm_crtc_state *state)
141562306a36Sopenharmony_ci{
141662306a36Sopenharmony_ci	__drm_atomic_helper_crtc_destroy_state(state);
141762306a36Sopenharmony_ci	kfree(state);
141862306a36Sopenharmony_ci}
141962306a36Sopenharmony_ci
142062306a36Sopenharmony_ci#define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
142162306a36Sopenharmony_ci
142262306a36Sopenharmony_cistatic const struct debugfs_reg32 tegra_dc_regs[] = {
142362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT),
142462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL),
142562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR),
142662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT),
142762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL),
142862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR),
142962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT),
143062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL),
143162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR),
143262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT),
143362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL),
143462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR),
143562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC),
143662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0),
143762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND),
143862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE),
143962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL),
144062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_INT_STATUS),
144162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_INT_MASK),
144262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_INT_ENABLE),
144362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_INT_TYPE),
144462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_INT_POLARITY),
144562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1),
144662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2),
144762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3),
144862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_STATE_ACCESS),
144962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_STATE_CONTROL),
145062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER),
145162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL),
145262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_CRC_CONTROL),
145362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM),
145462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)),
145562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)),
145662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)),
145762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)),
145862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)),
145962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)),
146062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)),
146162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)),
146262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)),
146362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)),
146462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)),
146562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)),
146662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)),
146762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)),
146862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)),
146962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)),
147062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)),
147162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)),
147262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)),
147362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)),
147462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)),
147562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)),
147662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)),
147762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
147862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
147962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
148062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
148162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
148262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
148362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
148462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_SPI_CONTROL),
148562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
148662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
148762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD),
148862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_HSPI_CS_DC),
148962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A),
149062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B),
149162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_GPIO_CTRL),
149262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER),
149362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED),
149462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0),
149562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1),
149662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS),
149762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY),
149862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER),
149962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS),
150062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_REF_TO_SYNC),
150162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SYNC_WIDTH),
150262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_BACK_PORCH),
150362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_ACTIVE),
150462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_FRONT_PORCH),
150562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL),
150662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A),
150762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B),
150862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C),
150962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D),
151062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL),
151162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A),
151262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B),
151362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C),
151462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D),
151562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL),
151662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A),
151762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B),
151862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C),
151962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D),
152062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL),
152162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A),
152262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B),
152362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C),
152462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL),
152562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A),
152662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B),
152762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C),
152862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL),
152962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A),
153062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL),
153162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A),
153262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_M0_CONTROL),
153362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_M1_CONTROL),
153462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DI_CONTROL),
153562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_PP_CONTROL),
153662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_PP_SELECT_A),
153762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_PP_SELECT_B),
153862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_PP_SELECT_C),
153962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_PP_SELECT_D),
154062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL),
154162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL),
154262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL),
154362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS),
154462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS),
154562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS),
154662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS),
154762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_BORDER_COLOR),
154862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER),
154962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER),
155062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER),
155162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER),
155262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND),
155362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND),
155462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR),
155562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS),
155662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION),
155762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS),
155862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL),
155962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A),
156062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B),
156162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C),
156262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D),
156362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL),
156462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST),
156562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST),
156662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST),
156762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST),
156862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL),
156962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL),
157062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_CONTROL),
157162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF),
157262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(0)),
157362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(1)),
157462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(2)),
157562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(3)),
157662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(4)),
157762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(5)),
157862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(6)),
157962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(7)),
158062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_LUT(8)),
158162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL),
158262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT),
158362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)),
158462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)),
158562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)),
158662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)),
158762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)),
158862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)),
158962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)),
159062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)),
159162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)),
159262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)),
159362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)),
159462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)),
159562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL),
159662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES),
159762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES),
159862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI),
159962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL),
160062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_WIN_OPTIONS),
160162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BYTE_SWAP),
160262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL),
160362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_COLOR_DEPTH),
160462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_POSITION),
160562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_SIZE),
160662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE),
160762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA),
160862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA),
160962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_DDA_INC),
161062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_LINE_STRIDE),
161162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BUF_STRIDE),
161262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE),
161362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE),
161462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_DV_CONTROL),
161562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BLEND_NOKEY),
161662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BLEND_1WIN),
161762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X),
161862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y),
161962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY),
162062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL),
162162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_START_ADDR),
162262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS),
162362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U),
162462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS),
162562306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V),
162662306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS),
162762306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET),
162862306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS),
162962306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET),
163062306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS),
163162306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS),
163262306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS),
163362306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS),
163462306a36Sopenharmony_ci	DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS),
163562306a36Sopenharmony_ci};
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_cistatic int tegra_dc_show_regs(struct seq_file *s, void *data)
163862306a36Sopenharmony_ci{
163962306a36Sopenharmony_ci	struct drm_info_node *node = s->private;
164062306a36Sopenharmony_ci	struct tegra_dc *dc = node->info_ent->data;
164162306a36Sopenharmony_ci	unsigned int i;
164262306a36Sopenharmony_ci	int err = 0;
164362306a36Sopenharmony_ci
164462306a36Sopenharmony_ci	drm_modeset_lock(&dc->base.mutex, NULL);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	if (!dc->base.state->active) {
164762306a36Sopenharmony_ci		err = -EBUSY;
164862306a36Sopenharmony_ci		goto unlock;
164962306a36Sopenharmony_ci	}
165062306a36Sopenharmony_ci
165162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) {
165262306a36Sopenharmony_ci		unsigned int offset = tegra_dc_regs[i].offset;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci		seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name,
165562306a36Sopenharmony_ci			   offset, tegra_dc_readl(dc, offset));
165662306a36Sopenharmony_ci	}
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ciunlock:
165962306a36Sopenharmony_ci	drm_modeset_unlock(&dc->base.mutex);
166062306a36Sopenharmony_ci	return err;
166162306a36Sopenharmony_ci}
166262306a36Sopenharmony_ci
166362306a36Sopenharmony_cistatic int tegra_dc_show_crc(struct seq_file *s, void *data)
166462306a36Sopenharmony_ci{
166562306a36Sopenharmony_ci	struct drm_info_node *node = s->private;
166662306a36Sopenharmony_ci	struct tegra_dc *dc = node->info_ent->data;
166762306a36Sopenharmony_ci	int err = 0;
166862306a36Sopenharmony_ci	u32 value;
166962306a36Sopenharmony_ci
167062306a36Sopenharmony_ci	drm_modeset_lock(&dc->base.mutex, NULL);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_ci	if (!dc->base.state->active) {
167362306a36Sopenharmony_ci		err = -EBUSY;
167462306a36Sopenharmony_ci		goto unlock;
167562306a36Sopenharmony_ci	}
167662306a36Sopenharmony_ci
167762306a36Sopenharmony_ci	value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
167862306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
167962306a36Sopenharmony_ci	tegra_dc_commit(dc);
168062306a36Sopenharmony_ci
168162306a36Sopenharmony_ci	drm_crtc_wait_one_vblank(&dc->base);
168262306a36Sopenharmony_ci	drm_crtc_wait_one_vblank(&dc->base);
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM);
168562306a36Sopenharmony_ci	seq_printf(s, "%08x\n", value);
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
168862306a36Sopenharmony_ci
168962306a36Sopenharmony_ciunlock:
169062306a36Sopenharmony_ci	drm_modeset_unlock(&dc->base.mutex);
169162306a36Sopenharmony_ci	return err;
169262306a36Sopenharmony_ci}
169362306a36Sopenharmony_ci
169462306a36Sopenharmony_cistatic int tegra_dc_show_stats(struct seq_file *s, void *data)
169562306a36Sopenharmony_ci{
169662306a36Sopenharmony_ci	struct drm_info_node *node = s->private;
169762306a36Sopenharmony_ci	struct tegra_dc *dc = node->info_ent->data;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	seq_printf(s, "frames: %lu\n", dc->stats.frames);
170062306a36Sopenharmony_ci	seq_printf(s, "vblank: %lu\n", dc->stats.vblank);
170162306a36Sopenharmony_ci	seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
170262306a36Sopenharmony_ci	seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
170362306a36Sopenharmony_ci
170462306a36Sopenharmony_ci	seq_printf(s, "frames total: %lu\n", dc->stats.frames_total);
170562306a36Sopenharmony_ci	seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total);
170662306a36Sopenharmony_ci	seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total);
170762306a36Sopenharmony_ci	seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total);
170862306a36Sopenharmony_ci
170962306a36Sopenharmony_ci	return 0;
171062306a36Sopenharmony_ci}
171162306a36Sopenharmony_ci
171262306a36Sopenharmony_cistatic struct drm_info_list debugfs_files[] = {
171362306a36Sopenharmony_ci	{ "regs", tegra_dc_show_regs, 0, NULL },
171462306a36Sopenharmony_ci	{ "crc", tegra_dc_show_crc, 0, NULL },
171562306a36Sopenharmony_ci	{ "stats", tegra_dc_show_stats, 0, NULL },
171662306a36Sopenharmony_ci};
171762306a36Sopenharmony_ci
171862306a36Sopenharmony_cistatic int tegra_dc_late_register(struct drm_crtc *crtc)
171962306a36Sopenharmony_ci{
172062306a36Sopenharmony_ci	unsigned int i, count = ARRAY_SIZE(debugfs_files);
172162306a36Sopenharmony_ci	struct drm_minor *minor = crtc->dev->primary;
172262306a36Sopenharmony_ci	struct dentry *root;
172362306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
172662306a36Sopenharmony_ci	root = crtc->debugfs_entry;
172762306a36Sopenharmony_ci#else
172862306a36Sopenharmony_ci	root = NULL;
172962306a36Sopenharmony_ci#endif
173062306a36Sopenharmony_ci
173162306a36Sopenharmony_ci	dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
173262306a36Sopenharmony_ci				    GFP_KERNEL);
173362306a36Sopenharmony_ci	if (!dc->debugfs_files)
173462306a36Sopenharmony_ci		return -ENOMEM;
173562306a36Sopenharmony_ci
173662306a36Sopenharmony_ci	for (i = 0; i < count; i++)
173762306a36Sopenharmony_ci		dc->debugfs_files[i].data = dc;
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	drm_debugfs_create_files(dc->debugfs_files, count, root, minor);
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	return 0;
174262306a36Sopenharmony_ci}
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_cistatic void tegra_dc_early_unregister(struct drm_crtc *crtc)
174562306a36Sopenharmony_ci{
174662306a36Sopenharmony_ci	unsigned int count = ARRAY_SIZE(debugfs_files);
174762306a36Sopenharmony_ci	struct drm_minor *minor = crtc->dev->primary;
174862306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_ci	drm_debugfs_remove_files(dc->debugfs_files, count, minor);
175162306a36Sopenharmony_ci	kfree(dc->debugfs_files);
175262306a36Sopenharmony_ci	dc->debugfs_files = NULL;
175362306a36Sopenharmony_ci}
175462306a36Sopenharmony_ci
175562306a36Sopenharmony_cistatic u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
175662306a36Sopenharmony_ci{
175762306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
175862306a36Sopenharmony_ci
175962306a36Sopenharmony_ci	/* XXX vblank syncpoints don't work with nvdisplay yet */
176062306a36Sopenharmony_ci	if (dc->syncpt && !dc->soc->has_nvdisplay)
176162306a36Sopenharmony_ci		return host1x_syncpt_read(dc->syncpt);
176262306a36Sopenharmony_ci
176362306a36Sopenharmony_ci	/* fallback to software emulated VBLANK counter */
176462306a36Sopenharmony_ci	return (u32)drm_crtc_vblank_count(&dc->base);
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_cistatic int tegra_dc_enable_vblank(struct drm_crtc *crtc)
176862306a36Sopenharmony_ci{
176962306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
177062306a36Sopenharmony_ci	u32 value;
177162306a36Sopenharmony_ci
177262306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
177362306a36Sopenharmony_ci	value |= VBLANK_INT;
177462306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
177562306a36Sopenharmony_ci
177662306a36Sopenharmony_ci	return 0;
177762306a36Sopenharmony_ci}
177862306a36Sopenharmony_ci
177962306a36Sopenharmony_cistatic void tegra_dc_disable_vblank(struct drm_crtc *crtc)
178062306a36Sopenharmony_ci{
178162306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
178262306a36Sopenharmony_ci	u32 value;
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
178562306a36Sopenharmony_ci	value &= ~VBLANK_INT;
178662306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
178762306a36Sopenharmony_ci}
178862306a36Sopenharmony_ci
178962306a36Sopenharmony_cistatic const struct drm_crtc_funcs tegra_crtc_funcs = {
179062306a36Sopenharmony_ci	.page_flip = drm_atomic_helper_page_flip,
179162306a36Sopenharmony_ci	.set_config = drm_atomic_helper_set_config,
179262306a36Sopenharmony_ci	.destroy = tegra_dc_destroy,
179362306a36Sopenharmony_ci	.reset = tegra_crtc_reset,
179462306a36Sopenharmony_ci	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
179562306a36Sopenharmony_ci	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
179662306a36Sopenharmony_ci	.late_register = tegra_dc_late_register,
179762306a36Sopenharmony_ci	.early_unregister = tegra_dc_early_unregister,
179862306a36Sopenharmony_ci	.get_vblank_counter = tegra_dc_get_vblank_counter,
179962306a36Sopenharmony_ci	.enable_vblank = tegra_dc_enable_vblank,
180062306a36Sopenharmony_ci	.disable_vblank = tegra_dc_disable_vblank,
180162306a36Sopenharmony_ci};
180262306a36Sopenharmony_ci
180362306a36Sopenharmony_cistatic int tegra_dc_set_timings(struct tegra_dc *dc,
180462306a36Sopenharmony_ci				struct drm_display_mode *mode)
180562306a36Sopenharmony_ci{
180662306a36Sopenharmony_ci	unsigned int h_ref_to_sync = 1;
180762306a36Sopenharmony_ci	unsigned int v_ref_to_sync = 1;
180862306a36Sopenharmony_ci	unsigned long value;
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
181162306a36Sopenharmony_ci		tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci		value = (v_ref_to_sync << 16) | h_ref_to_sync;
181462306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	value = ((mode->vsync_end - mode->vsync_start) << 16) |
181862306a36Sopenharmony_ci		((mode->hsync_end - mode->hsync_start) <<  0);
181962306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
182062306a36Sopenharmony_ci
182162306a36Sopenharmony_ci	value = ((mode->vtotal - mode->vsync_end) << 16) |
182262306a36Sopenharmony_ci		((mode->htotal - mode->hsync_end) <<  0);
182362306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ci	value = ((mode->vsync_start - mode->vdisplay) << 16) |
182662306a36Sopenharmony_ci		((mode->hsync_start - mode->hdisplay) <<  0);
182762306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
182862306a36Sopenharmony_ci
182962306a36Sopenharmony_ci	value = (mode->vdisplay << 16) | mode->hdisplay;
183062306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_ci	return 0;
183362306a36Sopenharmony_ci}
183462306a36Sopenharmony_ci
183562306a36Sopenharmony_ci/**
183662306a36Sopenharmony_ci * tegra_dc_state_setup_clock - check clock settings and store them in atomic
183762306a36Sopenharmony_ci *     state
183862306a36Sopenharmony_ci * @dc: display controller
183962306a36Sopenharmony_ci * @crtc_state: CRTC atomic state
184062306a36Sopenharmony_ci * @clk: parent clock for display controller
184162306a36Sopenharmony_ci * @pclk: pixel clock
184262306a36Sopenharmony_ci * @div: shift clock divider
184362306a36Sopenharmony_ci *
184462306a36Sopenharmony_ci * Returns:
184562306a36Sopenharmony_ci * 0 on success or a negative error-code on failure.
184662306a36Sopenharmony_ci */
184762306a36Sopenharmony_ciint tegra_dc_state_setup_clock(struct tegra_dc *dc,
184862306a36Sopenharmony_ci			       struct drm_crtc_state *crtc_state,
184962306a36Sopenharmony_ci			       struct clk *clk, unsigned long pclk,
185062306a36Sopenharmony_ci			       unsigned int div)
185162306a36Sopenharmony_ci{
185262306a36Sopenharmony_ci	struct tegra_dc_state *state = to_dc_state(crtc_state);
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_ci	if (!clk_has_parent(dc->clk, clk))
185562306a36Sopenharmony_ci		return -EINVAL;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	state->clk = clk;
185862306a36Sopenharmony_ci	state->pclk = pclk;
185962306a36Sopenharmony_ci	state->div = div;
186062306a36Sopenharmony_ci
186162306a36Sopenharmony_ci	return 0;
186262306a36Sopenharmony_ci}
186362306a36Sopenharmony_ci
186462306a36Sopenharmony_cistatic void tegra_dc_update_voltage_state(struct tegra_dc *dc,
186562306a36Sopenharmony_ci					  struct tegra_dc_state *state)
186662306a36Sopenharmony_ci{
186762306a36Sopenharmony_ci	unsigned long rate, pstate;
186862306a36Sopenharmony_ci	struct dev_pm_opp *opp;
186962306a36Sopenharmony_ci	int err;
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ci	if (!dc->has_opp_table)
187262306a36Sopenharmony_ci		return;
187362306a36Sopenharmony_ci
187462306a36Sopenharmony_ci	/* calculate actual pixel clock rate which depends on internal divider */
187562306a36Sopenharmony_ci	rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
187662306a36Sopenharmony_ci
187762306a36Sopenharmony_ci	/* find suitable OPP for the rate */
187862306a36Sopenharmony_ci	opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
187962306a36Sopenharmony_ci
188062306a36Sopenharmony_ci	/*
188162306a36Sopenharmony_ci	 * Very high resolution modes may results in a clock rate that is
188262306a36Sopenharmony_ci	 * above the characterized maximum. In this case it's okay to fall
188362306a36Sopenharmony_ci	 * back to the characterized maximum.
188462306a36Sopenharmony_ci	 */
188562306a36Sopenharmony_ci	if (opp == ERR_PTR(-ERANGE))
188662306a36Sopenharmony_ci		opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
188762306a36Sopenharmony_ci
188862306a36Sopenharmony_ci	if (IS_ERR(opp)) {
188962306a36Sopenharmony_ci		dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
189062306a36Sopenharmony_ci			rate, opp);
189162306a36Sopenharmony_ci		return;
189262306a36Sopenharmony_ci	}
189362306a36Sopenharmony_ci
189462306a36Sopenharmony_ci	pstate = dev_pm_opp_get_required_pstate(opp, 0);
189562306a36Sopenharmony_ci	dev_pm_opp_put(opp);
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	/*
189862306a36Sopenharmony_ci	 * The minimum core voltage depends on the pixel clock rate (which
189962306a36Sopenharmony_ci	 * depends on internal clock divider of the CRTC) and not on the
190062306a36Sopenharmony_ci	 * rate of the display controller clock. This is why we're not using
190162306a36Sopenharmony_ci	 * dev_pm_opp_set_rate() API and instead controlling the power domain
190262306a36Sopenharmony_ci	 * directly.
190362306a36Sopenharmony_ci	 */
190462306a36Sopenharmony_ci	err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
190562306a36Sopenharmony_ci	if (err)
190662306a36Sopenharmony_ci		dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
190762306a36Sopenharmony_ci			pstate, err);
190862306a36Sopenharmony_ci}
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_cistatic void tegra_dc_set_clock_rate(struct tegra_dc *dc,
191162306a36Sopenharmony_ci				    struct tegra_dc_state *state)
191262306a36Sopenharmony_ci{
191362306a36Sopenharmony_ci	int err;
191462306a36Sopenharmony_ci
191562306a36Sopenharmony_ci	err = clk_set_parent(dc->clk, state->clk);
191662306a36Sopenharmony_ci	if (err < 0)
191762306a36Sopenharmony_ci		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci	/*
192062306a36Sopenharmony_ci	 * Outputs may not want to change the parent clock rate. This is only
192162306a36Sopenharmony_ci	 * relevant to Tegra20 where only a single display PLL is available.
192262306a36Sopenharmony_ci	 * Since that PLL would typically be used for HDMI, an internal LVDS
192362306a36Sopenharmony_ci	 * panel would need to be driven by some other clock such as PLL_P
192462306a36Sopenharmony_ci	 * which is shared with other peripherals. Changing the clock rate
192562306a36Sopenharmony_ci	 * should therefore be avoided.
192662306a36Sopenharmony_ci	 */
192762306a36Sopenharmony_ci	if (state->pclk > 0) {
192862306a36Sopenharmony_ci		err = clk_set_rate(state->clk, state->pclk);
192962306a36Sopenharmony_ci		if (err < 0)
193062306a36Sopenharmony_ci			dev_err(dc->dev,
193162306a36Sopenharmony_ci				"failed to set clock rate to %lu Hz\n",
193262306a36Sopenharmony_ci				state->pclk);
193362306a36Sopenharmony_ci
193462306a36Sopenharmony_ci		err = clk_set_rate(dc->clk, state->pclk);
193562306a36Sopenharmony_ci		if (err < 0)
193662306a36Sopenharmony_ci			dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n",
193762306a36Sopenharmony_ci				dc->clk, state->pclk, err);
193862306a36Sopenharmony_ci	}
193962306a36Sopenharmony_ci
194062306a36Sopenharmony_ci	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
194162306a36Sopenharmony_ci		      state->div);
194262306a36Sopenharmony_ci	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_ci	tegra_dc_update_voltage_state(dc, state);
194562306a36Sopenharmony_ci}
194662306a36Sopenharmony_ci
194762306a36Sopenharmony_cistatic void tegra_dc_stop(struct tegra_dc *dc)
194862306a36Sopenharmony_ci{
194962306a36Sopenharmony_ci	u32 value;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	/* stop the display controller */
195262306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
195362306a36Sopenharmony_ci	value &= ~DISP_CTRL_MODE_MASK;
195462306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
195562306a36Sopenharmony_ci
195662306a36Sopenharmony_ci	tegra_dc_commit(dc);
195762306a36Sopenharmony_ci}
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_cistatic bool tegra_dc_idle(struct tegra_dc *dc)
196062306a36Sopenharmony_ci{
196162306a36Sopenharmony_ci	u32 value;
196262306a36Sopenharmony_ci
196362306a36Sopenharmony_ci	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
196462306a36Sopenharmony_ci
196562306a36Sopenharmony_ci	return (value & DISP_CTRL_MODE_MASK) == 0;
196662306a36Sopenharmony_ci}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_cistatic int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
196962306a36Sopenharmony_ci{
197062306a36Sopenharmony_ci	timeout = jiffies + msecs_to_jiffies(timeout);
197162306a36Sopenharmony_ci
197262306a36Sopenharmony_ci	while (time_before(jiffies, timeout)) {
197362306a36Sopenharmony_ci		if (tegra_dc_idle(dc))
197462306a36Sopenharmony_ci			return 0;
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci		usleep_range(1000, 2000);
197762306a36Sopenharmony_ci	}
197862306a36Sopenharmony_ci
197962306a36Sopenharmony_ci	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
198062306a36Sopenharmony_ci	return -ETIMEDOUT;
198162306a36Sopenharmony_ci}
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_cistatic void
198462306a36Sopenharmony_citegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc,
198562306a36Sopenharmony_ci				   struct drm_atomic_state *state,
198662306a36Sopenharmony_ci				   bool prepare_bandwidth_transition)
198762306a36Sopenharmony_ci{
198862306a36Sopenharmony_ci	const struct tegra_plane_state *old_tegra_state, *new_tegra_state;
198962306a36Sopenharmony_ci	u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw;
199062306a36Sopenharmony_ci	const struct drm_plane_state *old_plane_state;
199162306a36Sopenharmony_ci	const struct drm_crtc_state *old_crtc_state;
199262306a36Sopenharmony_ci	struct tegra_dc_window window, old_window;
199362306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
199462306a36Sopenharmony_ci	struct tegra_plane *tegra;
199562306a36Sopenharmony_ci	struct drm_plane *plane;
199662306a36Sopenharmony_ci
199762306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay)
199862306a36Sopenharmony_ci		return;
199962306a36Sopenharmony_ci
200062306a36Sopenharmony_ci	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
200162306a36Sopenharmony_ci
200262306a36Sopenharmony_ci	if (!crtc->state->active) {
200362306a36Sopenharmony_ci		if (!old_crtc_state->active)
200462306a36Sopenharmony_ci			return;
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_ci		/*
200762306a36Sopenharmony_ci		 * When CRTC is disabled on DPMS, the state of attached planes
200862306a36Sopenharmony_ci		 * is kept unchanged. Hence we need to enforce removal of the
200962306a36Sopenharmony_ci		 * bandwidths from the ICC paths.
201062306a36Sopenharmony_ci		 */
201162306a36Sopenharmony_ci		drm_atomic_crtc_for_each_plane(plane, crtc) {
201262306a36Sopenharmony_ci			tegra = to_tegra_plane(plane);
201362306a36Sopenharmony_ci
201462306a36Sopenharmony_ci			icc_set_bw(tegra->icc_mem, 0, 0);
201562306a36Sopenharmony_ci			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
201662306a36Sopenharmony_ci		}
201762306a36Sopenharmony_ci
201862306a36Sopenharmony_ci		return;
201962306a36Sopenharmony_ci	}
202062306a36Sopenharmony_ci
202162306a36Sopenharmony_ci	for_each_old_plane_in_state(old_crtc_state->state, plane,
202262306a36Sopenharmony_ci				    old_plane_state, i) {
202362306a36Sopenharmony_ci		old_tegra_state = to_const_tegra_plane_state(old_plane_state);
202462306a36Sopenharmony_ci		new_tegra_state = to_const_tegra_plane_state(plane->state);
202562306a36Sopenharmony_ci		tegra = to_tegra_plane(plane);
202662306a36Sopenharmony_ci
202762306a36Sopenharmony_ci		/*
202862306a36Sopenharmony_ci		 * We're iterating over the global atomic state and it contains
202962306a36Sopenharmony_ci		 * planes from another CRTC, hence we need to filter out the
203062306a36Sopenharmony_ci		 * planes unrelated to this CRTC.
203162306a36Sopenharmony_ci		 */
203262306a36Sopenharmony_ci		if (tegra->dc != dc)
203362306a36Sopenharmony_ci			continue;
203462306a36Sopenharmony_ci
203562306a36Sopenharmony_ci		new_avg_bw = new_tegra_state->avg_memory_bandwidth;
203662306a36Sopenharmony_ci		old_avg_bw = old_tegra_state->avg_memory_bandwidth;
203762306a36Sopenharmony_ci
203862306a36Sopenharmony_ci		new_peak_bw = new_tegra_state->total_peak_memory_bandwidth;
203962306a36Sopenharmony_ci		old_peak_bw = old_tegra_state->total_peak_memory_bandwidth;
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci		/*
204262306a36Sopenharmony_ci		 * See the comment related to !crtc->state->active above,
204362306a36Sopenharmony_ci		 * which explains why bandwidths need to be updated when
204462306a36Sopenharmony_ci		 * CRTC is turning ON.
204562306a36Sopenharmony_ci		 */
204662306a36Sopenharmony_ci		if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw &&
204762306a36Sopenharmony_ci		    old_crtc_state->active)
204862306a36Sopenharmony_ci			continue;
204962306a36Sopenharmony_ci
205062306a36Sopenharmony_ci		window.src.h = drm_rect_height(&plane->state->src) >> 16;
205162306a36Sopenharmony_ci		window.dst.h = drm_rect_height(&plane->state->dst);
205262306a36Sopenharmony_ci
205362306a36Sopenharmony_ci		old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16;
205462306a36Sopenharmony_ci		old_window.dst.h = drm_rect_height(&old_plane_state->dst);
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci		/*
205762306a36Sopenharmony_ci		 * During the preparation phase (atomic_begin), the memory
205862306a36Sopenharmony_ci		 * freq should go high before the DC changes are committed
205962306a36Sopenharmony_ci		 * if bandwidth requirement goes up, otherwise memory freq
206062306a36Sopenharmony_ci		 * should to stay high if BW requirement goes down.  The
206162306a36Sopenharmony_ci		 * opposite applies to the completion phase (post_commit).
206262306a36Sopenharmony_ci		 */
206362306a36Sopenharmony_ci		if (prepare_bandwidth_transition) {
206462306a36Sopenharmony_ci			new_avg_bw = max(old_avg_bw, new_avg_bw);
206562306a36Sopenharmony_ci			new_peak_bw = max(old_peak_bw, new_peak_bw);
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci			if (tegra_plane_use_vertical_filtering(tegra, &old_window))
206862306a36Sopenharmony_ci				window = old_window;
206962306a36Sopenharmony_ci		}
207062306a36Sopenharmony_ci
207162306a36Sopenharmony_ci		icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw);
207262306a36Sopenharmony_ci
207362306a36Sopenharmony_ci		if (tegra_plane_use_vertical_filtering(tegra, &window))
207462306a36Sopenharmony_ci			icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw);
207562306a36Sopenharmony_ci		else
207662306a36Sopenharmony_ci			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
207762306a36Sopenharmony_ci	}
207862306a36Sopenharmony_ci}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_cistatic void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
208162306a36Sopenharmony_ci				      struct drm_atomic_state *state)
208262306a36Sopenharmony_ci{
208362306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
208462306a36Sopenharmony_ci	u32 value;
208562306a36Sopenharmony_ci	int err;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	if (!tegra_dc_idle(dc)) {
208862306a36Sopenharmony_ci		tegra_dc_stop(dc);
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci		/*
209162306a36Sopenharmony_ci		 * Ignore the return value, there isn't anything useful to do
209262306a36Sopenharmony_ci		 * in case this fails.
209362306a36Sopenharmony_ci		 */
209462306a36Sopenharmony_ci		tegra_dc_wait_idle(dc, 100);
209562306a36Sopenharmony_ci	}
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	/*
209862306a36Sopenharmony_ci	 * This should really be part of the RGB encoder driver, but clearing
209962306a36Sopenharmony_ci	 * these bits has the side-effect of stopping the display controller.
210062306a36Sopenharmony_ci	 * When that happens no VBLANK interrupts will be raised. At the same
210162306a36Sopenharmony_ci	 * time the encoder is disabled before the display controller, so the
210262306a36Sopenharmony_ci	 * above code is always going to timeout waiting for the controller
210362306a36Sopenharmony_ci	 * to go idle.
210462306a36Sopenharmony_ci	 *
210562306a36Sopenharmony_ci	 * Given the close coupling between the RGB encoder and the display
210662306a36Sopenharmony_ci	 * controller doing it here is still kind of okay. None of the other
210762306a36Sopenharmony_ci	 * encoder drivers require these bits to be cleared.
210862306a36Sopenharmony_ci	 *
210962306a36Sopenharmony_ci	 * XXX: Perhaps given that the display controller is switched off at
211062306a36Sopenharmony_ci	 * this point anyway maybe clearing these bits isn't even useful for
211162306a36Sopenharmony_ci	 * the RGB encoder?
211262306a36Sopenharmony_ci	 */
211362306a36Sopenharmony_ci	if (dc->rgb) {
211462306a36Sopenharmony_ci		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
211562306a36Sopenharmony_ci		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
211662306a36Sopenharmony_ci			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
211762306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
211862306a36Sopenharmony_ci	}
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	tegra_dc_stats_reset(&dc->stats);
212162306a36Sopenharmony_ci	drm_crtc_vblank_off(crtc);
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	spin_lock_irq(&crtc->dev->event_lock);
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	if (crtc->state->event) {
212662306a36Sopenharmony_ci		drm_crtc_send_vblank_event(crtc, crtc->state->event);
212762306a36Sopenharmony_ci		crtc->state->event = NULL;
212862306a36Sopenharmony_ci	}
212962306a36Sopenharmony_ci
213062306a36Sopenharmony_ci	spin_unlock_irq(&crtc->dev->event_lock);
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci	err = host1x_client_suspend(&dc->client);
213362306a36Sopenharmony_ci	if (err < 0)
213462306a36Sopenharmony_ci		dev_err(dc->dev, "failed to suspend: %d\n", err);
213562306a36Sopenharmony_ci
213662306a36Sopenharmony_ci	if (dc->has_opp_table) {
213762306a36Sopenharmony_ci		err = dev_pm_genpd_set_performance_state(dc->dev, 0);
213862306a36Sopenharmony_ci		if (err)
213962306a36Sopenharmony_ci			dev_err(dc->dev,
214062306a36Sopenharmony_ci				"failed to clear power domain state: %d\n", err);
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci}
214362306a36Sopenharmony_ci
214462306a36Sopenharmony_cistatic void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
214562306a36Sopenharmony_ci				     struct drm_atomic_state *state)
214662306a36Sopenharmony_ci{
214762306a36Sopenharmony_ci	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
214862306a36Sopenharmony_ci	struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
214962306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
215062306a36Sopenharmony_ci	u32 value;
215162306a36Sopenharmony_ci	int err;
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_ci	/* apply PLL changes */
215462306a36Sopenharmony_ci	tegra_dc_set_clock_rate(dc, crtc_state);
215562306a36Sopenharmony_ci
215662306a36Sopenharmony_ci	err = host1x_client_resume(&dc->client);
215762306a36Sopenharmony_ci	if (err < 0) {
215862306a36Sopenharmony_ci		dev_err(dc->dev, "failed to resume: %d\n", err);
215962306a36Sopenharmony_ci		return;
216062306a36Sopenharmony_ci	}
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	/* initialize display controller */
216362306a36Sopenharmony_ci	if (dc->syncpt) {
216462306a36Sopenharmony_ci		u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci		if (dc->soc->has_nvdisplay)
216762306a36Sopenharmony_ci			enable = 1 << 31;
216862306a36Sopenharmony_ci		else
216962306a36Sopenharmony_ci			enable = 1 << 8;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci		value = SYNCPT_CNTRL_NO_STALL;
217262306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci		value = enable | syncpt;
217562306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
217662306a36Sopenharmony_ci	}
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay) {
217962306a36Sopenharmony_ci		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
218062306a36Sopenharmony_ci			DSC_OBUF_UF_INT;
218162306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
218262306a36Sopenharmony_ci
218362306a36Sopenharmony_ci		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
218462306a36Sopenharmony_ci			DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
218562306a36Sopenharmony_ci			HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
218662306a36Sopenharmony_ci			REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
218762306a36Sopenharmony_ci			VBLANK_INT | FRAME_END_INT;
218862306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
218962306a36Sopenharmony_ci
219062306a36Sopenharmony_ci		value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
219162306a36Sopenharmony_ci			FRAME_END_INT;
219262306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci		value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
219562306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
219662306a36Sopenharmony_ci
219762306a36Sopenharmony_ci		tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
219862306a36Sopenharmony_ci	} else {
219962306a36Sopenharmony_ci		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
220062306a36Sopenharmony_ci			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
220162306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
220462306a36Sopenharmony_ci			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
220562306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
220662306a36Sopenharmony_ci
220762306a36Sopenharmony_ci		/* initialize timer */
220862306a36Sopenharmony_ci		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
220962306a36Sopenharmony_ci			WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
221062306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
221162306a36Sopenharmony_ci
221262306a36Sopenharmony_ci		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
221362306a36Sopenharmony_ci			WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
221462306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci		value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
221762306a36Sopenharmony_ci			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
221862306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
221962306a36Sopenharmony_ci
222062306a36Sopenharmony_ci		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
222162306a36Sopenharmony_ci			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
222262306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
222362306a36Sopenharmony_ci	}
222462306a36Sopenharmony_ci
222562306a36Sopenharmony_ci	if (dc->soc->supports_background_color)
222662306a36Sopenharmony_ci		tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
222762306a36Sopenharmony_ci	else
222862306a36Sopenharmony_ci		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	/* apply pixel clock changes */
223162306a36Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
223262306a36Sopenharmony_ci		value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1;
223362306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
223462306a36Sopenharmony_ci	}
223562306a36Sopenharmony_ci
223662306a36Sopenharmony_ci	/* program display mode */
223762306a36Sopenharmony_ci	tegra_dc_set_timings(dc, mode);
223862306a36Sopenharmony_ci
223962306a36Sopenharmony_ci	/* interlacing isn't supported yet, so disable it */
224062306a36Sopenharmony_ci	if (dc->soc->supports_interlacing) {
224162306a36Sopenharmony_ci		value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
224262306a36Sopenharmony_ci		value &= ~INTERLACE_ENABLE;
224362306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
224462306a36Sopenharmony_ci	}
224562306a36Sopenharmony_ci
224662306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
224762306a36Sopenharmony_ci	value &= ~DISP_CTRL_MODE_MASK;
224862306a36Sopenharmony_ci	value |= DISP_CTRL_MODE_C_DISPLAY;
224962306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	if (!dc->soc->has_nvdisplay) {
225262306a36Sopenharmony_ci		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
225362306a36Sopenharmony_ci		value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
225462306a36Sopenharmony_ci			 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
225562306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
225662306a36Sopenharmony_ci	}
225762306a36Sopenharmony_ci
225862306a36Sopenharmony_ci	/* enable underflow reporting and display red for missing pixels */
225962306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay) {
226062306a36Sopenharmony_ci		value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
226162306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
226262306a36Sopenharmony_ci	}
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	if (dc->rgb) {
226562306a36Sopenharmony_ci		/* XXX: parameterize? */
226662306a36Sopenharmony_ci		value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
226762306a36Sopenharmony_ci		tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
226862306a36Sopenharmony_ci	}
226962306a36Sopenharmony_ci
227062306a36Sopenharmony_ci	tegra_dc_commit(dc);
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	drm_crtc_vblank_on(crtc);
227362306a36Sopenharmony_ci}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_cistatic void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
227662306a36Sopenharmony_ci				    struct drm_atomic_state *state)
227762306a36Sopenharmony_ci{
227862306a36Sopenharmony_ci	unsigned long flags;
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	tegra_crtc_update_memory_bandwidth(crtc, state, true);
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	if (crtc->state->event) {
228362306a36Sopenharmony_ci		spin_lock_irqsave(&crtc->dev->event_lock, flags);
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci		if (drm_crtc_vblank_get(crtc) != 0)
228662306a36Sopenharmony_ci			drm_crtc_send_vblank_event(crtc, crtc->state->event);
228762306a36Sopenharmony_ci		else
228862306a36Sopenharmony_ci			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci		crtc->state->event = NULL;
229362306a36Sopenharmony_ci	}
229462306a36Sopenharmony_ci}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_cistatic void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
229762306a36Sopenharmony_ci				    struct drm_atomic_state *state)
229862306a36Sopenharmony_ci{
229962306a36Sopenharmony_ci	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
230062306a36Sopenharmony_ci									  crtc);
230162306a36Sopenharmony_ci	struct tegra_dc_state *dc_state = to_dc_state(crtc_state);
230262306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
230362306a36Sopenharmony_ci	u32 value;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci	value = dc_state->planes << 8 | GENERAL_UPDATE;
230662306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
230762306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
230862306a36Sopenharmony_ci
230962306a36Sopenharmony_ci	value = dc_state->planes | GENERAL_ACT_REQ;
231062306a36Sopenharmony_ci	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
231162306a36Sopenharmony_ci	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
231262306a36Sopenharmony_ci}
231362306a36Sopenharmony_ci
231462306a36Sopenharmony_cistatic bool tegra_plane_is_cursor(const struct drm_plane_state *state)
231562306a36Sopenharmony_ci{
231662306a36Sopenharmony_ci	const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc;
231762306a36Sopenharmony_ci	const struct drm_format_info *fmt = state->fb->format;
231862306a36Sopenharmony_ci	unsigned int src_w = drm_rect_width(&state->src) >> 16;
231962306a36Sopenharmony_ci	unsigned int dst_w = drm_rect_width(&state->dst);
232062306a36Sopenharmony_ci
232162306a36Sopenharmony_ci	if (state->plane->type != DRM_PLANE_TYPE_CURSOR)
232262306a36Sopenharmony_ci		return false;
232362306a36Sopenharmony_ci
232462306a36Sopenharmony_ci	if (soc->supports_cursor)
232562306a36Sopenharmony_ci		return true;
232662306a36Sopenharmony_ci
232762306a36Sopenharmony_ci	if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256)
232862306a36Sopenharmony_ci		return false;
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	return true;
233162306a36Sopenharmony_ci}
233262306a36Sopenharmony_ci
233362306a36Sopenharmony_cistatic unsigned long
233462306a36Sopenharmony_citegra_plane_overlap_mask(struct drm_crtc_state *state,
233562306a36Sopenharmony_ci			 const struct drm_plane_state *plane_state)
233662306a36Sopenharmony_ci{
233762306a36Sopenharmony_ci	const struct drm_plane_state *other_state;
233862306a36Sopenharmony_ci	const struct tegra_plane *tegra;
233962306a36Sopenharmony_ci	unsigned long overlap_mask = 0;
234062306a36Sopenharmony_ci	struct drm_plane *plane;
234162306a36Sopenharmony_ci	struct drm_rect rect;
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	if (!plane_state->visible || !plane_state->fb)
234462306a36Sopenharmony_ci		return 0;
234562306a36Sopenharmony_ci
234662306a36Sopenharmony_ci	/*
234762306a36Sopenharmony_ci	 * Data-prefetch FIFO will easily help to overcome temporal memory
234862306a36Sopenharmony_ci	 * pressure if other plane overlaps with the cursor plane.
234962306a36Sopenharmony_ci	 */
235062306a36Sopenharmony_ci	if (tegra_plane_is_cursor(plane_state))
235162306a36Sopenharmony_ci		return 0;
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) {
235462306a36Sopenharmony_ci		rect = plane_state->dst;
235562306a36Sopenharmony_ci
235662306a36Sopenharmony_ci		tegra = to_tegra_plane(other_state->plane);
235762306a36Sopenharmony_ci
235862306a36Sopenharmony_ci		if (!other_state->visible || !other_state->fb)
235962306a36Sopenharmony_ci			continue;
236062306a36Sopenharmony_ci
236162306a36Sopenharmony_ci		/*
236262306a36Sopenharmony_ci		 * Ignore cursor plane overlaps because it's not practical to
236362306a36Sopenharmony_ci		 * assume that it contributes to the bandwidth in overlapping
236462306a36Sopenharmony_ci		 * area if window width is small.
236562306a36Sopenharmony_ci		 */
236662306a36Sopenharmony_ci		if (tegra_plane_is_cursor(other_state))
236762306a36Sopenharmony_ci			continue;
236862306a36Sopenharmony_ci
236962306a36Sopenharmony_ci		if (drm_rect_intersect(&rect, &other_state->dst))
237062306a36Sopenharmony_ci			overlap_mask |= BIT(tegra->index);
237162306a36Sopenharmony_ci	}
237262306a36Sopenharmony_ci
237362306a36Sopenharmony_ci	return overlap_mask;
237462306a36Sopenharmony_ci}
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_cistatic int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc,
237762306a36Sopenharmony_ci						 struct drm_atomic_state *state)
237862306a36Sopenharmony_ci{
237962306a36Sopenharmony_ci	ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask;
238062306a36Sopenharmony_ci	u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {};
238162306a36Sopenharmony_ci	bool all_planes_overlap_simultaneously = true;
238262306a36Sopenharmony_ci	const struct tegra_plane_state *tegra_state;
238362306a36Sopenharmony_ci	const struct drm_plane_state *plane_state;
238462306a36Sopenharmony_ci	struct tegra_dc *dc = to_tegra_dc(crtc);
238562306a36Sopenharmony_ci	struct drm_crtc_state *new_state;
238662306a36Sopenharmony_ci	struct tegra_plane *tegra;
238762306a36Sopenharmony_ci	struct drm_plane *plane;
238862306a36Sopenharmony_ci
238962306a36Sopenharmony_ci	/*
239062306a36Sopenharmony_ci	 * The nv-display uses shared planes.  The algorithm below assumes
239162306a36Sopenharmony_ci	 * maximum 3 planes per-CRTC, this assumption isn't applicable to
239262306a36Sopenharmony_ci	 * the nv-display.  Note that T124 support has additional windows,
239362306a36Sopenharmony_ci	 * but currently they aren't supported by the driver.
239462306a36Sopenharmony_ci	 */
239562306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay)
239662306a36Sopenharmony_ci		return 0;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	new_state = drm_atomic_get_new_crtc_state(state, crtc);
239962306a36Sopenharmony_ci
240062306a36Sopenharmony_ci	/*
240162306a36Sopenharmony_ci	 * For overlapping planes pixel's data is fetched for each plane at
240262306a36Sopenharmony_ci	 * the same time, hence bandwidths are accumulated in this case.
240362306a36Sopenharmony_ci	 * This needs to be taken into account for calculating total bandwidth
240462306a36Sopenharmony_ci	 * consumed by all planes.
240562306a36Sopenharmony_ci	 *
240662306a36Sopenharmony_ci	 * Here we get the overlapping state of each plane, which is a
240762306a36Sopenharmony_ci	 * bitmask of plane indices telling with what planes there is an
240862306a36Sopenharmony_ci	 * overlap. Note that bitmask[plane] includes BIT(plane) in order
240962306a36Sopenharmony_ci	 * to make further code nicer and simpler.
241062306a36Sopenharmony_ci	 */
241162306a36Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
241262306a36Sopenharmony_ci		tegra_state = to_const_tegra_plane_state(plane_state);
241362306a36Sopenharmony_ci		tegra = to_tegra_plane(plane);
241462306a36Sopenharmony_ci
241562306a36Sopenharmony_ci		if (WARN_ON_ONCE(tegra->index >= TEGRA_DC_LEGACY_PLANES_NUM))
241662306a36Sopenharmony_ci			return -EINVAL;
241762306a36Sopenharmony_ci
241862306a36Sopenharmony_ci		plane_peak_bw[tegra->index] = tegra_state->peak_memory_bandwidth;
241962306a36Sopenharmony_ci		mask = tegra_plane_overlap_mask(new_state, plane_state);
242062306a36Sopenharmony_ci		overlap_mask[tegra->index] = mask;
242162306a36Sopenharmony_ci
242262306a36Sopenharmony_ci		if (hweight_long(mask) != 3)
242362306a36Sopenharmony_ci			all_planes_overlap_simultaneously = false;
242462306a36Sopenharmony_ci	}
242562306a36Sopenharmony_ci
242662306a36Sopenharmony_ci	/*
242762306a36Sopenharmony_ci	 * Then we calculate maximum bandwidth of each plane state.
242862306a36Sopenharmony_ci	 * The bandwidth includes the plane BW + BW of the "simultaneously"
242962306a36Sopenharmony_ci	 * overlapping planes, where "simultaneously" means areas where DC
243062306a36Sopenharmony_ci	 * fetches from the planes simultaneously during of scan-out process.
243162306a36Sopenharmony_ci	 *
243262306a36Sopenharmony_ci	 * For example, if plane A overlaps with planes B and C, but B and C
243362306a36Sopenharmony_ci	 * don't overlap, then the peak bandwidth will be either in area where
243462306a36Sopenharmony_ci	 * A-and-B or A-and-C planes overlap.
243562306a36Sopenharmony_ci	 *
243662306a36Sopenharmony_ci	 * The plane_peak_bw[] contains peak memory bandwidth values of
243762306a36Sopenharmony_ci	 * each plane, this information is needed by interconnect provider
243862306a36Sopenharmony_ci	 * in order to set up latency allowance based on the peak BW, see
243962306a36Sopenharmony_ci	 * tegra_crtc_update_memory_bandwidth().
244062306a36Sopenharmony_ci	 */
244162306a36Sopenharmony_ci	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
244262306a36Sopenharmony_ci		u32 i, old_peak_bw, new_peak_bw, overlap_bw = 0;
244362306a36Sopenharmony_ci
244462306a36Sopenharmony_ci		/*
244562306a36Sopenharmony_ci		 * Note that plane's atomic check doesn't touch the
244662306a36Sopenharmony_ci		 * total_peak_memory_bandwidth of enabled plane, hence the
244762306a36Sopenharmony_ci		 * current state contains the old bandwidth state from the
244862306a36Sopenharmony_ci		 * previous CRTC commit.
244962306a36Sopenharmony_ci		 */
245062306a36Sopenharmony_ci		tegra_state = to_const_tegra_plane_state(plane_state);
245162306a36Sopenharmony_ci		tegra = to_tegra_plane(plane);
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci		for_each_set_bit(i, &overlap_mask[tegra->index], 3) {
245462306a36Sopenharmony_ci			if (i == tegra->index)
245562306a36Sopenharmony_ci				continue;
245662306a36Sopenharmony_ci
245762306a36Sopenharmony_ci			if (all_planes_overlap_simultaneously)
245862306a36Sopenharmony_ci				overlap_bw += plane_peak_bw[i];
245962306a36Sopenharmony_ci			else
246062306a36Sopenharmony_ci				overlap_bw = max(overlap_bw, plane_peak_bw[i]);
246162306a36Sopenharmony_ci		}
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci		new_peak_bw = plane_peak_bw[tegra->index] + overlap_bw;
246462306a36Sopenharmony_ci		old_peak_bw = tegra_state->total_peak_memory_bandwidth;
246562306a36Sopenharmony_ci
246662306a36Sopenharmony_ci		/*
246762306a36Sopenharmony_ci		 * If plane's peak bandwidth changed (for example plane isn't
246862306a36Sopenharmony_ci		 * overlapped anymore) and plane isn't in the atomic state,
246962306a36Sopenharmony_ci		 * then add plane to the state in order to have the bandwidth
247062306a36Sopenharmony_ci		 * updated.
247162306a36Sopenharmony_ci		 */
247262306a36Sopenharmony_ci		if (old_peak_bw != new_peak_bw) {
247362306a36Sopenharmony_ci			struct tegra_plane_state *new_tegra_state;
247462306a36Sopenharmony_ci			struct drm_plane_state *new_plane_state;
247562306a36Sopenharmony_ci
247662306a36Sopenharmony_ci			new_plane_state = drm_atomic_get_plane_state(state, plane);
247762306a36Sopenharmony_ci			if (IS_ERR(new_plane_state))
247862306a36Sopenharmony_ci				return PTR_ERR(new_plane_state);
247962306a36Sopenharmony_ci
248062306a36Sopenharmony_ci			new_tegra_state = to_tegra_plane_state(new_plane_state);
248162306a36Sopenharmony_ci			new_tegra_state->total_peak_memory_bandwidth = new_peak_bw;
248262306a36Sopenharmony_ci		}
248362306a36Sopenharmony_ci	}
248462306a36Sopenharmony_ci
248562306a36Sopenharmony_ci	return 0;
248662306a36Sopenharmony_ci}
248762306a36Sopenharmony_ci
248862306a36Sopenharmony_cistatic int tegra_crtc_atomic_check(struct drm_crtc *crtc,
248962306a36Sopenharmony_ci				   struct drm_atomic_state *state)
249062306a36Sopenharmony_ci{
249162306a36Sopenharmony_ci	int err;
249262306a36Sopenharmony_ci
249362306a36Sopenharmony_ci	err = tegra_crtc_calculate_memory_bandwidth(crtc, state);
249462306a36Sopenharmony_ci	if (err)
249562306a36Sopenharmony_ci		return err;
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci	return 0;
249862306a36Sopenharmony_ci}
249962306a36Sopenharmony_ci
250062306a36Sopenharmony_civoid tegra_crtc_atomic_post_commit(struct drm_crtc *crtc,
250162306a36Sopenharmony_ci				   struct drm_atomic_state *state)
250262306a36Sopenharmony_ci{
250362306a36Sopenharmony_ci	/*
250462306a36Sopenharmony_ci	 * Display bandwidth is allowed to go down only once hardware state
250562306a36Sopenharmony_ci	 * is known to be armed, i.e. state was committed and VBLANK event
250662306a36Sopenharmony_ci	 * received.
250762306a36Sopenharmony_ci	 */
250862306a36Sopenharmony_ci	tegra_crtc_update_memory_bandwidth(crtc, state, false);
250962306a36Sopenharmony_ci}
251062306a36Sopenharmony_ci
251162306a36Sopenharmony_cistatic const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
251262306a36Sopenharmony_ci	.atomic_check = tegra_crtc_atomic_check,
251362306a36Sopenharmony_ci	.atomic_begin = tegra_crtc_atomic_begin,
251462306a36Sopenharmony_ci	.atomic_flush = tegra_crtc_atomic_flush,
251562306a36Sopenharmony_ci	.atomic_enable = tegra_crtc_atomic_enable,
251662306a36Sopenharmony_ci	.atomic_disable = tegra_crtc_atomic_disable,
251762306a36Sopenharmony_ci};
251862306a36Sopenharmony_ci
251962306a36Sopenharmony_cistatic irqreturn_t tegra_dc_irq(int irq, void *data)
252062306a36Sopenharmony_ci{
252162306a36Sopenharmony_ci	struct tegra_dc *dc = data;
252262306a36Sopenharmony_ci	unsigned long status;
252362306a36Sopenharmony_ci
252462306a36Sopenharmony_ci	status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
252562306a36Sopenharmony_ci	tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	if (status & FRAME_END_INT) {
252862306a36Sopenharmony_ci		/*
252962306a36Sopenharmony_ci		dev_dbg(dc->dev, "%s(): frame end\n", __func__);
253062306a36Sopenharmony_ci		*/
253162306a36Sopenharmony_ci		dc->stats.frames_total++;
253262306a36Sopenharmony_ci		dc->stats.frames++;
253362306a36Sopenharmony_ci	}
253462306a36Sopenharmony_ci
253562306a36Sopenharmony_ci	if (status & VBLANK_INT) {
253662306a36Sopenharmony_ci		/*
253762306a36Sopenharmony_ci		dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
253862306a36Sopenharmony_ci		*/
253962306a36Sopenharmony_ci		drm_crtc_handle_vblank(&dc->base);
254062306a36Sopenharmony_ci		dc->stats.vblank_total++;
254162306a36Sopenharmony_ci		dc->stats.vblank++;
254262306a36Sopenharmony_ci	}
254362306a36Sopenharmony_ci
254462306a36Sopenharmony_ci	if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
254562306a36Sopenharmony_ci		/*
254662306a36Sopenharmony_ci		dev_dbg(dc->dev, "%s(): underflow\n", __func__);
254762306a36Sopenharmony_ci		*/
254862306a36Sopenharmony_ci		dc->stats.underflow_total++;
254962306a36Sopenharmony_ci		dc->stats.underflow++;
255062306a36Sopenharmony_ci	}
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) {
255362306a36Sopenharmony_ci		/*
255462306a36Sopenharmony_ci		dev_dbg(dc->dev, "%s(): overflow\n", __func__);
255562306a36Sopenharmony_ci		*/
255662306a36Sopenharmony_ci		dc->stats.overflow_total++;
255762306a36Sopenharmony_ci		dc->stats.overflow++;
255862306a36Sopenharmony_ci	}
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	if (status & HEAD_UF_INT) {
256162306a36Sopenharmony_ci		dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
256262306a36Sopenharmony_ci		dc->stats.underflow_total++;
256362306a36Sopenharmony_ci		dc->stats.underflow++;
256462306a36Sopenharmony_ci	}
256562306a36Sopenharmony_ci
256662306a36Sopenharmony_ci	return IRQ_HANDLED;
256762306a36Sopenharmony_ci}
256862306a36Sopenharmony_ci
256962306a36Sopenharmony_cistatic bool tegra_dc_has_window_groups(struct tegra_dc *dc)
257062306a36Sopenharmony_ci{
257162306a36Sopenharmony_ci	unsigned int i;
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_ci	if (!dc->soc->wgrps)
257462306a36Sopenharmony_ci		return true;
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	for (i = 0; i < dc->soc->num_wgrps; i++) {
257762306a36Sopenharmony_ci		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci		if (wgrp->dc == dc->pipe && wgrp->num_windows > 0)
258062306a36Sopenharmony_ci			return true;
258162306a36Sopenharmony_ci	}
258262306a36Sopenharmony_ci
258362306a36Sopenharmony_ci	return false;
258462306a36Sopenharmony_ci}
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_cistatic int tegra_dc_early_init(struct host1x_client *client)
258762306a36Sopenharmony_ci{
258862306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
258962306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
259062306a36Sopenharmony_ci
259162306a36Sopenharmony_ci	tegra->num_crtcs++;
259262306a36Sopenharmony_ci
259362306a36Sopenharmony_ci	return 0;
259462306a36Sopenharmony_ci}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_cistatic int tegra_dc_init(struct host1x_client *client)
259762306a36Sopenharmony_ci{
259862306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
259962306a36Sopenharmony_ci	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
260062306a36Sopenharmony_ci	struct tegra_dc *dc = host1x_client_to_dc(client);
260162306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
260262306a36Sopenharmony_ci	struct drm_plane *primary = NULL;
260362306a36Sopenharmony_ci	struct drm_plane *cursor = NULL;
260462306a36Sopenharmony_ci	int err;
260562306a36Sopenharmony_ci
260662306a36Sopenharmony_ci	/*
260762306a36Sopenharmony_ci	 * DC has been reset by now, so VBLANK syncpoint can be released
260862306a36Sopenharmony_ci	 * for general use.
260962306a36Sopenharmony_ci	 */
261062306a36Sopenharmony_ci	host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	/*
261362306a36Sopenharmony_ci	 * XXX do not register DCs with no window groups because we cannot
261462306a36Sopenharmony_ci	 * assign a primary plane to them, which in turn will cause KMS to
261562306a36Sopenharmony_ci	 * crash.
261662306a36Sopenharmony_ci	 */
261762306a36Sopenharmony_ci	if (!tegra_dc_has_window_groups(dc))
261862306a36Sopenharmony_ci		return 0;
261962306a36Sopenharmony_ci
262062306a36Sopenharmony_ci	/*
262162306a36Sopenharmony_ci	 * Set the display hub as the host1x client parent for the display
262262306a36Sopenharmony_ci	 * controller. This is needed for the runtime reference counting that
262362306a36Sopenharmony_ci	 * ensures the display hub is always powered when any of the display
262462306a36Sopenharmony_ci	 * controllers are.
262562306a36Sopenharmony_ci	 */
262662306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay)
262762306a36Sopenharmony_ci		client->parent = &tegra->hub->client;
262862306a36Sopenharmony_ci
262962306a36Sopenharmony_ci	dc->syncpt = host1x_syncpt_request(client, flags);
263062306a36Sopenharmony_ci	if (!dc->syncpt)
263162306a36Sopenharmony_ci		dev_warn(dc->dev, "failed to allocate syncpoint\n");
263262306a36Sopenharmony_ci
263362306a36Sopenharmony_ci	err = host1x_client_iommu_attach(client);
263462306a36Sopenharmony_ci	if (err < 0 && err != -ENODEV) {
263562306a36Sopenharmony_ci		dev_err(client->dev, "failed to attach to domain: %d\n", err);
263662306a36Sopenharmony_ci		return err;
263762306a36Sopenharmony_ci	}
263862306a36Sopenharmony_ci
263962306a36Sopenharmony_ci	if (dc->soc->wgrps)
264062306a36Sopenharmony_ci		primary = tegra_dc_add_shared_planes(drm, dc);
264162306a36Sopenharmony_ci	else
264262306a36Sopenharmony_ci		primary = tegra_dc_add_planes(drm, dc);
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	if (IS_ERR(primary)) {
264562306a36Sopenharmony_ci		err = PTR_ERR(primary);
264662306a36Sopenharmony_ci		goto cleanup;
264762306a36Sopenharmony_ci	}
264862306a36Sopenharmony_ci
264962306a36Sopenharmony_ci	if (dc->soc->supports_cursor) {
265062306a36Sopenharmony_ci		cursor = tegra_dc_cursor_plane_create(drm, dc);
265162306a36Sopenharmony_ci		if (IS_ERR(cursor)) {
265262306a36Sopenharmony_ci			err = PTR_ERR(cursor);
265362306a36Sopenharmony_ci			goto cleanup;
265462306a36Sopenharmony_ci		}
265562306a36Sopenharmony_ci	} else {
265662306a36Sopenharmony_ci		/* dedicate one overlay to mouse cursor */
265762306a36Sopenharmony_ci		cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true);
265862306a36Sopenharmony_ci		if (IS_ERR(cursor)) {
265962306a36Sopenharmony_ci			err = PTR_ERR(cursor);
266062306a36Sopenharmony_ci			goto cleanup;
266162306a36Sopenharmony_ci		}
266262306a36Sopenharmony_ci	}
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
266562306a36Sopenharmony_ci					&tegra_crtc_funcs, NULL);
266662306a36Sopenharmony_ci	if (err < 0)
266762306a36Sopenharmony_ci		goto cleanup;
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci	drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
267062306a36Sopenharmony_ci
267162306a36Sopenharmony_ci	/*
267262306a36Sopenharmony_ci	 * Keep track of the minimum pitch alignment across all display
267362306a36Sopenharmony_ci	 * controllers.
267462306a36Sopenharmony_ci	 */
267562306a36Sopenharmony_ci	if (dc->soc->pitch_align > tegra->pitch_align)
267662306a36Sopenharmony_ci		tegra->pitch_align = dc->soc->pitch_align;
267762306a36Sopenharmony_ci
267862306a36Sopenharmony_ci	/* track maximum resolution */
267962306a36Sopenharmony_ci	if (dc->soc->has_nvdisplay)
268062306a36Sopenharmony_ci		drm->mode_config.max_width = drm->mode_config.max_height = 16384;
268162306a36Sopenharmony_ci	else
268262306a36Sopenharmony_ci		drm->mode_config.max_width = drm->mode_config.max_height = 4096;
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	err = tegra_dc_rgb_init(drm, dc);
268562306a36Sopenharmony_ci	if (err < 0 && err != -ENODEV) {
268662306a36Sopenharmony_ci		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
268762306a36Sopenharmony_ci		goto cleanup;
268862306a36Sopenharmony_ci	}
268962306a36Sopenharmony_ci
269062306a36Sopenharmony_ci	err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
269162306a36Sopenharmony_ci			       dev_name(dc->dev), dc);
269262306a36Sopenharmony_ci	if (err < 0) {
269362306a36Sopenharmony_ci		dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
269462306a36Sopenharmony_ci			err);
269562306a36Sopenharmony_ci		goto cleanup;
269662306a36Sopenharmony_ci	}
269762306a36Sopenharmony_ci
269862306a36Sopenharmony_ci	/*
269962306a36Sopenharmony_ci	 * Inherit the DMA parameters (such as maximum segment size) from the
270062306a36Sopenharmony_ci	 * parent host1x device.
270162306a36Sopenharmony_ci	 */
270262306a36Sopenharmony_ci	client->dev->dma_parms = client->host->dma_parms;
270362306a36Sopenharmony_ci
270462306a36Sopenharmony_ci	return 0;
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_cicleanup:
270762306a36Sopenharmony_ci	if (!IS_ERR_OR_NULL(cursor))
270862306a36Sopenharmony_ci		drm_plane_cleanup(cursor);
270962306a36Sopenharmony_ci
271062306a36Sopenharmony_ci	if (!IS_ERR(primary))
271162306a36Sopenharmony_ci		drm_plane_cleanup(primary);
271262306a36Sopenharmony_ci
271362306a36Sopenharmony_ci	host1x_client_iommu_detach(client);
271462306a36Sopenharmony_ci	host1x_syncpt_put(dc->syncpt);
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	return err;
271762306a36Sopenharmony_ci}
271862306a36Sopenharmony_ci
271962306a36Sopenharmony_cistatic int tegra_dc_exit(struct host1x_client *client)
272062306a36Sopenharmony_ci{
272162306a36Sopenharmony_ci	struct tegra_dc *dc = host1x_client_to_dc(client);
272262306a36Sopenharmony_ci	int err;
272362306a36Sopenharmony_ci
272462306a36Sopenharmony_ci	if (!tegra_dc_has_window_groups(dc))
272562306a36Sopenharmony_ci		return 0;
272662306a36Sopenharmony_ci
272762306a36Sopenharmony_ci	/* avoid a dangling pointer just in case this disappears */
272862306a36Sopenharmony_ci	client->dev->dma_parms = NULL;
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	devm_free_irq(dc->dev, dc->irq, dc);
273162306a36Sopenharmony_ci
273262306a36Sopenharmony_ci	err = tegra_dc_rgb_exit(dc);
273362306a36Sopenharmony_ci	if (err) {
273462306a36Sopenharmony_ci		dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err);
273562306a36Sopenharmony_ci		return err;
273662306a36Sopenharmony_ci	}
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	host1x_client_iommu_detach(client);
273962306a36Sopenharmony_ci	host1x_syncpt_put(dc->syncpt);
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_ci	return 0;
274262306a36Sopenharmony_ci}
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_cistatic int tegra_dc_late_exit(struct host1x_client *client)
274562306a36Sopenharmony_ci{
274662306a36Sopenharmony_ci	struct drm_device *drm = dev_get_drvdata(client->host);
274762306a36Sopenharmony_ci	struct tegra_drm *tegra = drm->dev_private;
274862306a36Sopenharmony_ci
274962306a36Sopenharmony_ci	tegra->num_crtcs--;
275062306a36Sopenharmony_ci
275162306a36Sopenharmony_ci	return 0;
275262306a36Sopenharmony_ci}
275362306a36Sopenharmony_ci
275462306a36Sopenharmony_cistatic int tegra_dc_runtime_suspend(struct host1x_client *client)
275562306a36Sopenharmony_ci{
275662306a36Sopenharmony_ci	struct tegra_dc *dc = host1x_client_to_dc(client);
275762306a36Sopenharmony_ci	struct device *dev = client->dev;
275862306a36Sopenharmony_ci	int err;
275962306a36Sopenharmony_ci
276062306a36Sopenharmony_ci	err = reset_control_assert(dc->rst);
276162306a36Sopenharmony_ci	if (err < 0) {
276262306a36Sopenharmony_ci		dev_err(dev, "failed to assert reset: %d\n", err);
276362306a36Sopenharmony_ci		return err;
276462306a36Sopenharmony_ci	}
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ci	if (dc->soc->has_powergate)
276762306a36Sopenharmony_ci		tegra_powergate_power_off(dc->powergate);
276862306a36Sopenharmony_ci
276962306a36Sopenharmony_ci	clk_disable_unprepare(dc->clk);
277062306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
277162306a36Sopenharmony_ci
277262306a36Sopenharmony_ci	return 0;
277362306a36Sopenharmony_ci}
277462306a36Sopenharmony_ci
277562306a36Sopenharmony_cistatic int tegra_dc_runtime_resume(struct host1x_client *client)
277662306a36Sopenharmony_ci{
277762306a36Sopenharmony_ci	struct tegra_dc *dc = host1x_client_to_dc(client);
277862306a36Sopenharmony_ci	struct device *dev = client->dev;
277962306a36Sopenharmony_ci	int err;
278062306a36Sopenharmony_ci
278162306a36Sopenharmony_ci	err = pm_runtime_resume_and_get(dev);
278262306a36Sopenharmony_ci	if (err < 0) {
278362306a36Sopenharmony_ci		dev_err(dev, "failed to get runtime PM: %d\n", err);
278462306a36Sopenharmony_ci		return err;
278562306a36Sopenharmony_ci	}
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	if (dc->soc->has_powergate) {
278862306a36Sopenharmony_ci		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
278962306a36Sopenharmony_ci							dc->rst);
279062306a36Sopenharmony_ci		if (err < 0) {
279162306a36Sopenharmony_ci			dev_err(dev, "failed to power partition: %d\n", err);
279262306a36Sopenharmony_ci			goto put_rpm;
279362306a36Sopenharmony_ci		}
279462306a36Sopenharmony_ci	} else {
279562306a36Sopenharmony_ci		err = clk_prepare_enable(dc->clk);
279662306a36Sopenharmony_ci		if (err < 0) {
279762306a36Sopenharmony_ci			dev_err(dev, "failed to enable clock: %d\n", err);
279862306a36Sopenharmony_ci			goto put_rpm;
279962306a36Sopenharmony_ci		}
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci		err = reset_control_deassert(dc->rst);
280262306a36Sopenharmony_ci		if (err < 0) {
280362306a36Sopenharmony_ci			dev_err(dev, "failed to deassert reset: %d\n", err);
280462306a36Sopenharmony_ci			goto disable_clk;
280562306a36Sopenharmony_ci		}
280662306a36Sopenharmony_ci	}
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci	return 0;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_cidisable_clk:
281162306a36Sopenharmony_ci	clk_disable_unprepare(dc->clk);
281262306a36Sopenharmony_ciput_rpm:
281362306a36Sopenharmony_ci	pm_runtime_put_sync(dev);
281462306a36Sopenharmony_ci	return err;
281562306a36Sopenharmony_ci}
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_cistatic const struct host1x_client_ops dc_client_ops = {
281862306a36Sopenharmony_ci	.early_init = tegra_dc_early_init,
281962306a36Sopenharmony_ci	.init = tegra_dc_init,
282062306a36Sopenharmony_ci	.exit = tegra_dc_exit,
282162306a36Sopenharmony_ci	.late_exit = tegra_dc_late_exit,
282262306a36Sopenharmony_ci	.suspend = tegra_dc_runtime_suspend,
282362306a36Sopenharmony_ci	.resume = tegra_dc_runtime_resume,
282462306a36Sopenharmony_ci};
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra20_dc_soc_info = {
282762306a36Sopenharmony_ci	.supports_background_color = false,
282862306a36Sopenharmony_ci	.supports_interlacing = false,
282962306a36Sopenharmony_ci	.supports_cursor = false,
283062306a36Sopenharmony_ci	.supports_block_linear = false,
283162306a36Sopenharmony_ci	.supports_sector_layout = false,
283262306a36Sopenharmony_ci	.has_legacy_blending = true,
283362306a36Sopenharmony_ci	.pitch_align = 8,
283462306a36Sopenharmony_ci	.has_powergate = false,
283562306a36Sopenharmony_ci	.coupled_pm = true,
283662306a36Sopenharmony_ci	.has_nvdisplay = false,
283762306a36Sopenharmony_ci	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
283862306a36Sopenharmony_ci	.primary_formats = tegra20_primary_formats,
283962306a36Sopenharmony_ci	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
284062306a36Sopenharmony_ci	.overlay_formats = tegra20_overlay_formats,
284162306a36Sopenharmony_ci	.modifiers = tegra20_modifiers,
284262306a36Sopenharmony_ci	.has_win_a_without_filters = true,
284362306a36Sopenharmony_ci	.has_win_b_vfilter_mem_client = true,
284462306a36Sopenharmony_ci	.has_win_c_without_vert_filter = true,
284562306a36Sopenharmony_ci	.plane_tiled_memory_bandwidth_x2 = false,
284662306a36Sopenharmony_ci	.has_pll_d2_out0 = false,
284762306a36Sopenharmony_ci};
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra30_dc_soc_info = {
285062306a36Sopenharmony_ci	.supports_background_color = false,
285162306a36Sopenharmony_ci	.supports_interlacing = false,
285262306a36Sopenharmony_ci	.supports_cursor = false,
285362306a36Sopenharmony_ci	.supports_block_linear = false,
285462306a36Sopenharmony_ci	.supports_sector_layout = false,
285562306a36Sopenharmony_ci	.has_legacy_blending = true,
285662306a36Sopenharmony_ci	.pitch_align = 8,
285762306a36Sopenharmony_ci	.has_powergate = false,
285862306a36Sopenharmony_ci	.coupled_pm = false,
285962306a36Sopenharmony_ci	.has_nvdisplay = false,
286062306a36Sopenharmony_ci	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
286162306a36Sopenharmony_ci	.primary_formats = tegra20_primary_formats,
286262306a36Sopenharmony_ci	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
286362306a36Sopenharmony_ci	.overlay_formats = tegra20_overlay_formats,
286462306a36Sopenharmony_ci	.modifiers = tegra20_modifiers,
286562306a36Sopenharmony_ci	.has_win_a_without_filters = false,
286662306a36Sopenharmony_ci	.has_win_b_vfilter_mem_client = true,
286762306a36Sopenharmony_ci	.has_win_c_without_vert_filter = false,
286862306a36Sopenharmony_ci	.plane_tiled_memory_bandwidth_x2 = true,
286962306a36Sopenharmony_ci	.has_pll_d2_out0 = true,
287062306a36Sopenharmony_ci};
287162306a36Sopenharmony_ci
287262306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra114_dc_soc_info = {
287362306a36Sopenharmony_ci	.supports_background_color = false,
287462306a36Sopenharmony_ci	.supports_interlacing = false,
287562306a36Sopenharmony_ci	.supports_cursor = false,
287662306a36Sopenharmony_ci	.supports_block_linear = false,
287762306a36Sopenharmony_ci	.supports_sector_layout = false,
287862306a36Sopenharmony_ci	.has_legacy_blending = true,
287962306a36Sopenharmony_ci	.pitch_align = 64,
288062306a36Sopenharmony_ci	.has_powergate = true,
288162306a36Sopenharmony_ci	.coupled_pm = false,
288262306a36Sopenharmony_ci	.has_nvdisplay = false,
288362306a36Sopenharmony_ci	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
288462306a36Sopenharmony_ci	.primary_formats = tegra114_primary_formats,
288562306a36Sopenharmony_ci	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
288662306a36Sopenharmony_ci	.overlay_formats = tegra114_overlay_formats,
288762306a36Sopenharmony_ci	.modifiers = tegra20_modifiers,
288862306a36Sopenharmony_ci	.has_win_a_without_filters = false,
288962306a36Sopenharmony_ci	.has_win_b_vfilter_mem_client = false,
289062306a36Sopenharmony_ci	.has_win_c_without_vert_filter = false,
289162306a36Sopenharmony_ci	.plane_tiled_memory_bandwidth_x2 = true,
289262306a36Sopenharmony_ci	.has_pll_d2_out0 = true,
289362306a36Sopenharmony_ci};
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra124_dc_soc_info = {
289662306a36Sopenharmony_ci	.supports_background_color = true,
289762306a36Sopenharmony_ci	.supports_interlacing = true,
289862306a36Sopenharmony_ci	.supports_cursor = true,
289962306a36Sopenharmony_ci	.supports_block_linear = true,
290062306a36Sopenharmony_ci	.supports_sector_layout = false,
290162306a36Sopenharmony_ci	.has_legacy_blending = false,
290262306a36Sopenharmony_ci	.pitch_align = 64,
290362306a36Sopenharmony_ci	.has_powergate = true,
290462306a36Sopenharmony_ci	.coupled_pm = false,
290562306a36Sopenharmony_ci	.has_nvdisplay = false,
290662306a36Sopenharmony_ci	.num_primary_formats = ARRAY_SIZE(tegra124_primary_formats),
290762306a36Sopenharmony_ci	.primary_formats = tegra124_primary_formats,
290862306a36Sopenharmony_ci	.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
290962306a36Sopenharmony_ci	.overlay_formats = tegra124_overlay_formats,
291062306a36Sopenharmony_ci	.modifiers = tegra124_modifiers,
291162306a36Sopenharmony_ci	.has_win_a_without_filters = false,
291262306a36Sopenharmony_ci	.has_win_b_vfilter_mem_client = false,
291362306a36Sopenharmony_ci	.has_win_c_without_vert_filter = false,
291462306a36Sopenharmony_ci	.plane_tiled_memory_bandwidth_x2 = false,
291562306a36Sopenharmony_ci	.has_pll_d2_out0 = true,
291662306a36Sopenharmony_ci};
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra210_dc_soc_info = {
291962306a36Sopenharmony_ci	.supports_background_color = true,
292062306a36Sopenharmony_ci	.supports_interlacing = true,
292162306a36Sopenharmony_ci	.supports_cursor = true,
292262306a36Sopenharmony_ci	.supports_block_linear = true,
292362306a36Sopenharmony_ci	.supports_sector_layout = false,
292462306a36Sopenharmony_ci	.has_legacy_blending = false,
292562306a36Sopenharmony_ci	.pitch_align = 64,
292662306a36Sopenharmony_ci	.has_powergate = true,
292762306a36Sopenharmony_ci	.coupled_pm = false,
292862306a36Sopenharmony_ci	.has_nvdisplay = false,
292962306a36Sopenharmony_ci	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
293062306a36Sopenharmony_ci	.primary_formats = tegra114_primary_formats,
293162306a36Sopenharmony_ci	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
293262306a36Sopenharmony_ci	.overlay_formats = tegra114_overlay_formats,
293362306a36Sopenharmony_ci	.modifiers = tegra124_modifiers,
293462306a36Sopenharmony_ci	.has_win_a_without_filters = false,
293562306a36Sopenharmony_ci	.has_win_b_vfilter_mem_client = false,
293662306a36Sopenharmony_ci	.has_win_c_without_vert_filter = false,
293762306a36Sopenharmony_ci	.plane_tiled_memory_bandwidth_x2 = false,
293862306a36Sopenharmony_ci	.has_pll_d2_out0 = true,
293962306a36Sopenharmony_ci};
294062306a36Sopenharmony_ci
294162306a36Sopenharmony_cistatic const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
294262306a36Sopenharmony_ci	{
294362306a36Sopenharmony_ci		.index = 0,
294462306a36Sopenharmony_ci		.dc = 0,
294562306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 0 },
294662306a36Sopenharmony_ci		.num_windows = 1,
294762306a36Sopenharmony_ci	}, {
294862306a36Sopenharmony_ci		.index = 1,
294962306a36Sopenharmony_ci		.dc = 1,
295062306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 1 },
295162306a36Sopenharmony_ci		.num_windows = 1,
295262306a36Sopenharmony_ci	}, {
295362306a36Sopenharmony_ci		.index = 2,
295462306a36Sopenharmony_ci		.dc = 1,
295562306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 2 },
295662306a36Sopenharmony_ci		.num_windows = 1,
295762306a36Sopenharmony_ci	}, {
295862306a36Sopenharmony_ci		.index = 3,
295962306a36Sopenharmony_ci		.dc = 2,
296062306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 3 },
296162306a36Sopenharmony_ci		.num_windows = 1,
296262306a36Sopenharmony_ci	}, {
296362306a36Sopenharmony_ci		.index = 4,
296462306a36Sopenharmony_ci		.dc = 2,
296562306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 4 },
296662306a36Sopenharmony_ci		.num_windows = 1,
296762306a36Sopenharmony_ci	}, {
296862306a36Sopenharmony_ci		.index = 5,
296962306a36Sopenharmony_ci		.dc = 2,
297062306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 5 },
297162306a36Sopenharmony_ci		.num_windows = 1,
297262306a36Sopenharmony_ci	},
297362306a36Sopenharmony_ci};
297462306a36Sopenharmony_ci
297562306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra186_dc_soc_info = {
297662306a36Sopenharmony_ci	.supports_background_color = true,
297762306a36Sopenharmony_ci	.supports_interlacing = true,
297862306a36Sopenharmony_ci	.supports_cursor = true,
297962306a36Sopenharmony_ci	.supports_block_linear = true,
298062306a36Sopenharmony_ci	.supports_sector_layout = false,
298162306a36Sopenharmony_ci	.has_legacy_blending = false,
298262306a36Sopenharmony_ci	.pitch_align = 64,
298362306a36Sopenharmony_ci	.has_powergate = false,
298462306a36Sopenharmony_ci	.coupled_pm = false,
298562306a36Sopenharmony_ci	.has_nvdisplay = true,
298662306a36Sopenharmony_ci	.wgrps = tegra186_dc_wgrps,
298762306a36Sopenharmony_ci	.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
298862306a36Sopenharmony_ci	.plane_tiled_memory_bandwidth_x2 = false,
298962306a36Sopenharmony_ci	.has_pll_d2_out0 = false,
299062306a36Sopenharmony_ci};
299162306a36Sopenharmony_ci
299262306a36Sopenharmony_cistatic const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
299362306a36Sopenharmony_ci	{
299462306a36Sopenharmony_ci		.index = 0,
299562306a36Sopenharmony_ci		.dc = 0,
299662306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 0 },
299762306a36Sopenharmony_ci		.num_windows = 1,
299862306a36Sopenharmony_ci	}, {
299962306a36Sopenharmony_ci		.index = 1,
300062306a36Sopenharmony_ci		.dc = 1,
300162306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 1 },
300262306a36Sopenharmony_ci		.num_windows = 1,
300362306a36Sopenharmony_ci	}, {
300462306a36Sopenharmony_ci		.index = 2,
300562306a36Sopenharmony_ci		.dc = 1,
300662306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 2 },
300762306a36Sopenharmony_ci		.num_windows = 1,
300862306a36Sopenharmony_ci	}, {
300962306a36Sopenharmony_ci		.index = 3,
301062306a36Sopenharmony_ci		.dc = 2,
301162306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 3 },
301262306a36Sopenharmony_ci		.num_windows = 1,
301362306a36Sopenharmony_ci	}, {
301462306a36Sopenharmony_ci		.index = 4,
301562306a36Sopenharmony_ci		.dc = 2,
301662306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 4 },
301762306a36Sopenharmony_ci		.num_windows = 1,
301862306a36Sopenharmony_ci	}, {
301962306a36Sopenharmony_ci		.index = 5,
302062306a36Sopenharmony_ci		.dc = 2,
302162306a36Sopenharmony_ci		.windows = (const unsigned int[]) { 5 },
302262306a36Sopenharmony_ci		.num_windows = 1,
302362306a36Sopenharmony_ci	},
302462306a36Sopenharmony_ci};
302562306a36Sopenharmony_ci
302662306a36Sopenharmony_cistatic const struct tegra_dc_soc_info tegra194_dc_soc_info = {
302762306a36Sopenharmony_ci	.supports_background_color = true,
302862306a36Sopenharmony_ci	.supports_interlacing = true,
302962306a36Sopenharmony_ci	.supports_cursor = true,
303062306a36Sopenharmony_ci	.supports_block_linear = true,
303162306a36Sopenharmony_ci	.supports_sector_layout = true,
303262306a36Sopenharmony_ci	.has_legacy_blending = false,
303362306a36Sopenharmony_ci	.pitch_align = 64,
303462306a36Sopenharmony_ci	.has_powergate = false,
303562306a36Sopenharmony_ci	.coupled_pm = false,
303662306a36Sopenharmony_ci	.has_nvdisplay = true,
303762306a36Sopenharmony_ci	.wgrps = tegra194_dc_wgrps,
303862306a36Sopenharmony_ci	.num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
303962306a36Sopenharmony_ci	.plane_tiled_memory_bandwidth_x2 = false,
304062306a36Sopenharmony_ci	.has_pll_d2_out0 = false,
304162306a36Sopenharmony_ci};
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_cistatic const struct of_device_id tegra_dc_of_match[] = {
304462306a36Sopenharmony_ci	{
304562306a36Sopenharmony_ci		.compatible = "nvidia,tegra194-dc",
304662306a36Sopenharmony_ci		.data = &tegra194_dc_soc_info,
304762306a36Sopenharmony_ci	}, {
304862306a36Sopenharmony_ci		.compatible = "nvidia,tegra186-dc",
304962306a36Sopenharmony_ci		.data = &tegra186_dc_soc_info,
305062306a36Sopenharmony_ci	}, {
305162306a36Sopenharmony_ci		.compatible = "nvidia,tegra210-dc",
305262306a36Sopenharmony_ci		.data = &tegra210_dc_soc_info,
305362306a36Sopenharmony_ci	}, {
305462306a36Sopenharmony_ci		.compatible = "nvidia,tegra124-dc",
305562306a36Sopenharmony_ci		.data = &tegra124_dc_soc_info,
305662306a36Sopenharmony_ci	}, {
305762306a36Sopenharmony_ci		.compatible = "nvidia,tegra114-dc",
305862306a36Sopenharmony_ci		.data = &tegra114_dc_soc_info,
305962306a36Sopenharmony_ci	}, {
306062306a36Sopenharmony_ci		.compatible = "nvidia,tegra30-dc",
306162306a36Sopenharmony_ci		.data = &tegra30_dc_soc_info,
306262306a36Sopenharmony_ci	}, {
306362306a36Sopenharmony_ci		.compatible = "nvidia,tegra20-dc",
306462306a36Sopenharmony_ci		.data = &tegra20_dc_soc_info,
306562306a36Sopenharmony_ci	}, {
306662306a36Sopenharmony_ci		/* sentinel */
306762306a36Sopenharmony_ci	}
306862306a36Sopenharmony_ci};
306962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, tegra_dc_of_match);
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_cistatic int tegra_dc_parse_dt(struct tegra_dc *dc)
307262306a36Sopenharmony_ci{
307362306a36Sopenharmony_ci	struct device_node *np;
307462306a36Sopenharmony_ci	u32 value = 0;
307562306a36Sopenharmony_ci	int err;
307662306a36Sopenharmony_ci
307762306a36Sopenharmony_ci	err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value);
307862306a36Sopenharmony_ci	if (err < 0) {
307962306a36Sopenharmony_ci		dev_err(dc->dev, "missing \"nvidia,head\" property\n");
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci		/*
308262306a36Sopenharmony_ci		 * If the nvidia,head property isn't present, try to find the
308362306a36Sopenharmony_ci		 * correct head number by looking up the position of this
308462306a36Sopenharmony_ci		 * display controller's node within the device tree. Assuming
308562306a36Sopenharmony_ci		 * that the nodes are ordered properly in the DTS file and
308662306a36Sopenharmony_ci		 * that the translation into a flattened device tree blob
308762306a36Sopenharmony_ci		 * preserves that ordering this will actually yield the right
308862306a36Sopenharmony_ci		 * head number.
308962306a36Sopenharmony_ci		 *
309062306a36Sopenharmony_ci		 * If those assumptions don't hold, this will still work for
309162306a36Sopenharmony_ci		 * cases where only a single display controller is used.
309262306a36Sopenharmony_ci		 */
309362306a36Sopenharmony_ci		for_each_matching_node(np, tegra_dc_of_match) {
309462306a36Sopenharmony_ci			if (np == dc->dev->of_node) {
309562306a36Sopenharmony_ci				of_node_put(np);
309662306a36Sopenharmony_ci				break;
309762306a36Sopenharmony_ci			}
309862306a36Sopenharmony_ci
309962306a36Sopenharmony_ci			value++;
310062306a36Sopenharmony_ci		}
310162306a36Sopenharmony_ci	}
310262306a36Sopenharmony_ci
310362306a36Sopenharmony_ci	dc->pipe = value;
310462306a36Sopenharmony_ci
310562306a36Sopenharmony_ci	return 0;
310662306a36Sopenharmony_ci}
310762306a36Sopenharmony_ci
310862306a36Sopenharmony_cistatic int tegra_dc_match_by_pipe(struct device *dev, const void *data)
310962306a36Sopenharmony_ci{
311062306a36Sopenharmony_ci	struct tegra_dc *dc = dev_get_drvdata(dev);
311162306a36Sopenharmony_ci	unsigned int pipe = (unsigned long)(void *)data;
311262306a36Sopenharmony_ci
311362306a36Sopenharmony_ci	return dc->pipe == pipe;
311462306a36Sopenharmony_ci}
311562306a36Sopenharmony_ci
311662306a36Sopenharmony_cistatic int tegra_dc_couple(struct tegra_dc *dc)
311762306a36Sopenharmony_ci{
311862306a36Sopenharmony_ci	/*
311962306a36Sopenharmony_ci	 * On Tegra20, DC1 requires DC0 to be taken out of reset in order to
312062306a36Sopenharmony_ci	 * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND /
312162306a36Sopenharmony_ci	 * POWER_CONTROL registers during CRTC enabling.
312262306a36Sopenharmony_ci	 */
312362306a36Sopenharmony_ci	if (dc->soc->coupled_pm && dc->pipe == 1) {
312462306a36Sopenharmony_ci		struct device *companion;
312562306a36Sopenharmony_ci		struct tegra_dc *parent;
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci		companion = driver_find_device(dc->dev->driver, NULL, (const void *)0,
312862306a36Sopenharmony_ci					       tegra_dc_match_by_pipe);
312962306a36Sopenharmony_ci		if (!companion)
313062306a36Sopenharmony_ci			return -EPROBE_DEFER;
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci		parent = dev_get_drvdata(companion);
313362306a36Sopenharmony_ci		dc->client.parent = &parent->client;
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci		dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion));
313662306a36Sopenharmony_ci	}
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	return 0;
313962306a36Sopenharmony_ci}
314062306a36Sopenharmony_ci
314162306a36Sopenharmony_cistatic int tegra_dc_init_opp_table(struct tegra_dc *dc)
314262306a36Sopenharmony_ci{
314362306a36Sopenharmony_ci	struct tegra_core_opp_params opp_params = {};
314462306a36Sopenharmony_ci	int err;
314562306a36Sopenharmony_ci
314662306a36Sopenharmony_ci	err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
314762306a36Sopenharmony_ci	if (err && err != -ENODEV)
314862306a36Sopenharmony_ci		return err;
314962306a36Sopenharmony_ci
315062306a36Sopenharmony_ci	if (err)
315162306a36Sopenharmony_ci		dc->has_opp_table = false;
315262306a36Sopenharmony_ci	else
315362306a36Sopenharmony_ci		dc->has_opp_table = true;
315462306a36Sopenharmony_ci
315562306a36Sopenharmony_ci	return 0;
315662306a36Sopenharmony_ci}
315762306a36Sopenharmony_ci
315862306a36Sopenharmony_cistatic int tegra_dc_probe(struct platform_device *pdev)
315962306a36Sopenharmony_ci{
316062306a36Sopenharmony_ci	u64 dma_mask = dma_get_mask(pdev->dev.parent);
316162306a36Sopenharmony_ci	struct tegra_dc *dc;
316262306a36Sopenharmony_ci	int err;
316362306a36Sopenharmony_ci
316462306a36Sopenharmony_ci	err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
316562306a36Sopenharmony_ci	if (err < 0) {
316662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
316762306a36Sopenharmony_ci		return err;
316862306a36Sopenharmony_ci	}
316962306a36Sopenharmony_ci
317062306a36Sopenharmony_ci	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
317162306a36Sopenharmony_ci	if (!dc)
317262306a36Sopenharmony_ci		return -ENOMEM;
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	dc->soc = of_device_get_match_data(&pdev->dev);
317562306a36Sopenharmony_ci
317662306a36Sopenharmony_ci	INIT_LIST_HEAD(&dc->list);
317762306a36Sopenharmony_ci	dc->dev = &pdev->dev;
317862306a36Sopenharmony_ci
317962306a36Sopenharmony_ci	err = tegra_dc_parse_dt(dc);
318062306a36Sopenharmony_ci	if (err < 0)
318162306a36Sopenharmony_ci		return err;
318262306a36Sopenharmony_ci
318362306a36Sopenharmony_ci	err = tegra_dc_couple(dc);
318462306a36Sopenharmony_ci	if (err < 0)
318562306a36Sopenharmony_ci		return err;
318662306a36Sopenharmony_ci
318762306a36Sopenharmony_ci	dc->clk = devm_clk_get(&pdev->dev, NULL);
318862306a36Sopenharmony_ci	if (IS_ERR(dc->clk)) {
318962306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get clock\n");
319062306a36Sopenharmony_ci		return PTR_ERR(dc->clk);
319162306a36Sopenharmony_ci	}
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_ci	dc->rst = devm_reset_control_get(&pdev->dev, "dc");
319462306a36Sopenharmony_ci	if (IS_ERR(dc->rst)) {
319562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get reset\n");
319662306a36Sopenharmony_ci		return PTR_ERR(dc->rst);
319762306a36Sopenharmony_ci	}
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	/* assert reset and disable clock */
320062306a36Sopenharmony_ci	err = clk_prepare_enable(dc->clk);
320162306a36Sopenharmony_ci	if (err < 0)
320262306a36Sopenharmony_ci		return err;
320362306a36Sopenharmony_ci
320462306a36Sopenharmony_ci	usleep_range(2000, 4000);
320562306a36Sopenharmony_ci
320662306a36Sopenharmony_ci	err = reset_control_assert(dc->rst);
320762306a36Sopenharmony_ci	if (err < 0) {
320862306a36Sopenharmony_ci		clk_disable_unprepare(dc->clk);
320962306a36Sopenharmony_ci		return err;
321062306a36Sopenharmony_ci	}
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	usleep_range(2000, 4000);
321362306a36Sopenharmony_ci
321462306a36Sopenharmony_ci	clk_disable_unprepare(dc->clk);
321562306a36Sopenharmony_ci
321662306a36Sopenharmony_ci	if (dc->soc->has_powergate) {
321762306a36Sopenharmony_ci		if (dc->pipe == 0)
321862306a36Sopenharmony_ci			dc->powergate = TEGRA_POWERGATE_DIS;
321962306a36Sopenharmony_ci		else
322062306a36Sopenharmony_ci			dc->powergate = TEGRA_POWERGATE_DISB;
322162306a36Sopenharmony_ci
322262306a36Sopenharmony_ci		tegra_powergate_power_off(dc->powergate);
322362306a36Sopenharmony_ci	}
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci	err = tegra_dc_init_opp_table(dc);
322662306a36Sopenharmony_ci	if (err < 0)
322762306a36Sopenharmony_ci		return err;
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci	dc->regs = devm_platform_ioremap_resource(pdev, 0);
323062306a36Sopenharmony_ci	if (IS_ERR(dc->regs))
323162306a36Sopenharmony_ci		return PTR_ERR(dc->regs);
323262306a36Sopenharmony_ci
323362306a36Sopenharmony_ci	dc->irq = platform_get_irq(pdev, 0);
323462306a36Sopenharmony_ci	if (dc->irq < 0)
323562306a36Sopenharmony_ci		return -ENXIO;
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci	err = tegra_dc_rgb_probe(dc);
323862306a36Sopenharmony_ci	if (err < 0 && err != -ENODEV)
323962306a36Sopenharmony_ci		return dev_err_probe(&pdev->dev, err,
324062306a36Sopenharmony_ci				     "failed to probe RGB output\n");
324162306a36Sopenharmony_ci
324262306a36Sopenharmony_ci	platform_set_drvdata(pdev, dc);
324362306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	INIT_LIST_HEAD(&dc->client.list);
324662306a36Sopenharmony_ci	dc->client.ops = &dc_client_ops;
324762306a36Sopenharmony_ci	dc->client.dev = &pdev->dev;
324862306a36Sopenharmony_ci
324962306a36Sopenharmony_ci	err = host1x_client_register(&dc->client);
325062306a36Sopenharmony_ci	if (err < 0) {
325162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
325262306a36Sopenharmony_ci			err);
325362306a36Sopenharmony_ci		goto disable_pm;
325462306a36Sopenharmony_ci	}
325562306a36Sopenharmony_ci
325662306a36Sopenharmony_ci	return 0;
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_cidisable_pm:
325962306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
326062306a36Sopenharmony_ci	tegra_dc_rgb_remove(dc);
326162306a36Sopenharmony_ci
326262306a36Sopenharmony_ci	return err;
326362306a36Sopenharmony_ci}
326462306a36Sopenharmony_ci
326562306a36Sopenharmony_cistatic void tegra_dc_remove(struct platform_device *pdev)
326662306a36Sopenharmony_ci{
326762306a36Sopenharmony_ci	struct tegra_dc *dc = platform_get_drvdata(pdev);
326862306a36Sopenharmony_ci
326962306a36Sopenharmony_ci	host1x_client_unregister(&dc->client);
327062306a36Sopenharmony_ci
327162306a36Sopenharmony_ci	tegra_dc_rgb_remove(dc);
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
327462306a36Sopenharmony_ci}
327562306a36Sopenharmony_ci
327662306a36Sopenharmony_cistruct platform_driver tegra_dc_driver = {
327762306a36Sopenharmony_ci	.driver = {
327862306a36Sopenharmony_ci		.name = "tegra-dc",
327962306a36Sopenharmony_ci		.of_match_table = tegra_dc_of_match,
328062306a36Sopenharmony_ci	},
328162306a36Sopenharmony_ci	.probe = tegra_dc_probe,
328262306a36Sopenharmony_ci	.remove_new = tegra_dc_remove,
328362306a36Sopenharmony_ci};
3284