xref: /openbmc/linux/drivers/gpu/drm/tegra/dc.c (revision 4ce3048c0a622978436e04b47eade8c45e1e8a75)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dee8268fSThierry Reding /*
3dee8268fSThierry Reding  * Copyright (C) 2012 Avionic Design GmbH
4dee8268fSThierry Reding  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
5dee8268fSThierry Reding  */
6dee8268fSThierry Reding 
7dee8268fSThierry Reding #include <linux/clk.h>
8dee8268fSThierry Reding #include <linux/debugfs.h>
9eb1df694SSam Ravnborg #include <linux/delay.h>
10df06b759SThierry Reding #include <linux/iommu.h>
1104d5d5dfSDmitry Osipenko #include <linux/interconnect.h>
12eb1df694SSam Ravnborg #include <linux/module.h>
13b9ff7aeaSThierry Reding #include <linux/of_device.h>
14*4ce3048cSDmitry Osipenko #include <linux/pm_domain.h>
15*4ce3048cSDmitry Osipenko #include <linux/pm_opp.h>
1633a8eb8dSThierry Reding #include <linux/pm_runtime.h>
17ca48080aSStephen Warren #include <linux/reset.h>
18dee8268fSThierry Reding 
19*4ce3048cSDmitry Osipenko #include <soc/tegra/common.h>
209c012700SThierry Reding #include <soc/tegra/pmc.h>
219c012700SThierry Reding 
22eb1df694SSam Ravnborg #include <drm/drm_atomic.h>
23eb1df694SSam Ravnborg #include <drm/drm_atomic_helper.h>
24eb1df694SSam Ravnborg #include <drm/drm_debugfs.h>
25eb1df694SSam Ravnborg #include <drm/drm_fourcc.h>
26eb1df694SSam Ravnborg #include <drm/drm_plane_helper.h>
27eb1df694SSam Ravnborg #include <drm/drm_vblank.h>
28eb1df694SSam Ravnborg 
29dee8268fSThierry Reding #include "dc.h"
30dee8268fSThierry Reding #include "drm.h"
31dee8268fSThierry Reding #include "gem.h"
3247307954SThierry Reding #include "hub.h"
335acd3514SThierry Reding #include "plane.h"
34dee8268fSThierry Reding 
35b7e0b04aSMaarten Lankhorst static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
36b7e0b04aSMaarten Lankhorst 					    struct drm_crtc_state *state);
37b7e0b04aSMaarten Lankhorst 
38791ddb1eSThierry Reding static void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
39791ddb1eSThierry Reding {
40791ddb1eSThierry Reding 	stats->frames = 0;
41791ddb1eSThierry Reding 	stats->vblank = 0;
42791ddb1eSThierry Reding 	stats->underflow = 0;
43791ddb1eSThierry Reding 	stats->overflow = 0;
44791ddb1eSThierry Reding }
45791ddb1eSThierry Reding 
461087fac1SThierry Reding /* Reads the active copy of a register. */
4786df256fSThierry Reding static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
4886df256fSThierry Reding {
4986df256fSThierry Reding 	u32 value;
5086df256fSThierry Reding 
5186df256fSThierry Reding 	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
5286df256fSThierry Reding 	value = tegra_dc_readl(dc, offset);
5386df256fSThierry Reding 	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
5486df256fSThierry Reding 
5586df256fSThierry Reding 	return value;
5686df256fSThierry Reding }
5786df256fSThierry Reding 
581087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
591087fac1SThierry Reding 					      unsigned int offset)
601087fac1SThierry Reding {
611087fac1SThierry Reding 	if (offset >= 0x500 && offset <= 0x638) {
621087fac1SThierry Reding 		offset = 0x000 + (offset - 0x500);
631087fac1SThierry Reding 		return plane->offset + offset;
641087fac1SThierry Reding 	}
651087fac1SThierry Reding 
661087fac1SThierry Reding 	if (offset >= 0x700 && offset <= 0x719) {
671087fac1SThierry Reding 		offset = 0x180 + (offset - 0x700);
681087fac1SThierry Reding 		return plane->offset + offset;
691087fac1SThierry Reding 	}
701087fac1SThierry Reding 
711087fac1SThierry Reding 	if (offset >= 0x800 && offset <= 0x839) {
721087fac1SThierry Reding 		offset = 0x1c0 + (offset - 0x800);
731087fac1SThierry Reding 		return plane->offset + offset;
741087fac1SThierry Reding 	}
751087fac1SThierry Reding 
761087fac1SThierry Reding 	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
771087fac1SThierry Reding 
781087fac1SThierry Reding 	return plane->offset + offset;
791087fac1SThierry Reding }
801087fac1SThierry Reding 
811087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane,
821087fac1SThierry Reding 				    unsigned int offset)
831087fac1SThierry Reding {
841087fac1SThierry Reding 	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
851087fac1SThierry Reding }
861087fac1SThierry Reding 
871087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
881087fac1SThierry Reding 				      unsigned int offset)
891087fac1SThierry Reding {
901087fac1SThierry Reding 	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
911087fac1SThierry Reding }
921087fac1SThierry Reding 
93c57997bcSThierry Reding bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
94c57997bcSThierry Reding {
95c57997bcSThierry Reding 	struct device_node *np = dc->dev->of_node;
96c57997bcSThierry Reding 	struct of_phandle_iterator it;
97c57997bcSThierry Reding 	int err;
98c57997bcSThierry Reding 
99c57997bcSThierry Reding 	of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
100c57997bcSThierry Reding 		if (it.node == dev->of_node)
101c57997bcSThierry Reding 			return true;
102c57997bcSThierry Reding 
103c57997bcSThierry Reding 	return false;
104c57997bcSThierry Reding }
105c57997bcSThierry Reding 
10686df256fSThierry Reding /*
107d700ba7aSThierry Reding  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
108d700ba7aSThierry Reding  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
109d700ba7aSThierry Reding  * Latching happens mmediately if the display controller is in STOP mode or
110d700ba7aSThierry Reding  * on the next frame boundary otherwise.
111d700ba7aSThierry Reding  *
112d700ba7aSThierry Reding  * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The
113d700ba7aSThierry Reding  * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits
114d700ba7aSThierry Reding  * are written. When the *_ACT_REQ bits are written, the ARM copy is latched
115d700ba7aSThierry Reding  * into the ACTIVE copy, either immediately if the display controller is in
116d700ba7aSThierry Reding  * STOP mode, or at the next frame boundary otherwise.
117d700ba7aSThierry Reding  */
11862b9e063SThierry Reding void tegra_dc_commit(struct tegra_dc *dc)
119205d48edSThierry Reding {
120205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
121205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
122205d48edSThierry Reding }
123205d48edSThierry Reding 
12410288eeaSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
12510288eeaSThierry Reding 				  unsigned int bpp)
12610288eeaSThierry Reding {
12710288eeaSThierry Reding 	fixed20_12 outf = dfixed_init(out);
12810288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
12910288eeaSThierry Reding 	u32 dda_inc;
13010288eeaSThierry Reding 	int max;
13110288eeaSThierry Reding 
13210288eeaSThierry Reding 	if (v)
13310288eeaSThierry Reding 		max = 15;
13410288eeaSThierry Reding 	else {
13510288eeaSThierry Reding 		switch (bpp) {
13610288eeaSThierry Reding 		case 2:
13710288eeaSThierry Reding 			max = 8;
13810288eeaSThierry Reding 			break;
13910288eeaSThierry Reding 
14010288eeaSThierry Reding 		default:
14110288eeaSThierry Reding 			WARN_ON_ONCE(1);
142df561f66SGustavo A. R. Silva 			fallthrough;
14310288eeaSThierry Reding 		case 4:
14410288eeaSThierry Reding 			max = 4;
14510288eeaSThierry Reding 			break;
14610288eeaSThierry Reding 		}
14710288eeaSThierry Reding 	}
14810288eeaSThierry Reding 
14910288eeaSThierry Reding 	outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
15010288eeaSThierry Reding 	inf.full -= dfixed_const(1);
15110288eeaSThierry Reding 
15210288eeaSThierry Reding 	dda_inc = dfixed_div(inf, outf);
15310288eeaSThierry Reding 	dda_inc = min_t(u32, dda_inc, dfixed_const(max));
15410288eeaSThierry Reding 
15510288eeaSThierry Reding 	return dda_inc;
15610288eeaSThierry Reding }
15710288eeaSThierry Reding 
15810288eeaSThierry Reding static inline u32 compute_initial_dda(unsigned int in)
15910288eeaSThierry Reding {
16010288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
16110288eeaSThierry Reding 	return dfixed_frac(inf);
16210288eeaSThierry Reding }
16310288eeaSThierry Reding 
164ab7d3f58SThierry Reding static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
165ab7d3f58SThierry Reding {
166ebae8d07SThierry Reding 	u32 background[3] = {
167ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
168ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
169ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
170ebae8d07SThierry Reding 	};
171ebae8d07SThierry Reding 	u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
172ebae8d07SThierry Reding 			 BLEND_COLOR_KEY_NONE;
173ebae8d07SThierry Reding 	u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
174ebae8d07SThierry Reding 	struct tegra_plane_state *state;
1753dae08bcSDmitry Osipenko 	u32 blending[2];
176ebae8d07SThierry Reding 	unsigned int i;
177ebae8d07SThierry Reding 
1783dae08bcSDmitry Osipenko 	/* disable blending for non-overlapping case */
179ebae8d07SThierry Reding 	tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
180ebae8d07SThierry Reding 	tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
181ab7d3f58SThierry Reding 
1823dae08bcSDmitry Osipenko 	state = to_tegra_plane_state(plane->base.state);
1833dae08bcSDmitry Osipenko 
1843dae08bcSDmitry Osipenko 	if (state->opaque) {
1853dae08bcSDmitry Osipenko 		/*
1863dae08bcSDmitry Osipenko 		 * Since custom fix-weight blending isn't utilized and weight
1873dae08bcSDmitry Osipenko 		 * of top window is set to max, we can enforce dependent
1883dae08bcSDmitry Osipenko 		 * blending which in this case results in transparent bottom
1893dae08bcSDmitry Osipenko 		 * window if top window is opaque and if top window enables
1903dae08bcSDmitry Osipenko 		 * alpha blending, then bottom window is getting alpha value
1913dae08bcSDmitry Osipenko 		 * of 1 minus the sum of alpha components of the overlapping
1923dae08bcSDmitry Osipenko 		 * plane.
1933dae08bcSDmitry Osipenko 		 */
1943dae08bcSDmitry Osipenko 		background[0] |= BLEND_CONTROL_DEPENDENT;
1953dae08bcSDmitry Osipenko 		background[1] |= BLEND_CONTROL_DEPENDENT;
1963dae08bcSDmitry Osipenko 
1973dae08bcSDmitry Osipenko 		/*
1983dae08bcSDmitry Osipenko 		 * The region where three windows overlap is the intersection
1993dae08bcSDmitry Osipenko 		 * of the two regions where two windows overlap. It contributes
2003dae08bcSDmitry Osipenko 		 * to the area if all of the windows on top of it have an alpha
2013dae08bcSDmitry Osipenko 		 * component.
2023dae08bcSDmitry Osipenko 		 */
2033dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2043dae08bcSDmitry Osipenko 		case 0:
2053dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2063dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2073dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2083dae08bcSDmitry Osipenko 			break;
2093dae08bcSDmitry Osipenko 
2103dae08bcSDmitry Osipenko 		case 1:
2113dae08bcSDmitry Osipenko 			background[2] |= BLEND_CONTROL_DEPENDENT;
2123dae08bcSDmitry Osipenko 			break;
2133dae08bcSDmitry Osipenko 		}
2143dae08bcSDmitry Osipenko 	} else {
2153dae08bcSDmitry Osipenko 		/*
2163dae08bcSDmitry Osipenko 		 * Enable alpha blending if pixel format has an alpha
2173dae08bcSDmitry Osipenko 		 * component.
2183dae08bcSDmitry Osipenko 		 */
2193dae08bcSDmitry Osipenko 		foreground |= BLEND_CONTROL_ALPHA;
2203dae08bcSDmitry Osipenko 
2213dae08bcSDmitry Osipenko 		/*
2223dae08bcSDmitry Osipenko 		 * If any of the windows on top of this window is opaque, it
2233dae08bcSDmitry Osipenko 		 * will completely conceal this window within that area. If
2243dae08bcSDmitry Osipenko 		 * top window has an alpha component, it is blended over the
2253dae08bcSDmitry Osipenko 		 * bottom window.
2263dae08bcSDmitry Osipenko 		 */
2273dae08bcSDmitry Osipenko 		for (i = 0; i < 2; i++) {
2283dae08bcSDmitry Osipenko 			if (state->blending[i].alpha &&
2293dae08bcSDmitry Osipenko 			    state->blending[i].top)
2303dae08bcSDmitry Osipenko 				background[i] |= BLEND_CONTROL_DEPENDENT;
2313dae08bcSDmitry Osipenko 		}
2323dae08bcSDmitry Osipenko 
2333dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2343dae08bcSDmitry Osipenko 		case 0:
2353dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2363dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2373dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2383dae08bcSDmitry Osipenko 			break;
2393dae08bcSDmitry Osipenko 
2403dae08bcSDmitry Osipenko 		case 1:
2413dae08bcSDmitry Osipenko 			/*
2423dae08bcSDmitry Osipenko 			 * When both middle and topmost windows have an alpha,
2433dae08bcSDmitry Osipenko 			 * these windows a mixed together and then the result
2443dae08bcSDmitry Osipenko 			 * is blended over the bottom window.
2453dae08bcSDmitry Osipenko 			 */
2463dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2473dae08bcSDmitry Osipenko 			    state->blending[0].top)
2483dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2493dae08bcSDmitry Osipenko 
2503dae08bcSDmitry Osipenko 			if (state->blending[1].alpha &&
2513dae08bcSDmitry Osipenko 			    state->blending[1].top)
2523dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2533dae08bcSDmitry Osipenko 			break;
2543dae08bcSDmitry Osipenko 		}
2553dae08bcSDmitry Osipenko 	}
2563dae08bcSDmitry Osipenko 
2573dae08bcSDmitry Osipenko 	switch (state->base.normalized_zpos) {
258ab7d3f58SThierry Reding 	case 0:
259ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
260ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
261ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
262ab7d3f58SThierry Reding 		break;
263ab7d3f58SThierry Reding 
264ab7d3f58SThierry Reding 	case 1:
2653dae08bcSDmitry Osipenko 		/*
2663dae08bcSDmitry Osipenko 		 * If window B / C is topmost, then X / Y registers are
2673dae08bcSDmitry Osipenko 		 * matching the order of blending[...] state indices,
2683dae08bcSDmitry Osipenko 		 * otherwise a swap is required.
2693dae08bcSDmitry Osipenko 		 */
2703dae08bcSDmitry Osipenko 		if (!state->blending[0].top && state->blending[1].top) {
2713dae08bcSDmitry Osipenko 			blending[0] = foreground;
2723dae08bcSDmitry Osipenko 			blending[1] = background[1];
2733dae08bcSDmitry Osipenko 		} else {
2743dae08bcSDmitry Osipenko 			blending[0] = background[0];
2753dae08bcSDmitry Osipenko 			blending[1] = foreground;
2763dae08bcSDmitry Osipenko 		}
2773dae08bcSDmitry Osipenko 
2783dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
2793dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
280ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
281ab7d3f58SThierry Reding 		break;
282ab7d3f58SThierry Reding 
283ab7d3f58SThierry Reding 	case 2:
284ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
285ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
286ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
287ab7d3f58SThierry Reding 		break;
288ab7d3f58SThierry Reding 	}
289ab7d3f58SThierry Reding }
290ab7d3f58SThierry Reding 
291ab7d3f58SThierry Reding static void tegra_plane_setup_blending(struct tegra_plane *plane,
292ab7d3f58SThierry Reding 				       const struct tegra_dc_window *window)
293ab7d3f58SThierry Reding {
294ab7d3f58SThierry Reding 	u32 value;
295ab7d3f58SThierry Reding 
296ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
297ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
298ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
299ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT);
300ab7d3f58SThierry Reding 
301ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
302ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
303ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
304ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT);
305ab7d3f58SThierry Reding 
306ab7d3f58SThierry Reding 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos);
307ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
308ab7d3f58SThierry Reding }
309ab7d3f58SThierry Reding 
310acc6a3a9SDmitry Osipenko static bool
311acc6a3a9SDmitry Osipenko tegra_plane_use_horizontal_filtering(struct tegra_plane *plane,
312acc6a3a9SDmitry Osipenko 				     const struct tegra_dc_window *window)
313acc6a3a9SDmitry Osipenko {
314acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
315acc6a3a9SDmitry Osipenko 
316acc6a3a9SDmitry Osipenko 	if (window->src.w == window->dst.w)
317acc6a3a9SDmitry Osipenko 		return false;
318acc6a3a9SDmitry Osipenko 
319acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
320acc6a3a9SDmitry Osipenko 		return false;
321acc6a3a9SDmitry Osipenko 
322acc6a3a9SDmitry Osipenko 	return true;
323acc6a3a9SDmitry Osipenko }
324acc6a3a9SDmitry Osipenko 
325acc6a3a9SDmitry Osipenko static bool
326acc6a3a9SDmitry Osipenko tegra_plane_use_vertical_filtering(struct tegra_plane *plane,
327acc6a3a9SDmitry Osipenko 				   const struct tegra_dc_window *window)
328acc6a3a9SDmitry Osipenko {
329acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
330acc6a3a9SDmitry Osipenko 
331acc6a3a9SDmitry Osipenko 	if (window->src.h == window->dst.h)
332acc6a3a9SDmitry Osipenko 		return false;
333acc6a3a9SDmitry Osipenko 
334acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
335acc6a3a9SDmitry Osipenko 		return false;
336acc6a3a9SDmitry Osipenko 
337acc6a3a9SDmitry Osipenko 	if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter)
338acc6a3a9SDmitry Osipenko 		return false;
339acc6a3a9SDmitry Osipenko 
340acc6a3a9SDmitry Osipenko 	return true;
341acc6a3a9SDmitry Osipenko }
342acc6a3a9SDmitry Osipenko 
3431087fac1SThierry Reding static void tegra_dc_setup_window(struct tegra_plane *plane,
34410288eeaSThierry Reding 				  const struct tegra_dc_window *window)
34510288eeaSThierry Reding {
34610288eeaSThierry Reding 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
3471087fac1SThierry Reding 	struct tegra_dc *dc = plane->dc;
34810288eeaSThierry Reding 	bool yuv, planar;
3491087fac1SThierry Reding 	u32 value;
35010288eeaSThierry Reding 
35110288eeaSThierry Reding 	/*
35210288eeaSThierry Reding 	 * For YUV planar modes, the number of bytes per pixel takes into
35310288eeaSThierry Reding 	 * account only the luma component and therefore is 1.
35410288eeaSThierry Reding 	 */
355e16efff4SThierry Reding 	yuv = tegra_plane_format_is_yuv(window->format, &planar, NULL);
35610288eeaSThierry Reding 	if (!yuv)
35710288eeaSThierry Reding 		bpp = window->bits_per_pixel / 8;
35810288eeaSThierry Reding 	else
35910288eeaSThierry Reding 		bpp = planar ? 1 : 2;
36010288eeaSThierry Reding 
3611087fac1SThierry Reding 	tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH);
3621087fac1SThierry Reding 	tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP);
36310288eeaSThierry Reding 
36410288eeaSThierry Reding 	value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
3651087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_POSITION);
36610288eeaSThierry Reding 
36710288eeaSThierry Reding 	value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
3681087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_SIZE);
36910288eeaSThierry Reding 
37010288eeaSThierry Reding 	h_offset = window->src.x * bpp;
37110288eeaSThierry Reding 	v_offset = window->src.y;
37210288eeaSThierry Reding 	h_size = window->src.w * bpp;
37310288eeaSThierry Reding 	v_size = window->src.h;
37410288eeaSThierry Reding 
375cd740777SDmitry Osipenko 	if (window->reflect_x)
376cd740777SDmitry Osipenko 		h_offset += (window->src.w - 1) * bpp;
377cd740777SDmitry Osipenko 
378cd740777SDmitry Osipenko 	if (window->reflect_y)
379cd740777SDmitry Osipenko 		v_offset += window->src.h - 1;
380cd740777SDmitry Osipenko 
38110288eeaSThierry Reding 	value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
3821087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE);
38310288eeaSThierry Reding 
38410288eeaSThierry Reding 	/*
38510288eeaSThierry Reding 	 * For DDA computations the number of bytes per pixel for YUV planar
38610288eeaSThierry Reding 	 * modes needs to take into account all Y, U and V components.
38710288eeaSThierry Reding 	 */
38810288eeaSThierry Reding 	if (yuv && planar)
38910288eeaSThierry Reding 		bpp = 2;
39010288eeaSThierry Reding 
39110288eeaSThierry Reding 	h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
39210288eeaSThierry Reding 	v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
39310288eeaSThierry Reding 
39410288eeaSThierry Reding 	value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
3951087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_DDA_INC);
39610288eeaSThierry Reding 
39710288eeaSThierry Reding 	h_dda = compute_initial_dda(window->src.x);
39810288eeaSThierry Reding 	v_dda = compute_initial_dda(window->src.y);
39910288eeaSThierry Reding 
4001087fac1SThierry Reding 	tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA);
4011087fac1SThierry Reding 	tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA);
40210288eeaSThierry Reding 
4031087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE);
4041087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE);
40510288eeaSThierry Reding 
4061087fac1SThierry Reding 	tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR);
40710288eeaSThierry Reding 
40810288eeaSThierry Reding 	if (yuv && planar) {
4091087fac1SThierry Reding 		tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U);
4101087fac1SThierry Reding 		tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V);
41110288eeaSThierry Reding 		value = window->stride[1] << 16 | window->stride[0];
4121087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE);
41310288eeaSThierry Reding 	} else {
4141087fac1SThierry Reding 		tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE);
41510288eeaSThierry Reding 	}
41610288eeaSThierry Reding 
4171087fac1SThierry Reding 	tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET);
4181087fac1SThierry Reding 	tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET);
41910288eeaSThierry Reding 
420c134f019SThierry Reding 	if (dc->soc->supports_block_linear) {
421c134f019SThierry Reding 		unsigned long height = window->tiling.value;
422c134f019SThierry Reding 
423c134f019SThierry Reding 		switch (window->tiling.mode) {
424c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
425c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_PITCH;
426c134f019SThierry Reding 			break;
427c134f019SThierry Reding 
428c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
429c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_TILED;
430c134f019SThierry Reding 			break;
431c134f019SThierry Reding 
432c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
433c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
434c134f019SThierry Reding 				DC_WINBUF_SURFACE_KIND_BLOCK;
435c134f019SThierry Reding 			break;
436c134f019SThierry Reding 		}
437c134f019SThierry Reding 
4381087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND);
43910288eeaSThierry Reding 	} else {
440c134f019SThierry Reding 		switch (window->tiling.mode) {
441c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
44210288eeaSThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
44310288eeaSThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
444c134f019SThierry Reding 			break;
445c134f019SThierry Reding 
446c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
447c134f019SThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
448c134f019SThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_TILE;
449c134f019SThierry Reding 			break;
450c134f019SThierry Reding 
451c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
4524aa3df71SThierry Reding 			/*
4534aa3df71SThierry Reding 			 * No need to handle this here because ->atomic_check
4544aa3df71SThierry Reding 			 * will already have filtered it out.
4554aa3df71SThierry Reding 			 */
4564aa3df71SThierry Reding 			break;
45710288eeaSThierry Reding 		}
45810288eeaSThierry Reding 
4591087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE);
460c134f019SThierry Reding 	}
46110288eeaSThierry Reding 
46210288eeaSThierry Reding 	value = WIN_ENABLE;
46310288eeaSThierry Reding 
46410288eeaSThierry Reding 	if (yuv) {
46510288eeaSThierry Reding 		/* setup default colorspace conversion coefficients */
4661087fac1SThierry Reding 		tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF);
4671087fac1SThierry Reding 		tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB);
4681087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR);
4691087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR);
4701087fac1SThierry Reding 		tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG);
4711087fac1SThierry Reding 		tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG);
4721087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB);
4731087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB);
47410288eeaSThierry Reding 
47510288eeaSThierry Reding 		value |= CSC_ENABLE;
47610288eeaSThierry Reding 	} else if (window->bits_per_pixel < 24) {
47710288eeaSThierry Reding 		value |= COLOR_EXPAND;
47810288eeaSThierry Reding 	}
47910288eeaSThierry Reding 
480cd740777SDmitry Osipenko 	if (window->reflect_x)
481cd740777SDmitry Osipenko 		value |= H_DIRECTION;
482cd740777SDmitry Osipenko 
483e9e476f7SDmitry Osipenko 	if (window->reflect_y)
48410288eeaSThierry Reding 		value |= V_DIRECTION;
48510288eeaSThierry Reding 
486acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_horizontal_filtering(plane, window)) {
487acc6a3a9SDmitry Osipenko 		/*
488acc6a3a9SDmitry Osipenko 		 * Enable horizontal 6-tap filter and set filtering
489acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
490acc6a3a9SDmitry Osipenko 		 */
491acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0));
492acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1));
493acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2));
494acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3));
495acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4));
496acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5));
497acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6));
498acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7));
499acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8));
500acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9));
501acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10));
502acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11));
503acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12));
504acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13));
505acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14));
506acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15));
507acc6a3a9SDmitry Osipenko 
508acc6a3a9SDmitry Osipenko 		value |= H_FILTER;
509acc6a3a9SDmitry Osipenko 	}
510acc6a3a9SDmitry Osipenko 
511acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_vertical_filtering(plane, window)) {
512acc6a3a9SDmitry Osipenko 		unsigned int i, k;
513acc6a3a9SDmitry Osipenko 
514acc6a3a9SDmitry Osipenko 		/*
515acc6a3a9SDmitry Osipenko 		 * Enable vertical 2-tap filter and set filtering
516acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
517acc6a3a9SDmitry Osipenko 		 */
518acc6a3a9SDmitry Osipenko 		for (i = 0, k = 128; i < 16; i++, k -= 8)
519acc6a3a9SDmitry Osipenko 			tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i));
520acc6a3a9SDmitry Osipenko 
521acc6a3a9SDmitry Osipenko 		value |= V_FILTER;
522acc6a3a9SDmitry Osipenko 	}
523acc6a3a9SDmitry Osipenko 
5241087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
52510288eeaSThierry Reding 
526a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending)
527ab7d3f58SThierry Reding 		tegra_plane_setup_blending_legacy(plane);
528a43d0a00SDmitry Osipenko 	else
529a43d0a00SDmitry Osipenko 		tegra_plane_setup_blending(plane, window);
530c7679306SThierry Reding }
531c7679306SThierry Reding 
532511c7023SThierry Reding static const u32 tegra20_primary_formats[] = {
533511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
534511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
535c7679306SThierry Reding 	DRM_FORMAT_RGB565,
536511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
537511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
538511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
539ebae8d07SThierry Reding 	/* non-native formats */
540ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
541ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
542ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
543ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
544511c7023SThierry Reding };
545511c7023SThierry Reding 
546e90124cbSThierry Reding static const u64 tegra20_modifiers[] = {
547e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
548e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED,
549e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
550e90124cbSThierry Reding };
551e90124cbSThierry Reding 
552511c7023SThierry Reding static const u32 tegra114_primary_formats[] = {
553511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
554511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
555511c7023SThierry Reding 	DRM_FORMAT_RGB565,
556511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
557511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
558511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
559511c7023SThierry Reding 	/* new on Tegra114 */
560511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
561511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
562511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
563511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
564511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
565511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
566511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
567511c7023SThierry Reding 	DRM_FORMAT_BGR565,
568511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
569511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
570511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
571511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
572511c7023SThierry Reding };
573511c7023SThierry Reding 
574511c7023SThierry Reding static const u32 tegra124_primary_formats[] = {
575511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
576511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
577511c7023SThierry Reding 	DRM_FORMAT_RGB565,
578511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
579511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
580511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
581511c7023SThierry Reding 	/* new on Tegra114 */
582511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
583511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
584511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
585511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
586511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
587511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
588511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
589511c7023SThierry Reding 	DRM_FORMAT_BGR565,
590511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
591511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
592511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
593511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
594511c7023SThierry Reding 	/* new on Tegra124 */
595511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
596511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
597c7679306SThierry Reding };
598c7679306SThierry Reding 
599e90124cbSThierry Reding static const u64 tegra124_modifiers[] = {
600e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
601e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
602e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
603e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
604e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
605e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
606e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
607e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
608e90124cbSThierry Reding };
609e90124cbSThierry Reding 
6104aa3df71SThierry Reding static int tegra_plane_atomic_check(struct drm_plane *plane,
6117c11b99aSMaxime Ripard 				    struct drm_atomic_state *state)
6124aa3df71SThierry Reding {
6137c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
6147c11b99aSMaxime Ripard 										 plane);
615ba5c1649SMaxime Ripard 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
616cd740777SDmitry Osipenko 	unsigned int supported_rotation = DRM_MODE_ROTATE_0 |
617cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_X |
618cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_Y;
619ba5c1649SMaxime Ripard 	unsigned int rotation = new_plane_state->rotation;
6208f604f8cSThierry Reding 	struct tegra_bo_tiling *tiling = &plane_state->tiling;
62147802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
622ba5c1649SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
623c7679306SThierry Reding 	int err;
624c7679306SThierry Reding 
62504d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
62604d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
62704d5d5dfSDmitry Osipenko 
6284aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
62904d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
63004d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
6314aa3df71SThierry Reding 		return 0;
63204d5d5dfSDmitry Osipenko 	}
6334aa3df71SThierry Reding 
634ba5c1649SMaxime Ripard 	err = tegra_plane_format(new_plane_state->fb->format->format,
6353dae08bcSDmitry Osipenko 				 &plane_state->format,
6368f604f8cSThierry Reding 				 &plane_state->swap);
6374aa3df71SThierry Reding 	if (err < 0)
6384aa3df71SThierry Reding 		return err;
6394aa3df71SThierry Reding 
640ebae8d07SThierry Reding 	/*
641ebae8d07SThierry Reding 	 * Tegra20 and Tegra30 are special cases here because they support
642ebae8d07SThierry Reding 	 * only variants of specific formats with an alpha component, but not
643ebae8d07SThierry Reding 	 * the corresponding opaque formats. However, the opaque formats can
644ebae8d07SThierry Reding 	 * be emulated by disabling alpha blending for the plane.
645ebae8d07SThierry Reding 	 */
646a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending) {
6473dae08bcSDmitry Osipenko 		err = tegra_plane_setup_legacy_state(tegra, plane_state);
648ebae8d07SThierry Reding 		if (err < 0)
649ebae8d07SThierry Reding 			return err;
650ebae8d07SThierry Reding 	}
651ebae8d07SThierry Reding 
652ba5c1649SMaxime Ripard 	err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
6538f604f8cSThierry Reding 	if (err < 0)
6548f604f8cSThierry Reding 		return err;
6558f604f8cSThierry Reding 
6568f604f8cSThierry Reding 	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
6574aa3df71SThierry Reding 	    !dc->soc->supports_block_linear) {
6584aa3df71SThierry Reding 		DRM_ERROR("hardware doesn't support block linear mode\n");
6594aa3df71SThierry Reding 		return -EINVAL;
6604aa3df71SThierry Reding 	}
6614aa3df71SThierry Reding 
662cd740777SDmitry Osipenko 	/*
663cd740777SDmitry Osipenko 	 * Older userspace used custom BO flag in order to specify the Y
664cd740777SDmitry Osipenko 	 * reflection, while modern userspace uses the generic DRM rotation
665cd740777SDmitry Osipenko 	 * property in order to achieve the same result.  The legacy BO flag
666cd740777SDmitry Osipenko 	 * duplicates the DRM rotation property when both are set.
667cd740777SDmitry Osipenko 	 */
668ba5c1649SMaxime Ripard 	if (tegra_fb_is_bottom_up(new_plane_state->fb))
669cd740777SDmitry Osipenko 		rotation |= DRM_MODE_REFLECT_Y;
670cd740777SDmitry Osipenko 
671cd740777SDmitry Osipenko 	rotation = drm_rotation_simplify(rotation, supported_rotation);
672cd740777SDmitry Osipenko 
673cd740777SDmitry Osipenko 	if (rotation & DRM_MODE_REFLECT_X)
674cd740777SDmitry Osipenko 		plane_state->reflect_x = true;
675cd740777SDmitry Osipenko 	else
676cd740777SDmitry Osipenko 		plane_state->reflect_x = false;
677995c5a50SThierry Reding 
678995c5a50SThierry Reding 	if (rotation & DRM_MODE_REFLECT_Y)
679e9e476f7SDmitry Osipenko 		plane_state->reflect_y = true;
680995c5a50SThierry Reding 	else
681e9e476f7SDmitry Osipenko 		plane_state->reflect_y = false;
682995c5a50SThierry Reding 
6834aa3df71SThierry Reding 	/*
6844aa3df71SThierry Reding 	 * Tegra doesn't support different strides for U and V planes so we
6854aa3df71SThierry Reding 	 * error out if the user tries to display a framebuffer with such a
6864aa3df71SThierry Reding 	 * configuration.
6874aa3df71SThierry Reding 	 */
688ba5c1649SMaxime Ripard 	if (new_plane_state->fb->format->num_planes > 2) {
689ba5c1649SMaxime Ripard 		if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
6904aa3df71SThierry Reding 			DRM_ERROR("unsupported UV-plane configuration\n");
6914aa3df71SThierry Reding 			return -EINVAL;
6924aa3df71SThierry Reding 		}
6934aa3df71SThierry Reding 	}
6944aa3df71SThierry Reding 
695ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
69647802b09SThierry Reding 	if (err < 0)
69747802b09SThierry Reding 		return err;
69847802b09SThierry Reding 
6994aa3df71SThierry Reding 	return 0;
7004aa3df71SThierry Reding }
7014aa3df71SThierry Reding 
702a4bfa096SThierry Reding static void tegra_plane_atomic_disable(struct drm_plane *plane,
703977697e2SMaxime Ripard 				       struct drm_atomic_state *state)
70480d3eef1SDmitry Osipenko {
705977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
706977697e2SMaxime Ripard 									   plane);
707a4bfa096SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
70880d3eef1SDmitry Osipenko 	u32 value;
70980d3eef1SDmitry Osipenko 
710a4bfa096SThierry Reding 	/* rien ne va plus */
711a4bfa096SThierry Reding 	if (!old_state || !old_state->crtc)
712a4bfa096SThierry Reding 		return;
713a4bfa096SThierry Reding 
7141087fac1SThierry Reding 	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
71580d3eef1SDmitry Osipenko 	value &= ~WIN_ENABLE;
7161087fac1SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
71780d3eef1SDmitry Osipenko }
71880d3eef1SDmitry Osipenko 
7194aa3df71SThierry Reding static void tegra_plane_atomic_update(struct drm_plane *plane,
720977697e2SMaxime Ripard 				      struct drm_atomic_state *state)
7214aa3df71SThierry Reding {
72237418bf1SMaxime Ripard 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
72337418bf1SMaxime Ripard 									   plane);
72441016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
725e05162c0SMaxime Ripard 	struct drm_framebuffer *fb = new_state->fb;
7264aa3df71SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
7274aa3df71SThierry Reding 	struct tegra_dc_window window;
7284aa3df71SThierry Reding 	unsigned int i;
7294aa3df71SThierry Reding 
7304aa3df71SThierry Reding 	/* rien ne va plus */
731e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
7324aa3df71SThierry Reding 		return;
7334aa3df71SThierry Reding 
734e05162c0SMaxime Ripard 	if (!new_state->visible)
735977697e2SMaxime Ripard 		return tegra_plane_atomic_disable(plane, state);
73680d3eef1SDmitry Osipenko 
737c7679306SThierry Reding 	memset(&window, 0, sizeof(window));
738e05162c0SMaxime Ripard 	window.src.x = new_state->src.x1 >> 16;
739e05162c0SMaxime Ripard 	window.src.y = new_state->src.y1 >> 16;
740e05162c0SMaxime Ripard 	window.src.w = drm_rect_width(&new_state->src) >> 16;
741e05162c0SMaxime Ripard 	window.src.h = drm_rect_height(&new_state->src) >> 16;
742e05162c0SMaxime Ripard 	window.dst.x = new_state->dst.x1;
743e05162c0SMaxime Ripard 	window.dst.y = new_state->dst.y1;
744e05162c0SMaxime Ripard 	window.dst.w = drm_rect_width(&new_state->dst);
745e05162c0SMaxime Ripard 	window.dst.h = drm_rect_height(&new_state->dst);
746272725c7SVille Syrjälä 	window.bits_per_pixel = fb->format->cpp[0] * 8;
74741016fe1SMaxime Ripard 	window.reflect_x = tegra_plane_state->reflect_x;
74841016fe1SMaxime Ripard 	window.reflect_y = tegra_plane_state->reflect_y;
749c7679306SThierry Reding 
7508f604f8cSThierry Reding 	/* copy from state */
751e05162c0SMaxime Ripard 	window.zpos = new_state->normalized_zpos;
75241016fe1SMaxime Ripard 	window.tiling = tegra_plane_state->tiling;
75341016fe1SMaxime Ripard 	window.format = tegra_plane_state->format;
75441016fe1SMaxime Ripard 	window.swap = tegra_plane_state->swap;
755c7679306SThierry Reding 
756bcb0b461SVille Syrjälä 	for (i = 0; i < fb->format->num_planes; i++) {
75741016fe1SMaxime Ripard 		window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i];
75808ee0178SDmitry Osipenko 
75908ee0178SDmitry Osipenko 		/*
76008ee0178SDmitry Osipenko 		 * Tegra uses a shared stride for UV planes. Framebuffers are
76108ee0178SDmitry Osipenko 		 * already checked for this in the tegra_plane_atomic_check()
76208ee0178SDmitry Osipenko 		 * function, so it's safe to ignore the V-plane pitch here.
76308ee0178SDmitry Osipenko 		 */
76408ee0178SDmitry Osipenko 		if (i < 2)
7654aa3df71SThierry Reding 			window.stride[i] = fb->pitches[i];
766c7679306SThierry Reding 	}
767c7679306SThierry Reding 
7681087fac1SThierry Reding 	tegra_dc_setup_window(p, &window);
7694aa3df71SThierry Reding }
7704aa3df71SThierry Reding 
771a4bfa096SThierry Reding static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
7722e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
7732e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
7744aa3df71SThierry Reding 	.atomic_check = tegra_plane_atomic_check,
7754aa3df71SThierry Reding 	.atomic_disable = tegra_plane_atomic_disable,
776a4bfa096SThierry Reding 	.atomic_update = tegra_plane_atomic_update,
777c7679306SThierry Reding };
778c7679306SThierry Reding 
77989f65018SThierry Reding static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm)
780c7679306SThierry Reding {
781518e6227SThierry Reding 	/*
782518e6227SThierry Reding 	 * Ideally this would use drm_crtc_mask(), but that would require the
783518e6227SThierry Reding 	 * CRTC to already be in the mode_config's list of CRTCs. However, it
784518e6227SThierry Reding 	 * will only be added to that list in the drm_crtc_init_with_planes()
785518e6227SThierry Reding 	 * (in tegra_dc_init()), which in turn requires registration of these
786518e6227SThierry Reding 	 * planes. So we have ourselves a nice little chicken and egg problem
787518e6227SThierry Reding 	 * here.
788518e6227SThierry Reding 	 *
789518e6227SThierry Reding 	 * We work around this by manually creating the mask from the number
790518e6227SThierry Reding 	 * of CRTCs that have been registered, and should therefore always be
791518e6227SThierry Reding 	 * the same as drm_crtc_index() after registration.
792518e6227SThierry Reding 	 */
79389f65018SThierry Reding 	return 1 << drm->mode_config.num_crtc;
79489f65018SThierry Reding }
79589f65018SThierry Reding 
79689f65018SThierry Reding static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
79789f65018SThierry Reding 						    struct tegra_dc *dc)
79889f65018SThierry Reding {
79989f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
80047307954SThierry Reding 	enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
801c7679306SThierry Reding 	struct tegra_plane *plane;
802c7679306SThierry Reding 	unsigned int num_formats;
803e90124cbSThierry Reding 	const u64 *modifiers;
804c7679306SThierry Reding 	const u32 *formats;
805c7679306SThierry Reding 	int err;
806c7679306SThierry Reding 
807c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
808c7679306SThierry Reding 	if (!plane)
809c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
810c7679306SThierry Reding 
8111087fac1SThierry Reding 	/* Always use window A as primary window */
8121087fac1SThierry Reding 	plane->offset = 0xa00;
813c4755fb9SThierry Reding 	plane->index = 0;
8141087fac1SThierry Reding 	plane->dc = dc;
8151087fac1SThierry Reding 
8161087fac1SThierry Reding 	num_formats = dc->soc->num_primary_formats;
8171087fac1SThierry Reding 	formats = dc->soc->primary_formats;
818e90124cbSThierry Reding 	modifiers = dc->soc->modifiers;
819c4755fb9SThierry Reding 
82004d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
82104d5d5dfSDmitry Osipenko 	if (err) {
82204d5d5dfSDmitry Osipenko 		kfree(plane);
82304d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
82404d5d5dfSDmitry Osipenko 	}
82504d5d5dfSDmitry Osipenko 
826518e6227SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
827c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
828e90124cbSThierry Reding 				       num_formats, modifiers, type, NULL);
829c7679306SThierry Reding 	if (err < 0) {
830c7679306SThierry Reding 		kfree(plane);
831c7679306SThierry Reding 		return ERR_PTR(err);
832c7679306SThierry Reding 	}
833c7679306SThierry Reding 
834a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
8353dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
836ab7d3f58SThierry Reding 
837995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
838995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
839995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
8404fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
841cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
842995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
843995c5a50SThierry Reding 	if (err < 0)
844995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
845995c5a50SThierry Reding 			err);
846995c5a50SThierry Reding 
847c7679306SThierry Reding 	return &plane->base;
848c7679306SThierry Reding }
849c7679306SThierry Reding 
850d5ec699dSThierry Reding static const u32 tegra_legacy_cursor_plane_formats[] = {
851c7679306SThierry Reding 	DRM_FORMAT_RGBA8888,
852c7679306SThierry Reding };
853c7679306SThierry Reding 
854d5ec699dSThierry Reding static const u32 tegra_cursor_plane_formats[] = {
855d5ec699dSThierry Reding 	DRM_FORMAT_ARGB8888,
856d5ec699dSThierry Reding };
857d5ec699dSThierry Reding 
8584aa3df71SThierry Reding static int tegra_cursor_atomic_check(struct drm_plane *plane,
8597c11b99aSMaxime Ripard 				     struct drm_atomic_state *state)
860c7679306SThierry Reding {
8617c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
8627c11b99aSMaxime Ripard 										 plane);
86304d5d5dfSDmitry Osipenko 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
86447802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
86547802b09SThierry Reding 	int err;
86647802b09SThierry Reding 
86704d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
86804d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
86904d5d5dfSDmitry Osipenko 
8704aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
87104d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
87204d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
8734aa3df71SThierry Reding 		return 0;
87404d5d5dfSDmitry Osipenko 	}
875c7679306SThierry Reding 
876c7679306SThierry Reding 	/* scaling not supported for cursor */
877ba5c1649SMaxime Ripard 	if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) ||
878ba5c1649SMaxime Ripard 	    (new_plane_state->src_h >> 16 != new_plane_state->crtc_h))
879c7679306SThierry Reding 		return -EINVAL;
880c7679306SThierry Reding 
881c7679306SThierry Reding 	/* only square cursors supported */
882ba5c1649SMaxime Ripard 	if (new_plane_state->src_w != new_plane_state->src_h)
883c7679306SThierry Reding 		return -EINVAL;
884c7679306SThierry Reding 
885ba5c1649SMaxime Ripard 	if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 &&
886ba5c1649SMaxime Ripard 	    new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256)
8874aa3df71SThierry Reding 		return -EINVAL;
8884aa3df71SThierry Reding 
889ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
89047802b09SThierry Reding 	if (err < 0)
89147802b09SThierry Reding 		return err;
89247802b09SThierry Reding 
8934aa3df71SThierry Reding 	return 0;
8944aa3df71SThierry Reding }
8954aa3df71SThierry Reding 
896cae7472eSThierry Reding static void __tegra_cursor_atomic_update(struct drm_plane *plane,
897cae7472eSThierry Reding 					 struct drm_plane_state *new_state)
8984aa3df71SThierry Reding {
89941016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
900e05162c0SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
901d5ec699dSThierry Reding 	struct tegra_drm *tegra = plane->dev->dev_private;
902d5ec699dSThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
903d5ec699dSThierry Reding 	u64 dma_mask = *dc->dev->dma_mask;
904d5ec699dSThierry Reding #endif
905d5ec699dSThierry Reding 	unsigned int x, y;
906d5ec699dSThierry Reding 	u32 value = 0;
9074aa3df71SThierry Reding 
9084aa3df71SThierry Reding 	/* rien ne va plus */
909e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
9104aa3df71SThierry Reding 		return;
9114aa3df71SThierry Reding 
912d5ec699dSThierry Reding 	/*
913d5ec699dSThierry Reding 	 * Legacy display supports hardware clipping of the cursor, but
914d5ec699dSThierry Reding 	 * nvdisplay relies on software to clip the cursor to the screen.
915d5ec699dSThierry Reding 	 */
916d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay)
917d5ec699dSThierry Reding 		value |= CURSOR_CLIP_DISPLAY;
918d5ec699dSThierry Reding 
919e05162c0SMaxime Ripard 	switch (new_state->crtc_w) {
920c7679306SThierry Reding 	case 32:
921c7679306SThierry Reding 		value |= CURSOR_SIZE_32x32;
922c7679306SThierry Reding 		break;
923c7679306SThierry Reding 
924c7679306SThierry Reding 	case 64:
925c7679306SThierry Reding 		value |= CURSOR_SIZE_64x64;
926c7679306SThierry Reding 		break;
927c7679306SThierry Reding 
928c7679306SThierry Reding 	case 128:
929c7679306SThierry Reding 		value |= CURSOR_SIZE_128x128;
930c7679306SThierry Reding 		break;
931c7679306SThierry Reding 
932c7679306SThierry Reding 	case 256:
933c7679306SThierry Reding 		value |= CURSOR_SIZE_256x256;
934c7679306SThierry Reding 		break;
935c7679306SThierry Reding 
936c7679306SThierry Reding 	default:
937c52e167bSThierry Reding 		WARN(1, "cursor size %ux%u not supported\n",
938e05162c0SMaxime Ripard 		     new_state->crtc_w, new_state->crtc_h);
9394aa3df71SThierry Reding 		return;
940c7679306SThierry Reding 	}
941c7679306SThierry Reding 
94241016fe1SMaxime Ripard 	value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff;
943c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
944c7679306SThierry Reding 
945c7679306SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
946d5ec699dSThierry Reding 	value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
947c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
948c7679306SThierry Reding #endif
949c7679306SThierry Reding 
950c7679306SThierry Reding 	/* enable cursor and set blend mode */
951c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
952c7679306SThierry Reding 	value |= CURSOR_ENABLE;
953c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
954c7679306SThierry Reding 
955c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
956c7679306SThierry Reding 	value &= ~CURSOR_DST_BLEND_MASK;
957c7679306SThierry Reding 	value &= ~CURSOR_SRC_BLEND_MASK;
958d5ec699dSThierry Reding 
959d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay)
960d5ec699dSThierry Reding 		value &= ~CURSOR_COMPOSITION_MODE_XOR;
961d5ec699dSThierry Reding 	else
962c7679306SThierry Reding 		value |= CURSOR_MODE_NORMAL;
963d5ec699dSThierry Reding 
964c7679306SThierry Reding 	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
965c7679306SThierry Reding 	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
966c7679306SThierry Reding 	value |= CURSOR_ALPHA;
967c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
968c7679306SThierry Reding 
969d5ec699dSThierry Reding 	/* nvdisplay relies on software for clipping */
970d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay) {
971d5ec699dSThierry Reding 		struct drm_rect src;
972d5ec699dSThierry Reding 
973d5ec699dSThierry Reding 		x = new_state->dst.x1;
974d5ec699dSThierry Reding 		y = new_state->dst.y1;
975d5ec699dSThierry Reding 
976d5ec699dSThierry Reding 		drm_rect_fp_to_int(&src, &new_state->src);
977d5ec699dSThierry Reding 
978d5ec699dSThierry Reding 		value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
979d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
980d5ec699dSThierry Reding 
981d5ec699dSThierry Reding 		value = (drm_rect_height(&src) & tegra->vmask) << 16 |
982d5ec699dSThierry Reding 			(drm_rect_width(&src) & tegra->hmask);
983d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
984d5ec699dSThierry Reding 	} else {
985d5ec699dSThierry Reding 		x = new_state->crtc_x;
986d5ec699dSThierry Reding 		y = new_state->crtc_y;
987d5ec699dSThierry Reding 	}
988d5ec699dSThierry Reding 
989c7679306SThierry Reding 	/* position the cursor */
990d5ec699dSThierry Reding 	value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
991c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
992c7679306SThierry Reding }
993c7679306SThierry Reding 
994cae7472eSThierry Reding static void tegra_cursor_atomic_update(struct drm_plane *plane,
995cae7472eSThierry Reding 				       struct drm_atomic_state *state)
996cae7472eSThierry Reding {
997cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
998cae7472eSThierry Reding 
999cae7472eSThierry Reding 	__tegra_cursor_atomic_update(plane, new_state);
1000cae7472eSThierry Reding }
1001cae7472eSThierry Reding 
10024aa3df71SThierry Reding static void tegra_cursor_atomic_disable(struct drm_plane *plane,
1003977697e2SMaxime Ripard 					struct drm_atomic_state *state)
1004c7679306SThierry Reding {
1005977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
1006977697e2SMaxime Ripard 									   plane);
10074aa3df71SThierry Reding 	struct tegra_dc *dc;
1008c7679306SThierry Reding 	u32 value;
1009c7679306SThierry Reding 
10104aa3df71SThierry Reding 	/* rien ne va plus */
10114aa3df71SThierry Reding 	if (!old_state || !old_state->crtc)
10124aa3df71SThierry Reding 		return;
10134aa3df71SThierry Reding 
10144aa3df71SThierry Reding 	dc = to_tegra_dc(old_state->crtc);
1015c7679306SThierry Reding 
1016c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
1017c7679306SThierry Reding 	value &= ~CURSOR_ENABLE;
1018c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
1019c7679306SThierry Reding }
1020c7679306SThierry Reding 
1021cae7472eSThierry Reding static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state)
1022cae7472eSThierry Reding {
1023cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1024cae7472eSThierry Reding 	struct drm_crtc_state *crtc_state;
1025cae7472eSThierry Reding 	int min_scale, max_scale;
1026cae7472eSThierry Reding 	int err;
1027cae7472eSThierry Reding 
1028cae7472eSThierry Reding 	crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
1029cae7472eSThierry Reding 	if (WARN_ON(!crtc_state))
1030cae7472eSThierry Reding 		return -EINVAL;
1031cae7472eSThierry Reding 
1032cae7472eSThierry Reding 	if (!crtc_state->active)
1033cae7472eSThierry Reding 		return -EINVAL;
1034cae7472eSThierry Reding 
1035cae7472eSThierry Reding 	if (plane->state->crtc != new_state->crtc ||
1036cae7472eSThierry Reding 	    plane->state->src_w != new_state->src_w ||
1037cae7472eSThierry Reding 	    plane->state->src_h != new_state->src_h ||
1038cae7472eSThierry Reding 	    plane->state->crtc_w != new_state->crtc_w ||
1039cae7472eSThierry Reding 	    plane->state->crtc_h != new_state->crtc_h ||
1040cae7472eSThierry Reding 	    plane->state->fb != new_state->fb ||
1041cae7472eSThierry Reding 	    plane->state->fb == NULL)
1042cae7472eSThierry Reding 		return -EINVAL;
1043cae7472eSThierry Reding 
1044cae7472eSThierry Reding 	min_scale = (1 << 16) / 8;
1045cae7472eSThierry Reding 	max_scale = (8 << 16) / 1;
1046cae7472eSThierry Reding 
1047cae7472eSThierry Reding 	err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale,
1048cae7472eSThierry Reding 						  true, true);
1049cae7472eSThierry Reding 	if (err < 0)
1050cae7472eSThierry Reding 		return err;
1051cae7472eSThierry Reding 
1052cae7472eSThierry Reding 	if (new_state->visible != plane->state->visible)
1053cae7472eSThierry Reding 		return -EINVAL;
1054cae7472eSThierry Reding 
1055cae7472eSThierry Reding 	return 0;
1056cae7472eSThierry Reding }
1057cae7472eSThierry Reding 
1058cae7472eSThierry Reding static void tegra_cursor_atomic_async_update(struct drm_plane *plane,
1059cae7472eSThierry Reding 					     struct drm_atomic_state *state)
1060cae7472eSThierry Reding {
1061cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1062cae7472eSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
1063cae7472eSThierry Reding 
1064cae7472eSThierry Reding 	plane->state->src_x = new_state->src_x;
1065cae7472eSThierry Reding 	plane->state->src_y = new_state->src_y;
1066cae7472eSThierry Reding 	plane->state->crtc_x = new_state->crtc_x;
1067cae7472eSThierry Reding 	plane->state->crtc_y = new_state->crtc_y;
1068cae7472eSThierry Reding 
1069cae7472eSThierry Reding 	if (new_state->visible) {
1070cae7472eSThierry Reding 		struct tegra_plane *p = to_tegra_plane(plane);
1071cae7472eSThierry Reding 		u32 value;
1072cae7472eSThierry Reding 
1073cae7472eSThierry Reding 		__tegra_cursor_atomic_update(plane, new_state);
1074cae7472eSThierry Reding 
1075cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE;
1076cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1077cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1078cae7472eSThierry Reding 
1079cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ;
1080cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1081cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1082cae7472eSThierry Reding 	}
1083cae7472eSThierry Reding }
1084cae7472eSThierry Reding 
10854aa3df71SThierry Reding static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
10862e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
10872e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
10884aa3df71SThierry Reding 	.atomic_check = tegra_cursor_atomic_check,
10894aa3df71SThierry Reding 	.atomic_update = tegra_cursor_atomic_update,
10904aa3df71SThierry Reding 	.atomic_disable = tegra_cursor_atomic_disable,
1091cae7472eSThierry Reding 	.atomic_async_check = tegra_cursor_atomic_async_check,
1092cae7472eSThierry Reding 	.atomic_async_update = tegra_cursor_atomic_async_update,
1093c7679306SThierry Reding };
1094c7679306SThierry Reding 
1095be4306adSDaniel Vetter static const uint64_t linear_modifiers[] = {
1096be4306adSDaniel Vetter 	DRM_FORMAT_MOD_LINEAR,
1097be4306adSDaniel Vetter 	DRM_FORMAT_MOD_INVALID
1098be4306adSDaniel Vetter };
1099be4306adSDaniel Vetter 
1100c7679306SThierry Reding static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
1101c7679306SThierry Reding 						      struct tegra_dc *dc)
1102c7679306SThierry Reding {
110389f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1104c7679306SThierry Reding 	struct tegra_plane *plane;
1105c7679306SThierry Reding 	unsigned int num_formats;
1106c7679306SThierry Reding 	const u32 *formats;
1107c7679306SThierry Reding 	int err;
1108c7679306SThierry Reding 
1109c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1110c7679306SThierry Reding 	if (!plane)
1111c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1112c7679306SThierry Reding 
111347802b09SThierry Reding 	/*
1114a1df3b24SThierry Reding 	 * This index is kind of fake. The cursor isn't a regular plane, but
1115a1df3b24SThierry Reding 	 * its update and activation request bits in DC_CMD_STATE_CONTROL do
1116a1df3b24SThierry Reding 	 * use the same programming. Setting this fake index here allows the
1117a1df3b24SThierry Reding 	 * code in tegra_add_plane_state() to do the right thing without the
1118a1df3b24SThierry Reding 	 * need to special-casing the cursor plane.
111947802b09SThierry Reding 	 */
112047802b09SThierry Reding 	plane->index = 6;
11211087fac1SThierry Reding 	plane->dc = dc;
112247802b09SThierry Reding 
1123d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay) {
1124d5ec699dSThierry Reding 		num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
1125d5ec699dSThierry Reding 		formats = tegra_legacy_cursor_plane_formats;
112604d5d5dfSDmitry Osipenko 
112704d5d5dfSDmitry Osipenko 		err = tegra_plane_interconnect_init(plane);
112804d5d5dfSDmitry Osipenko 		if (err) {
112904d5d5dfSDmitry Osipenko 			kfree(plane);
113004d5d5dfSDmitry Osipenko 			return ERR_PTR(err);
113104d5d5dfSDmitry Osipenko 		}
1132d5ec699dSThierry Reding 	} else {
1133c7679306SThierry Reding 		num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
1134c7679306SThierry Reding 		formats = tegra_cursor_plane_formats;
1135d5ec699dSThierry Reding 	}
1136c7679306SThierry Reding 
113789f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1138c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
1139be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1140e6fc3b68SBen Widawsky 				       DRM_PLANE_TYPE_CURSOR, NULL);
1141c7679306SThierry Reding 	if (err < 0) {
1142c7679306SThierry Reding 		kfree(plane);
1143c7679306SThierry Reding 		return ERR_PTR(err);
1144c7679306SThierry Reding 	}
1145c7679306SThierry Reding 
11464aa3df71SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
1147fce3a51dSThierry Reding 	drm_plane_create_zpos_immutable_property(&plane->base, 255);
11484aa3df71SThierry Reding 
1149c7679306SThierry Reding 	return &plane->base;
1150c7679306SThierry Reding }
1151c7679306SThierry Reding 
1152511c7023SThierry Reding static const u32 tegra20_overlay_formats[] = {
1153511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1154511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1155dee8268fSThierry Reding 	DRM_FORMAT_RGB565,
1156511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1157511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1158511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1159ebae8d07SThierry Reding 	/* non-native formats */
1160ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
1161ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
1162ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
1163ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
1164511c7023SThierry Reding 	/* planar formats */
1165511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1166511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1167511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1168511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1169511c7023SThierry Reding };
1170511c7023SThierry Reding 
1171511c7023SThierry Reding static const u32 tegra114_overlay_formats[] = {
1172511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1173511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1174511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1175511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1176511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1177511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1178511c7023SThierry Reding 	/* new on Tegra114 */
1179511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1180511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1181511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1182511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1183511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1184511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1185511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1186511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1187511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1188511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1189511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1190511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1191511c7023SThierry Reding 	/* planar formats */
1192511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1193511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1194511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1195511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1196511c7023SThierry Reding };
1197511c7023SThierry Reding 
1198511c7023SThierry Reding static const u32 tegra124_overlay_formats[] = {
1199511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1200511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1201511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1202511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1203511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1204511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1205511c7023SThierry Reding 	/* new on Tegra114 */
1206511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1207511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1208511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1209511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1210511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1211511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1212511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1213511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1214511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1215511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1216511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1217511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1218511c7023SThierry Reding 	/* new on Tegra124 */
1219511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
1220511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
1221511c7023SThierry Reding 	/* planar formats */
1222dee8268fSThierry Reding 	DRM_FORMAT_UYVY,
1223f925390eSThierry Reding 	DRM_FORMAT_YUYV,
1224dee8268fSThierry Reding 	DRM_FORMAT_YUV420,
1225dee8268fSThierry Reding 	DRM_FORMAT_YUV422,
1226dee8268fSThierry Reding };
1227dee8268fSThierry Reding 
1228c7679306SThierry Reding static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
1229c7679306SThierry Reding 						       struct tegra_dc *dc,
12309f446d83SDmitry Osipenko 						       unsigned int index,
12319f446d83SDmitry Osipenko 						       bool cursor)
1232dee8268fSThierry Reding {
123389f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1234dee8268fSThierry Reding 	struct tegra_plane *plane;
1235c7679306SThierry Reding 	unsigned int num_formats;
12369f446d83SDmitry Osipenko 	enum drm_plane_type type;
1237c7679306SThierry Reding 	const u32 *formats;
1238c7679306SThierry Reding 	int err;
1239dee8268fSThierry Reding 
1240f002abc1SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1241dee8268fSThierry Reding 	if (!plane)
1242c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1243dee8268fSThierry Reding 
12441087fac1SThierry Reding 	plane->offset = 0xa00 + 0x200 * index;
1245c7679306SThierry Reding 	plane->index = index;
12461087fac1SThierry Reding 	plane->dc = dc;
1247dee8268fSThierry Reding 
1248511c7023SThierry Reding 	num_formats = dc->soc->num_overlay_formats;
1249511c7023SThierry Reding 	formats = dc->soc->overlay_formats;
1250c7679306SThierry Reding 
125104d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
125204d5d5dfSDmitry Osipenko 	if (err) {
125304d5d5dfSDmitry Osipenko 		kfree(plane);
125404d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
125504d5d5dfSDmitry Osipenko 	}
125604d5d5dfSDmitry Osipenko 
12579f446d83SDmitry Osipenko 	if (!cursor)
12589f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_OVERLAY;
12599f446d83SDmitry Osipenko 	else
12609f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_CURSOR;
12619f446d83SDmitry Osipenko 
126289f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1263301e0ddbSThierry Reding 				       &tegra_plane_funcs, formats,
1264be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1265be4306adSDaniel Vetter 				       type, NULL);
1266f002abc1SThierry Reding 	if (err < 0) {
1267f002abc1SThierry Reding 		kfree(plane);
1268c7679306SThierry Reding 		return ERR_PTR(err);
1269dee8268fSThierry Reding 	}
1270c7679306SThierry Reding 
1271a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
12723dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
1273ab7d3f58SThierry Reding 
1274995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
1275995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
1276995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
12774fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
1278cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
1279995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
1280995c5a50SThierry Reding 	if (err < 0)
1281995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
1282995c5a50SThierry Reding 			err);
1283995c5a50SThierry Reding 
1284c7679306SThierry Reding 	return &plane->base;
1285c7679306SThierry Reding }
1286c7679306SThierry Reding 
128747307954SThierry Reding static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
128847307954SThierry Reding 						    struct tegra_dc *dc)
1289c7679306SThierry Reding {
129047307954SThierry Reding 	struct drm_plane *plane, *primary = NULL;
129147307954SThierry Reding 	unsigned int i, j;
129247307954SThierry Reding 
129347307954SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
129447307954SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
129547307954SThierry Reding 
129647307954SThierry Reding 		if (wgrp->dc == dc->pipe) {
129747307954SThierry Reding 			for (j = 0; j < wgrp->num_windows; j++) {
129847307954SThierry Reding 				unsigned int index = wgrp->windows[j];
129947307954SThierry Reding 
130047307954SThierry Reding 				plane = tegra_shared_plane_create(drm, dc,
130147307954SThierry Reding 								  wgrp->index,
130247307954SThierry Reding 								  index);
130347307954SThierry Reding 				if (IS_ERR(plane))
130447307954SThierry Reding 					return plane;
130547307954SThierry Reding 
130647307954SThierry Reding 				/*
130747307954SThierry Reding 				 * Choose the first shared plane owned by this
130847307954SThierry Reding 				 * head as the primary plane.
130947307954SThierry Reding 				 */
131047307954SThierry Reding 				if (!primary) {
131147307954SThierry Reding 					plane->type = DRM_PLANE_TYPE_PRIMARY;
131247307954SThierry Reding 					primary = plane;
131347307954SThierry Reding 				}
131447307954SThierry Reding 			}
131547307954SThierry Reding 		}
131647307954SThierry Reding 	}
131747307954SThierry Reding 
131847307954SThierry Reding 	return primary;
131947307954SThierry Reding }
132047307954SThierry Reding 
132147307954SThierry Reding static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
132247307954SThierry Reding 					     struct tegra_dc *dc)
132347307954SThierry Reding {
13248f62142eSThierry Reding 	struct drm_plane *planes[2], *primary;
13259f446d83SDmitry Osipenko 	unsigned int planes_num;
1326c7679306SThierry Reding 	unsigned int i;
13278f62142eSThierry Reding 	int err;
1328c7679306SThierry Reding 
132947307954SThierry Reding 	primary = tegra_primary_plane_create(drm, dc);
133047307954SThierry Reding 	if (IS_ERR(primary))
133147307954SThierry Reding 		return primary;
133247307954SThierry Reding 
13339f446d83SDmitry Osipenko 	if (dc->soc->supports_cursor)
13349f446d83SDmitry Osipenko 		planes_num = 2;
13359f446d83SDmitry Osipenko 	else
13369f446d83SDmitry Osipenko 		planes_num = 1;
13379f446d83SDmitry Osipenko 
13389f446d83SDmitry Osipenko 	for (i = 0; i < planes_num; i++) {
13399f446d83SDmitry Osipenko 		planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i,
13409f446d83SDmitry Osipenko 							  false);
13418f62142eSThierry Reding 		if (IS_ERR(planes[i])) {
13428f62142eSThierry Reding 			err = PTR_ERR(planes[i]);
13438f62142eSThierry Reding 
13448f62142eSThierry Reding 			while (i--)
134540dc962dSThierry Reding 				planes[i]->funcs->destroy(planes[i]);
13468f62142eSThierry Reding 
134740dc962dSThierry Reding 			primary->funcs->destroy(primary);
13488f62142eSThierry Reding 			return ERR_PTR(err);
134947307954SThierry Reding 		}
1350f002abc1SThierry Reding 	}
1351dee8268fSThierry Reding 
135247307954SThierry Reding 	return primary;
1353dee8268fSThierry Reding }
1354dee8268fSThierry Reding 
1355f002abc1SThierry Reding static void tegra_dc_destroy(struct drm_crtc *crtc)
1356f002abc1SThierry Reding {
1357f002abc1SThierry Reding 	drm_crtc_cleanup(crtc);
1358f002abc1SThierry Reding }
1359f002abc1SThierry Reding 
1360ca915b10SThierry Reding static void tegra_crtc_reset(struct drm_crtc *crtc)
1361ca915b10SThierry Reding {
1362b7e0b04aSMaarten Lankhorst 	struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
1363ca915b10SThierry Reding 
13643b59b7acSThierry Reding 	if (crtc->state)
1365b7e0b04aSMaarten Lankhorst 		tegra_crtc_atomic_destroy_state(crtc, crtc->state);
13663b59b7acSThierry Reding 
1367b7e0b04aSMaarten Lankhorst 	__drm_atomic_helper_crtc_reset(crtc, &state->base);
1368ca915b10SThierry Reding }
1369ca915b10SThierry Reding 
1370ca915b10SThierry Reding static struct drm_crtc_state *
1371ca915b10SThierry Reding tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
1372ca915b10SThierry Reding {
1373ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc->state);
1374ca915b10SThierry Reding 	struct tegra_dc_state *copy;
1375ca915b10SThierry Reding 
13763b59b7acSThierry Reding 	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
1377ca915b10SThierry Reding 	if (!copy)
1378ca915b10SThierry Reding 		return NULL;
1379ca915b10SThierry Reding 
13803b59b7acSThierry Reding 	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
13813b59b7acSThierry Reding 	copy->clk = state->clk;
13823b59b7acSThierry Reding 	copy->pclk = state->pclk;
13833b59b7acSThierry Reding 	copy->div = state->div;
13843b59b7acSThierry Reding 	copy->planes = state->planes;
1385ca915b10SThierry Reding 
1386ca915b10SThierry Reding 	return &copy->base;
1387ca915b10SThierry Reding }
1388ca915b10SThierry Reding 
1389ca915b10SThierry Reding static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
1390ca915b10SThierry Reding 					    struct drm_crtc_state *state)
1391ca915b10SThierry Reding {
1392ec2dc6a0SDaniel Vetter 	__drm_atomic_helper_crtc_destroy_state(state);
1393ca915b10SThierry Reding 	kfree(state);
1394ca915b10SThierry Reding }
1395ca915b10SThierry Reding 
1396b95800eeSThierry Reding #define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
1397b95800eeSThierry Reding 
1398b95800eeSThierry Reding static const struct debugfs_reg32 tegra_dc_regs[] = {
1399b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT),
1400b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL),
1401b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR),
1402b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT),
1403b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL),
1404b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR),
1405b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT),
1406b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL),
1407b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR),
1408b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT),
1409b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL),
1410b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR),
1411b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC),
1412b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0),
1413b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND),
1414b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE),
1415b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL),
1416b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_STATUS),
1417b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_MASK),
1418b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_ENABLE),
1419b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_TYPE),
1420b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_POLARITY),
1421b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1),
1422b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2),
1423b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3),
1424b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_ACCESS),
1425b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_CONTROL),
1426b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER),
1427b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL),
1428b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CONTROL),
1429b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM),
1430b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)),
1431b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)),
1432b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)),
1433b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)),
1434b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)),
1435b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)),
1436b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)),
1437b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)),
1438b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)),
1439b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)),
1440b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)),
1441b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)),
1442b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)),
1443b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)),
1444b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)),
1445b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)),
1446b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)),
1447b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)),
1448b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)),
1449b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)),
1450b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)),
1451b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)),
1452b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)),
1453b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
1454b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
1455b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
1456b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
1457b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
1458b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
1459b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
1460b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_CONTROL),
1461b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
1462b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
1463b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD),
1464b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_CS_DC),
1465b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A),
1466b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B),
1467b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_CTRL),
1468b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER),
1469b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED),
1470b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0),
1471b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1),
1472b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS),
1473b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY),
1474b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER),
1475b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS),
1476b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_REF_TO_SYNC),
1477b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SYNC_WIDTH),
1478b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BACK_PORCH),
1479b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_ACTIVE),
1480b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_FRONT_PORCH),
1481b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL),
1482b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A),
1483b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B),
1484b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C),
1485b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D),
1486b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL),
1487b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A),
1488b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B),
1489b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C),
1490b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D),
1491b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL),
1492b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A),
1493b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B),
1494b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C),
1495b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D),
1496b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL),
1497b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A),
1498b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B),
1499b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C),
1500b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL),
1501b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A),
1502b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B),
1503b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C),
1504b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL),
1505b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A),
1506b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL),
1507b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A),
1508b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M0_CONTROL),
1509b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M1_CONTROL),
1510b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DI_CONTROL),
1511b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_CONTROL),
1512b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_A),
1513b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_B),
1514b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_C),
1515b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_D),
1516b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL),
1517b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL),
1518b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL),
1519b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS),
1520b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS),
1521b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS),
1522b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS),
1523b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BORDER_COLOR),
1524b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER),
1525b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER),
1526b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER),
1527b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER),
1528b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND),
1529b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND),
1530b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR),
1531b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS),
1532b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION),
1533b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS),
1534b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL),
1535b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A),
1536b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B),
1537b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C),
1538b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D),
1539b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL),
1540b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST),
1541b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST),
1542b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST),
1543b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST),
1544b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL),
1545b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL),
1546b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CONTROL),
1547b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF),
1548b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(0)),
1549b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(1)),
1550b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(2)),
1551b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(3)),
1552b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(4)),
1553b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(5)),
1554b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(6)),
1555b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(7)),
1556b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(8)),
1557b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL),
1558b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT),
1559b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)),
1560b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)),
1561b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)),
1562b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)),
1563b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)),
1564b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)),
1565b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)),
1566b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)),
1567b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)),
1568b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)),
1569b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)),
1570b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)),
1571b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL),
1572b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES),
1573b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES),
1574b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI),
1575b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL),
1576b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_WIN_OPTIONS),
1577b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BYTE_SWAP),
1578b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL),
1579b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_COLOR_DEPTH),
1580b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_POSITION),
1581b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_SIZE),
1582b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE),
1583b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA),
1584b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA),
1585b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DDA_INC),
1586b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_LINE_STRIDE),
1587b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUF_STRIDE),
1588b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE),
1589b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE),
1590b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DV_CONTROL),
1591b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_NOKEY),
1592b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_1WIN),
1593b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X),
1594b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y),
1595b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY),
1596b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL),
1597b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR),
1598b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS),
1599b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U),
1600b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS),
1601b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V),
1602b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS),
1603b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET),
1604b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS),
1605b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET),
1606b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS),
1607b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS),
1608b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS),
1609b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS),
1610b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS),
1611b95800eeSThierry Reding };
1612b95800eeSThierry Reding 
1613b95800eeSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data)
1614b95800eeSThierry Reding {
1615b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1616b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1617b95800eeSThierry Reding 	unsigned int i;
1618b95800eeSThierry Reding 	int err = 0;
1619b95800eeSThierry Reding 
1620b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1621b95800eeSThierry Reding 
1622b95800eeSThierry Reding 	if (!dc->base.state->active) {
1623b95800eeSThierry Reding 		err = -EBUSY;
1624b95800eeSThierry Reding 		goto unlock;
1625b95800eeSThierry Reding 	}
1626b95800eeSThierry Reding 
1627b95800eeSThierry Reding 	for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) {
1628b95800eeSThierry Reding 		unsigned int offset = tegra_dc_regs[i].offset;
1629b95800eeSThierry Reding 
1630b95800eeSThierry Reding 		seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name,
1631b95800eeSThierry Reding 			   offset, tegra_dc_readl(dc, offset));
1632b95800eeSThierry Reding 	}
1633b95800eeSThierry Reding 
1634b95800eeSThierry Reding unlock:
1635b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1636b95800eeSThierry Reding 	return err;
1637b95800eeSThierry Reding }
1638b95800eeSThierry Reding 
1639b95800eeSThierry Reding static int tegra_dc_show_crc(struct seq_file *s, void *data)
1640b95800eeSThierry Reding {
1641b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1642b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1643b95800eeSThierry Reding 	int err = 0;
1644b95800eeSThierry Reding 	u32 value;
1645b95800eeSThierry Reding 
1646b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1647b95800eeSThierry Reding 
1648b95800eeSThierry Reding 	if (!dc->base.state->active) {
1649b95800eeSThierry Reding 		err = -EBUSY;
1650b95800eeSThierry Reding 		goto unlock;
1651b95800eeSThierry Reding 	}
1652b95800eeSThierry Reding 
1653b95800eeSThierry Reding 	value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
1654b95800eeSThierry Reding 	tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
1655b95800eeSThierry Reding 	tegra_dc_commit(dc);
1656b95800eeSThierry Reding 
1657b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1658b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1659b95800eeSThierry Reding 
1660b95800eeSThierry Reding 	value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM);
1661b95800eeSThierry Reding 	seq_printf(s, "%08x\n", value);
1662b95800eeSThierry Reding 
1663b95800eeSThierry Reding 	tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
1664b95800eeSThierry Reding 
1665b95800eeSThierry Reding unlock:
1666b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1667b95800eeSThierry Reding 	return err;
1668b95800eeSThierry Reding }
1669b95800eeSThierry Reding 
1670b95800eeSThierry Reding static int tegra_dc_show_stats(struct seq_file *s, void *data)
1671b95800eeSThierry Reding {
1672b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1673b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1674b95800eeSThierry Reding 
1675b95800eeSThierry Reding 	seq_printf(s, "frames: %lu\n", dc->stats.frames);
1676b95800eeSThierry Reding 	seq_printf(s, "vblank: %lu\n", dc->stats.vblank);
1677b95800eeSThierry Reding 	seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
1678b95800eeSThierry Reding 	seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
1679b95800eeSThierry Reding 
1680ad85b084SDmitry Osipenko 	seq_printf(s, "frames total: %lu\n", dc->stats.frames_total);
1681ad85b084SDmitry Osipenko 	seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total);
1682ad85b084SDmitry Osipenko 	seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total);
1683ad85b084SDmitry Osipenko 	seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total);
1684ad85b084SDmitry Osipenko 
1685b95800eeSThierry Reding 	return 0;
1686b95800eeSThierry Reding }
1687b95800eeSThierry Reding 
1688b95800eeSThierry Reding static struct drm_info_list debugfs_files[] = {
1689b95800eeSThierry Reding 	{ "regs", tegra_dc_show_regs, 0, NULL },
1690b95800eeSThierry Reding 	{ "crc", tegra_dc_show_crc, 0, NULL },
1691b95800eeSThierry Reding 	{ "stats", tegra_dc_show_stats, 0, NULL },
1692b95800eeSThierry Reding };
1693b95800eeSThierry Reding 
1694b95800eeSThierry Reding static int tegra_dc_late_register(struct drm_crtc *crtc)
1695b95800eeSThierry Reding {
1696b95800eeSThierry Reding 	unsigned int i, count = ARRAY_SIZE(debugfs_files);
1697b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
169839f55c61SArnd Bergmann 	struct dentry *root;
1699b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1700b95800eeSThierry Reding 
170139f55c61SArnd Bergmann #ifdef CONFIG_DEBUG_FS
170239f55c61SArnd Bergmann 	root = crtc->debugfs_entry;
170339f55c61SArnd Bergmann #else
170439f55c61SArnd Bergmann 	root = NULL;
170539f55c61SArnd Bergmann #endif
170639f55c61SArnd Bergmann 
1707b95800eeSThierry Reding 	dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
1708b95800eeSThierry Reding 				    GFP_KERNEL);
1709b95800eeSThierry Reding 	if (!dc->debugfs_files)
1710b95800eeSThierry Reding 		return -ENOMEM;
1711b95800eeSThierry Reding 
1712b95800eeSThierry Reding 	for (i = 0; i < count; i++)
1713b95800eeSThierry Reding 		dc->debugfs_files[i].data = dc;
1714b95800eeSThierry Reding 
1715ad6d94f2SWambui Karuga 	drm_debugfs_create_files(dc->debugfs_files, count, root, minor);
1716b95800eeSThierry Reding 
1717b95800eeSThierry Reding 	return 0;
1718b95800eeSThierry Reding }
1719b95800eeSThierry Reding 
1720b95800eeSThierry Reding static void tegra_dc_early_unregister(struct drm_crtc *crtc)
1721b95800eeSThierry Reding {
1722b95800eeSThierry Reding 	unsigned int count = ARRAY_SIZE(debugfs_files);
1723b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
1724b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1725b95800eeSThierry Reding 
1726b95800eeSThierry Reding 	drm_debugfs_remove_files(dc->debugfs_files, count, minor);
1727b95800eeSThierry Reding 	kfree(dc->debugfs_files);
1728b95800eeSThierry Reding 	dc->debugfs_files = NULL;
1729b95800eeSThierry Reding }
1730b95800eeSThierry Reding 
1731c49c81e2SThierry Reding static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
1732c49c81e2SThierry Reding {
1733c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1734c49c81e2SThierry Reding 
173547307954SThierry Reding 	/* XXX vblank syncpoints don't work with nvdisplay yet */
173647307954SThierry Reding 	if (dc->syncpt && !dc->soc->has_nvdisplay)
1737c49c81e2SThierry Reding 		return host1x_syncpt_read(dc->syncpt);
1738c49c81e2SThierry Reding 
1739c49c81e2SThierry Reding 	/* fallback to software emulated VBLANK counter */
17403abe2413SDhinakaran Pandiyan 	return (u32)drm_crtc_vblank_count(&dc->base);
1741c49c81e2SThierry Reding }
1742c49c81e2SThierry Reding 
1743c49c81e2SThierry Reding static int tegra_dc_enable_vblank(struct drm_crtc *crtc)
1744c49c81e2SThierry Reding {
1745c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1746363541e8SThierry Reding 	u32 value;
1747c49c81e2SThierry Reding 
1748c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1749c49c81e2SThierry Reding 	value |= VBLANK_INT;
1750c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1751c49c81e2SThierry Reding 
1752c49c81e2SThierry Reding 	return 0;
1753c49c81e2SThierry Reding }
1754c49c81e2SThierry Reding 
1755c49c81e2SThierry Reding static void tegra_dc_disable_vblank(struct drm_crtc *crtc)
1756c49c81e2SThierry Reding {
1757c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1758363541e8SThierry Reding 	u32 value;
1759c49c81e2SThierry Reding 
1760c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1761c49c81e2SThierry Reding 	value &= ~VBLANK_INT;
1762c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1763c49c81e2SThierry Reding }
1764c49c81e2SThierry Reding 
1765dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = {
17661503ca47SThierry Reding 	.page_flip = drm_atomic_helper_page_flip,
176774f48791SThierry Reding 	.set_config = drm_atomic_helper_set_config,
1768f002abc1SThierry Reding 	.destroy = tegra_dc_destroy,
1769ca915b10SThierry Reding 	.reset = tegra_crtc_reset,
1770ca915b10SThierry Reding 	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
1771ca915b10SThierry Reding 	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
1772b95800eeSThierry Reding 	.late_register = tegra_dc_late_register,
1773b95800eeSThierry Reding 	.early_unregister = tegra_dc_early_unregister,
177410437d9bSShawn Guo 	.get_vblank_counter = tegra_dc_get_vblank_counter,
177510437d9bSShawn Guo 	.enable_vblank = tegra_dc_enable_vblank,
177610437d9bSShawn Guo 	.disable_vblank = tegra_dc_disable_vblank,
1777dee8268fSThierry Reding };
1778dee8268fSThierry Reding 
1779dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc,
1780dee8268fSThierry Reding 				struct drm_display_mode *mode)
1781dee8268fSThierry Reding {
17820444c0ffSThierry Reding 	unsigned int h_ref_to_sync = 1;
17830444c0ffSThierry Reding 	unsigned int v_ref_to_sync = 1;
1784dee8268fSThierry Reding 	unsigned long value;
1785dee8268fSThierry Reding 
178647307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
1787dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
1788dee8268fSThierry Reding 
1789dee8268fSThierry Reding 		value = (v_ref_to_sync << 16) | h_ref_to_sync;
1790dee8268fSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
179147307954SThierry Reding 	}
1792dee8268fSThierry Reding 
1793dee8268fSThierry Reding 	value = ((mode->vsync_end - mode->vsync_start) << 16) |
1794dee8268fSThierry Reding 		((mode->hsync_end - mode->hsync_start) <<  0);
1795dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
1796dee8268fSThierry Reding 
1797dee8268fSThierry Reding 	value = ((mode->vtotal - mode->vsync_end) << 16) |
1798dee8268fSThierry Reding 		((mode->htotal - mode->hsync_end) <<  0);
1799dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
1800dee8268fSThierry Reding 
1801dee8268fSThierry Reding 	value = ((mode->vsync_start - mode->vdisplay) << 16) |
1802dee8268fSThierry Reding 		((mode->hsync_start - mode->hdisplay) <<  0);
1803dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
1804dee8268fSThierry Reding 
1805dee8268fSThierry Reding 	value = (mode->vdisplay << 16) | mode->hdisplay;
1806dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
1807dee8268fSThierry Reding 
1808dee8268fSThierry Reding 	return 0;
1809dee8268fSThierry Reding }
1810dee8268fSThierry Reding 
18119d910b60SThierry Reding /**
18129d910b60SThierry Reding  * tegra_dc_state_setup_clock - check clock settings and store them in atomic
18139d910b60SThierry Reding  *     state
18149d910b60SThierry Reding  * @dc: display controller
18159d910b60SThierry Reding  * @crtc_state: CRTC atomic state
18169d910b60SThierry Reding  * @clk: parent clock for display controller
18179d910b60SThierry Reding  * @pclk: pixel clock
18189d910b60SThierry Reding  * @div: shift clock divider
18199d910b60SThierry Reding  *
18209d910b60SThierry Reding  * Returns:
18219d910b60SThierry Reding  * 0 on success or a negative error-code on failure.
18229d910b60SThierry Reding  */
1823ca915b10SThierry Reding int tegra_dc_state_setup_clock(struct tegra_dc *dc,
1824ca915b10SThierry Reding 			       struct drm_crtc_state *crtc_state,
1825ca915b10SThierry Reding 			       struct clk *clk, unsigned long pclk,
1826ca915b10SThierry Reding 			       unsigned int div)
1827ca915b10SThierry Reding {
1828ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc_state);
1829ca915b10SThierry Reding 
1830d2982748SThierry Reding 	if (!clk_has_parent(dc->clk, clk))
1831d2982748SThierry Reding 		return -EINVAL;
1832d2982748SThierry Reding 
1833ca915b10SThierry Reding 	state->clk = clk;
1834ca915b10SThierry Reding 	state->pclk = pclk;
1835ca915b10SThierry Reding 	state->div = div;
1836ca915b10SThierry Reding 
1837ca915b10SThierry Reding 	return 0;
1838ca915b10SThierry Reding }
1839ca915b10SThierry Reding 
1840*4ce3048cSDmitry Osipenko static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
1841*4ce3048cSDmitry Osipenko 					  struct tegra_dc_state *state)
1842*4ce3048cSDmitry Osipenko {
1843*4ce3048cSDmitry Osipenko 	unsigned long rate, pstate;
1844*4ce3048cSDmitry Osipenko 	struct dev_pm_opp *opp;
1845*4ce3048cSDmitry Osipenko 	int err;
1846*4ce3048cSDmitry Osipenko 
1847*4ce3048cSDmitry Osipenko 	if (!dc->has_opp_table)
1848*4ce3048cSDmitry Osipenko 		return;
1849*4ce3048cSDmitry Osipenko 
1850*4ce3048cSDmitry Osipenko 	/* calculate actual pixel clock rate which depends on internal divider */
1851*4ce3048cSDmitry Osipenko 	rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
1852*4ce3048cSDmitry Osipenko 
1853*4ce3048cSDmitry Osipenko 	/* find suitable OPP for the rate */
1854*4ce3048cSDmitry Osipenko 	opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
1855*4ce3048cSDmitry Osipenko 
1856*4ce3048cSDmitry Osipenko 	/*
1857*4ce3048cSDmitry Osipenko 	 * Very high resolution modes may results in a clock rate that is
1858*4ce3048cSDmitry Osipenko 	 * above the characterized maximum. In this case it's okay to fall
1859*4ce3048cSDmitry Osipenko 	 * back to the characterized maximum.
1860*4ce3048cSDmitry Osipenko 	 */
1861*4ce3048cSDmitry Osipenko 	if (opp == ERR_PTR(-ERANGE))
1862*4ce3048cSDmitry Osipenko 		opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
1863*4ce3048cSDmitry Osipenko 
1864*4ce3048cSDmitry Osipenko 	if (IS_ERR(opp)) {
1865*4ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
1866*4ce3048cSDmitry Osipenko 			rate, opp);
1867*4ce3048cSDmitry Osipenko 		return;
1868*4ce3048cSDmitry Osipenko 	}
1869*4ce3048cSDmitry Osipenko 
1870*4ce3048cSDmitry Osipenko 	pstate = dev_pm_opp_get_required_pstate(opp, 0);
1871*4ce3048cSDmitry Osipenko 	dev_pm_opp_put(opp);
1872*4ce3048cSDmitry Osipenko 
1873*4ce3048cSDmitry Osipenko 	/*
1874*4ce3048cSDmitry Osipenko 	 * The minimum core voltage depends on the pixel clock rate (which
1875*4ce3048cSDmitry Osipenko 	 * depends on internal clock divider of the CRTC) and not on the
1876*4ce3048cSDmitry Osipenko 	 * rate of the display controller clock. This is why we're not using
1877*4ce3048cSDmitry Osipenko 	 * dev_pm_opp_set_rate() API and instead controlling the power domain
1878*4ce3048cSDmitry Osipenko 	 * directly.
1879*4ce3048cSDmitry Osipenko 	 */
1880*4ce3048cSDmitry Osipenko 	err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
1881*4ce3048cSDmitry Osipenko 	if (err)
1882*4ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
1883*4ce3048cSDmitry Osipenko 			pstate, err);
1884*4ce3048cSDmitry Osipenko }
1885*4ce3048cSDmitry Osipenko 
18860c921b6dSDmitry Osipenko static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
188776d59ed0SThierry Reding 				    struct tegra_dc_state *state)
188876d59ed0SThierry Reding {
188976d59ed0SThierry Reding 	int err;
189076d59ed0SThierry Reding 
189176d59ed0SThierry Reding 	err = clk_set_parent(dc->clk, state->clk);
189276d59ed0SThierry Reding 	if (err < 0)
189376d59ed0SThierry Reding 		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
189476d59ed0SThierry Reding 
189576d59ed0SThierry Reding 	/*
189676d59ed0SThierry Reding 	 * Outputs may not want to change the parent clock rate. This is only
189776d59ed0SThierry Reding 	 * relevant to Tegra20 where only a single display PLL is available.
189876d59ed0SThierry Reding 	 * Since that PLL would typically be used for HDMI, an internal LVDS
189976d59ed0SThierry Reding 	 * panel would need to be driven by some other clock such as PLL_P
190076d59ed0SThierry Reding 	 * which is shared with other peripherals. Changing the clock rate
190176d59ed0SThierry Reding 	 * should therefore be avoided.
190276d59ed0SThierry Reding 	 */
190376d59ed0SThierry Reding 	if (state->pclk > 0) {
190476d59ed0SThierry Reding 		err = clk_set_rate(state->clk, state->pclk);
190576d59ed0SThierry Reding 		if (err < 0)
190676d59ed0SThierry Reding 			dev_err(dc->dev,
190776d59ed0SThierry Reding 				"failed to set clock rate to %lu Hz\n",
190876d59ed0SThierry Reding 				state->pclk);
1909f8fb97c9SDmitry Osipenko 
1910f8fb97c9SDmitry Osipenko 		err = clk_set_rate(dc->clk, state->pclk);
1911f8fb97c9SDmitry Osipenko 		if (err < 0)
1912f8fb97c9SDmitry Osipenko 			dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n",
1913f8fb97c9SDmitry Osipenko 				dc->clk, state->pclk, err);
191476d59ed0SThierry Reding 	}
191576d59ed0SThierry Reding 
191676d59ed0SThierry Reding 	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
191776d59ed0SThierry Reding 		      state->div);
191876d59ed0SThierry Reding 	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
1919*4ce3048cSDmitry Osipenko 
1920*4ce3048cSDmitry Osipenko 	tegra_dc_update_voltage_state(dc, state);
192176d59ed0SThierry Reding }
192276d59ed0SThierry Reding 
1923003fc848SThierry Reding static void tegra_dc_stop(struct tegra_dc *dc)
1924003fc848SThierry Reding {
1925003fc848SThierry Reding 	u32 value;
1926003fc848SThierry Reding 
1927003fc848SThierry Reding 	/* stop the display controller */
1928003fc848SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
1929003fc848SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
1930003fc848SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
1931003fc848SThierry Reding 
1932003fc848SThierry Reding 	tegra_dc_commit(dc);
1933003fc848SThierry Reding }
1934003fc848SThierry Reding 
1935003fc848SThierry Reding static bool tegra_dc_idle(struct tegra_dc *dc)
1936003fc848SThierry Reding {
1937003fc848SThierry Reding 	u32 value;
1938003fc848SThierry Reding 
1939003fc848SThierry Reding 	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
1940003fc848SThierry Reding 
1941003fc848SThierry Reding 	return (value & DISP_CTRL_MODE_MASK) == 0;
1942003fc848SThierry Reding }
1943003fc848SThierry Reding 
1944003fc848SThierry Reding static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
1945003fc848SThierry Reding {
1946003fc848SThierry Reding 	timeout = jiffies + msecs_to_jiffies(timeout);
1947003fc848SThierry Reding 
1948003fc848SThierry Reding 	while (time_before(jiffies, timeout)) {
1949003fc848SThierry Reding 		if (tegra_dc_idle(dc))
1950003fc848SThierry Reding 			return 0;
1951003fc848SThierry Reding 
1952003fc848SThierry Reding 		usleep_range(1000, 2000);
1953003fc848SThierry Reding 	}
1954003fc848SThierry Reding 
1955003fc848SThierry Reding 	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
1956003fc848SThierry Reding 	return -ETIMEDOUT;
1957003fc848SThierry Reding }
1958003fc848SThierry Reding 
195904d5d5dfSDmitry Osipenko static void
196004d5d5dfSDmitry Osipenko tegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc,
196104d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state,
196204d5d5dfSDmitry Osipenko 				   bool prepare_bandwidth_transition)
196304d5d5dfSDmitry Osipenko {
196404d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *old_tegra_state, *new_tegra_state;
196504d5d5dfSDmitry Osipenko 	u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw;
196604d5d5dfSDmitry Osipenko 	const struct drm_plane_state *old_plane_state;
196704d5d5dfSDmitry Osipenko 	const struct drm_crtc_state *old_crtc_state;
196804d5d5dfSDmitry Osipenko 	struct tegra_dc_window window, old_window;
196904d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
197004d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
197104d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
197204d5d5dfSDmitry Osipenko 
197304d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
197404d5d5dfSDmitry Osipenko 		return;
197504d5d5dfSDmitry Osipenko 
197604d5d5dfSDmitry Osipenko 	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
197704d5d5dfSDmitry Osipenko 
197804d5d5dfSDmitry Osipenko 	if (!crtc->state->active) {
197904d5d5dfSDmitry Osipenko 		if (!old_crtc_state->active)
198004d5d5dfSDmitry Osipenko 			return;
198104d5d5dfSDmitry Osipenko 
198204d5d5dfSDmitry Osipenko 		/*
198304d5d5dfSDmitry Osipenko 		 * When CRTC is disabled on DPMS, the state of attached planes
198404d5d5dfSDmitry Osipenko 		 * is kept unchanged. Hence we need to enforce removal of the
198504d5d5dfSDmitry Osipenko 		 * bandwidths from the ICC paths.
198604d5d5dfSDmitry Osipenko 		 */
198704d5d5dfSDmitry Osipenko 		drm_atomic_crtc_for_each_plane(plane, crtc) {
198804d5d5dfSDmitry Osipenko 			tegra = to_tegra_plane(plane);
198904d5d5dfSDmitry Osipenko 
199004d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem, 0, 0);
199104d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
199204d5d5dfSDmitry Osipenko 		}
199304d5d5dfSDmitry Osipenko 
199404d5d5dfSDmitry Osipenko 		return;
199504d5d5dfSDmitry Osipenko 	}
199604d5d5dfSDmitry Osipenko 
199704d5d5dfSDmitry Osipenko 	for_each_old_plane_in_state(old_crtc_state->state, plane,
199804d5d5dfSDmitry Osipenko 				    old_plane_state, i) {
199904d5d5dfSDmitry Osipenko 		old_tegra_state = to_const_tegra_plane_state(old_plane_state);
200004d5d5dfSDmitry Osipenko 		new_tegra_state = to_const_tegra_plane_state(plane->state);
200104d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
200204d5d5dfSDmitry Osipenko 
200304d5d5dfSDmitry Osipenko 		/*
200404d5d5dfSDmitry Osipenko 		 * We're iterating over the global atomic state and it contains
200504d5d5dfSDmitry Osipenko 		 * planes from another CRTC, hence we need to filter out the
200604d5d5dfSDmitry Osipenko 		 * planes unrelated to this CRTC.
200704d5d5dfSDmitry Osipenko 		 */
200804d5d5dfSDmitry Osipenko 		if (tegra->dc != dc)
200904d5d5dfSDmitry Osipenko 			continue;
201004d5d5dfSDmitry Osipenko 
201104d5d5dfSDmitry Osipenko 		new_avg_bw = new_tegra_state->avg_memory_bandwidth;
201204d5d5dfSDmitry Osipenko 		old_avg_bw = old_tegra_state->avg_memory_bandwidth;
201304d5d5dfSDmitry Osipenko 
201404d5d5dfSDmitry Osipenko 		new_peak_bw = new_tegra_state->total_peak_memory_bandwidth;
201504d5d5dfSDmitry Osipenko 		old_peak_bw = old_tegra_state->total_peak_memory_bandwidth;
201604d5d5dfSDmitry Osipenko 
201704d5d5dfSDmitry Osipenko 		/*
201804d5d5dfSDmitry Osipenko 		 * See the comment related to !crtc->state->active above,
201904d5d5dfSDmitry Osipenko 		 * which explains why bandwidths need to be updated when
202004d5d5dfSDmitry Osipenko 		 * CRTC is turning ON.
202104d5d5dfSDmitry Osipenko 		 */
202204d5d5dfSDmitry Osipenko 		if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw &&
202304d5d5dfSDmitry Osipenko 		    old_crtc_state->active)
202404d5d5dfSDmitry Osipenko 			continue;
202504d5d5dfSDmitry Osipenko 
202604d5d5dfSDmitry Osipenko 		window.src.h = drm_rect_height(&plane->state->src) >> 16;
202704d5d5dfSDmitry Osipenko 		window.dst.h = drm_rect_height(&plane->state->dst);
202804d5d5dfSDmitry Osipenko 
202904d5d5dfSDmitry Osipenko 		old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16;
203004d5d5dfSDmitry Osipenko 		old_window.dst.h = drm_rect_height(&old_plane_state->dst);
203104d5d5dfSDmitry Osipenko 
203204d5d5dfSDmitry Osipenko 		/*
203304d5d5dfSDmitry Osipenko 		 * During the preparation phase (atomic_begin), the memory
203404d5d5dfSDmitry Osipenko 		 * freq should go high before the DC changes are committed
203504d5d5dfSDmitry Osipenko 		 * if bandwidth requirement goes up, otherwise memory freq
203604d5d5dfSDmitry Osipenko 		 * should to stay high if BW requirement goes down.  The
203704d5d5dfSDmitry Osipenko 		 * opposite applies to the completion phase (post_commit).
203804d5d5dfSDmitry Osipenko 		 */
203904d5d5dfSDmitry Osipenko 		if (prepare_bandwidth_transition) {
204004d5d5dfSDmitry Osipenko 			new_avg_bw = max(old_avg_bw, new_avg_bw);
204104d5d5dfSDmitry Osipenko 			new_peak_bw = max(old_peak_bw, new_peak_bw);
204204d5d5dfSDmitry Osipenko 
204304d5d5dfSDmitry Osipenko 			if (tegra_plane_use_vertical_filtering(tegra, &old_window))
204404d5d5dfSDmitry Osipenko 				window = old_window;
204504d5d5dfSDmitry Osipenko 		}
204604d5d5dfSDmitry Osipenko 
204704d5d5dfSDmitry Osipenko 		icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw);
204804d5d5dfSDmitry Osipenko 
204904d5d5dfSDmitry Osipenko 		if (tegra_plane_use_vertical_filtering(tegra, &window))
205004d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw);
205104d5d5dfSDmitry Osipenko 		else
205204d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
205304d5d5dfSDmitry Osipenko 	}
205404d5d5dfSDmitry Osipenko }
205504d5d5dfSDmitry Osipenko 
205664581714SLaurent Pinchart static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
2057351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
2058003fc848SThierry Reding {
2059003fc848SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2060003fc848SThierry Reding 	u32 value;
2061fd67e9c6SThierry Reding 	int err;
2062003fc848SThierry Reding 
2063003fc848SThierry Reding 	if (!tegra_dc_idle(dc)) {
2064003fc848SThierry Reding 		tegra_dc_stop(dc);
2065003fc848SThierry Reding 
2066003fc848SThierry Reding 		/*
2067003fc848SThierry Reding 		 * Ignore the return value, there isn't anything useful to do
2068003fc848SThierry Reding 		 * in case this fails.
2069003fc848SThierry Reding 		 */
2070003fc848SThierry Reding 		tegra_dc_wait_idle(dc, 100);
2071003fc848SThierry Reding 	}
2072003fc848SThierry Reding 
2073003fc848SThierry Reding 	/*
2074003fc848SThierry Reding 	 * This should really be part of the RGB encoder driver, but clearing
2075003fc848SThierry Reding 	 * these bits has the side-effect of stopping the display controller.
2076003fc848SThierry Reding 	 * When that happens no VBLANK interrupts will be raised. At the same
2077003fc848SThierry Reding 	 * time the encoder is disabled before the display controller, so the
2078003fc848SThierry Reding 	 * above code is always going to timeout waiting for the controller
2079003fc848SThierry Reding 	 * to go idle.
2080003fc848SThierry Reding 	 *
2081003fc848SThierry Reding 	 * Given the close coupling between the RGB encoder and the display
2082003fc848SThierry Reding 	 * controller doing it here is still kind of okay. None of the other
2083003fc848SThierry Reding 	 * encoder drivers require these bits to be cleared.
2084003fc848SThierry Reding 	 *
2085003fc848SThierry Reding 	 * XXX: Perhaps given that the display controller is switched off at
2086003fc848SThierry Reding 	 * this point anyway maybe clearing these bits isn't even useful for
2087003fc848SThierry Reding 	 * the RGB encoder?
2088003fc848SThierry Reding 	 */
2089003fc848SThierry Reding 	if (dc->rgb) {
2090003fc848SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2091003fc848SThierry Reding 		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2092003fc848SThierry Reding 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
2093003fc848SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
2094003fc848SThierry Reding 	}
2095003fc848SThierry Reding 
2096003fc848SThierry Reding 	tegra_dc_stats_reset(&dc->stats);
2097003fc848SThierry Reding 	drm_crtc_vblank_off(crtc);
209833a8eb8dSThierry Reding 
20999d99ab6eSThierry Reding 	spin_lock_irq(&crtc->dev->event_lock);
21009d99ab6eSThierry Reding 
21019d99ab6eSThierry Reding 	if (crtc->state->event) {
21029d99ab6eSThierry Reding 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
21039d99ab6eSThierry Reding 		crtc->state->event = NULL;
21049d99ab6eSThierry Reding 	}
21059d99ab6eSThierry Reding 
21069d99ab6eSThierry Reding 	spin_unlock_irq(&crtc->dev->event_lock);
21079d99ab6eSThierry Reding 
2108fd67e9c6SThierry Reding 	err = host1x_client_suspend(&dc->client);
2109fd67e9c6SThierry Reding 	if (err < 0)
2110fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to suspend: %d\n", err);
2111*4ce3048cSDmitry Osipenko 
2112*4ce3048cSDmitry Osipenko 	if (dc->has_opp_table) {
2113*4ce3048cSDmitry Osipenko 		err = dev_pm_genpd_set_performance_state(dc->dev, 0);
2114*4ce3048cSDmitry Osipenko 		if (err)
2115*4ce3048cSDmitry Osipenko 			dev_err(dc->dev,
2116*4ce3048cSDmitry Osipenko 				"failed to clear power domain state: %d\n", err);
2117*4ce3048cSDmitry Osipenko 	}
2118003fc848SThierry Reding }
2119003fc848SThierry Reding 
21200b20a0f8SLaurent Pinchart static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
2121351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
2122dee8268fSThierry Reding {
21234aa3df71SThierry Reding 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
2124351f950dSMaxime Ripard 	struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
2125dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2126dbb3f2f7SThierry Reding 	u32 value;
2127fd67e9c6SThierry Reding 	int err;
2128dee8268fSThierry Reding 
21290c921b6dSDmitry Osipenko 	/* apply PLL changes */
21300c921b6dSDmitry Osipenko 	tegra_dc_set_clock_rate(dc, crtc_state);
21310c921b6dSDmitry Osipenko 
2132fd67e9c6SThierry Reding 	err = host1x_client_resume(&dc->client);
2133fd67e9c6SThierry Reding 	if (err < 0) {
2134fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to resume: %d\n", err);
2135fd67e9c6SThierry Reding 		return;
2136fd67e9c6SThierry Reding 	}
213733a8eb8dSThierry Reding 
213833a8eb8dSThierry Reding 	/* initialize display controller */
213933a8eb8dSThierry Reding 	if (dc->syncpt) {
214047307954SThierry Reding 		u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
214147307954SThierry Reding 
214247307954SThierry Reding 		if (dc->soc->has_nvdisplay)
214347307954SThierry Reding 			enable = 1 << 31;
214447307954SThierry Reding 		else
214547307954SThierry Reding 			enable = 1 << 8;
214633a8eb8dSThierry Reding 
214733a8eb8dSThierry Reding 		value = SYNCPT_CNTRL_NO_STALL;
214833a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
214933a8eb8dSThierry Reding 
215047307954SThierry Reding 		value = enable | syncpt;
215133a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
215233a8eb8dSThierry Reding 	}
215333a8eb8dSThierry Reding 
215447307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
215547307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
215647307954SThierry Reding 			DSC_OBUF_UF_INT;
215747307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
215847307954SThierry Reding 
215947307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
216047307954SThierry Reding 			DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
216147307954SThierry Reding 			HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
216247307954SThierry Reding 			REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
216347307954SThierry Reding 			VBLANK_INT | FRAME_END_INT;
216447307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
216547307954SThierry Reding 
216647307954SThierry Reding 		value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
216747307954SThierry Reding 			FRAME_END_INT;
216847307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
216947307954SThierry Reding 
217047307954SThierry Reding 		value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
217147307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
217247307954SThierry Reding 
217347307954SThierry Reding 		tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
217447307954SThierry Reding 	} else {
217533a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
217633a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
217733a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
217833a8eb8dSThierry Reding 
217933a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
218033a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
218133a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
218233a8eb8dSThierry Reding 
218333a8eb8dSThierry Reding 		/* initialize timer */
218433a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
218533a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
218633a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
218733a8eb8dSThierry Reding 
218833a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
218933a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
219033a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
219133a8eb8dSThierry Reding 
219233a8eb8dSThierry Reding 		value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
219333a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
219433a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
219533a8eb8dSThierry Reding 
219633a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
219733a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
219833a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
219947307954SThierry Reding 	}
220033a8eb8dSThierry Reding 
22017116e9a8SThierry Reding 	if (dc->soc->supports_background_color)
22027116e9a8SThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
22037116e9a8SThierry Reding 	else
220433a8eb8dSThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
220533a8eb8dSThierry Reding 
22060c921b6dSDmitry Osipenko 	/* apply pixel clock changes */
22070c921b6dSDmitry Osipenko 	if (!dc->soc->has_nvdisplay) {
22080c921b6dSDmitry Osipenko 		value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1;
22090c921b6dSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
22100c921b6dSDmitry Osipenko 	}
221176d59ed0SThierry Reding 
2212dee8268fSThierry Reding 	/* program display mode */
2213dee8268fSThierry Reding 	tegra_dc_set_timings(dc, mode);
2214dee8268fSThierry Reding 
22158620fc62SThierry Reding 	/* interlacing isn't supported yet, so disable it */
22168620fc62SThierry Reding 	if (dc->soc->supports_interlacing) {
22178620fc62SThierry Reding 		value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
22188620fc62SThierry Reding 		value &= ~INTERLACE_ENABLE;
22198620fc62SThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
22208620fc62SThierry Reding 	}
2221666cb873SThierry Reding 
2222666cb873SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
2223666cb873SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
2224666cb873SThierry Reding 	value |= DISP_CTRL_MODE_C_DISPLAY;
2225666cb873SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
2226666cb873SThierry Reding 
222747307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
2228666cb873SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2229666cb873SThierry Reding 		value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2230666cb873SThierry Reding 			 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
2231666cb873SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
223247307954SThierry Reding 	}
223347307954SThierry Reding 
223447307954SThierry Reding 	/* enable underflow reporting and display red for missing pixels */
223547307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
223647307954SThierry Reding 		value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
223747307954SThierry Reding 		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
223847307954SThierry Reding 	}
2239666cb873SThierry Reding 
2240f7d6c6aeSDmitry Osipenko 	if (dc->rgb) {
2241f7d6c6aeSDmitry Osipenko 		/* XXX: parameterize? */
2242f7d6c6aeSDmitry Osipenko 		value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
2243f7d6c6aeSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
2244f7d6c6aeSDmitry Osipenko 	}
2245f7d6c6aeSDmitry Osipenko 
2246666cb873SThierry Reding 	tegra_dc_commit(dc);
2247dee8268fSThierry Reding 
22488ff64c17SThierry Reding 	drm_crtc_vblank_on(crtc);
2249dee8268fSThierry Reding }
2250dee8268fSThierry Reding 
2251613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
2252f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22534aa3df71SThierry Reding {
22549d99ab6eSThierry Reding 	unsigned long flags;
22551503ca47SThierry Reding 
225604d5d5dfSDmitry Osipenko 	tegra_crtc_update_memory_bandwidth(crtc, state, true);
225704d5d5dfSDmitry Osipenko 
22581503ca47SThierry Reding 	if (crtc->state->event) {
22599d99ab6eSThierry Reding 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
22601503ca47SThierry Reding 
22619d99ab6eSThierry Reding 		if (drm_crtc_vblank_get(crtc) != 0)
22629d99ab6eSThierry Reding 			drm_crtc_send_vblank_event(crtc, crtc->state->event);
22639d99ab6eSThierry Reding 		else
22649d99ab6eSThierry Reding 			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
22651503ca47SThierry Reding 
22669d99ab6eSThierry Reding 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
22679d99ab6eSThierry Reding 
22681503ca47SThierry Reding 		crtc->state->event = NULL;
22691503ca47SThierry Reding 	}
22704aa3df71SThierry Reding }
22714aa3df71SThierry Reding 
2272613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
2273f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22744aa3df71SThierry Reding {
2275253f28b6SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
2276253f28b6SMaxime Ripard 									  crtc);
2277253f28b6SMaxime Ripard 	struct tegra_dc_state *dc_state = to_dc_state(crtc_state);
227847802b09SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
227947307954SThierry Reding 	u32 value;
228047802b09SThierry Reding 
2281253f28b6SMaxime Ripard 	value = dc_state->planes << 8 | GENERAL_UPDATE;
228247307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
228347307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
228447307954SThierry Reding 
2285253f28b6SMaxime Ripard 	value = dc_state->planes | GENERAL_ACT_REQ;
228647307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
228747307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
22884aa3df71SThierry Reding }
22894aa3df71SThierry Reding 
229004d5d5dfSDmitry Osipenko static bool tegra_plane_is_cursor(const struct drm_plane_state *state)
229104d5d5dfSDmitry Osipenko {
229204d5d5dfSDmitry Osipenko 	const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc;
229304d5d5dfSDmitry Osipenko 	const struct drm_format_info *fmt = state->fb->format;
229404d5d5dfSDmitry Osipenko 	unsigned int src_w = drm_rect_width(&state->src) >> 16;
229504d5d5dfSDmitry Osipenko 	unsigned int dst_w = drm_rect_width(&state->dst);
229604d5d5dfSDmitry Osipenko 
229704d5d5dfSDmitry Osipenko 	if (state->plane->type != DRM_PLANE_TYPE_CURSOR)
229804d5d5dfSDmitry Osipenko 		return false;
229904d5d5dfSDmitry Osipenko 
230004d5d5dfSDmitry Osipenko 	if (soc->supports_cursor)
230104d5d5dfSDmitry Osipenko 		return true;
230204d5d5dfSDmitry Osipenko 
230304d5d5dfSDmitry Osipenko 	if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256)
230404d5d5dfSDmitry Osipenko 		return false;
230504d5d5dfSDmitry Osipenko 
230604d5d5dfSDmitry Osipenko 	return true;
230704d5d5dfSDmitry Osipenko }
230804d5d5dfSDmitry Osipenko 
230904d5d5dfSDmitry Osipenko static unsigned long
231004d5d5dfSDmitry Osipenko tegra_plane_overlap_mask(struct drm_crtc_state *state,
231104d5d5dfSDmitry Osipenko 			 const struct drm_plane_state *plane_state)
231204d5d5dfSDmitry Osipenko {
231304d5d5dfSDmitry Osipenko 	const struct drm_plane_state *other_state;
231404d5d5dfSDmitry Osipenko 	const struct tegra_plane *tegra;
231504d5d5dfSDmitry Osipenko 	unsigned long overlap_mask = 0;
231604d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
231704d5d5dfSDmitry Osipenko 	struct drm_rect rect;
231804d5d5dfSDmitry Osipenko 
231904d5d5dfSDmitry Osipenko 	if (!plane_state->visible || !plane_state->fb)
232004d5d5dfSDmitry Osipenko 		return 0;
232104d5d5dfSDmitry Osipenko 
232204d5d5dfSDmitry Osipenko 	/*
232304d5d5dfSDmitry Osipenko 	 * Data-prefetch FIFO will easily help to overcome temporal memory
232404d5d5dfSDmitry Osipenko 	 * pressure if other plane overlaps with the cursor plane.
232504d5d5dfSDmitry Osipenko 	 */
232604d5d5dfSDmitry Osipenko 	if (tegra_plane_is_cursor(plane_state))
232704d5d5dfSDmitry Osipenko 		return 0;
232804d5d5dfSDmitry Osipenko 
232904d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) {
233004d5d5dfSDmitry Osipenko 		rect = plane_state->dst;
233104d5d5dfSDmitry Osipenko 
233204d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(other_state->plane);
233304d5d5dfSDmitry Osipenko 
233404d5d5dfSDmitry Osipenko 		if (!other_state->visible || !other_state->fb)
233504d5d5dfSDmitry Osipenko 			continue;
233604d5d5dfSDmitry Osipenko 
233704d5d5dfSDmitry Osipenko 		/*
233804d5d5dfSDmitry Osipenko 		 * Ignore cursor plane overlaps because it's not practical to
233904d5d5dfSDmitry Osipenko 		 * assume that it contributes to the bandwidth in overlapping
234004d5d5dfSDmitry Osipenko 		 * area if window width is small.
234104d5d5dfSDmitry Osipenko 		 */
234204d5d5dfSDmitry Osipenko 		if (tegra_plane_is_cursor(other_state))
234304d5d5dfSDmitry Osipenko 			continue;
234404d5d5dfSDmitry Osipenko 
234504d5d5dfSDmitry Osipenko 		if (drm_rect_intersect(&rect, &other_state->dst))
234604d5d5dfSDmitry Osipenko 			overlap_mask |= BIT(tegra->index);
234704d5d5dfSDmitry Osipenko 	}
234804d5d5dfSDmitry Osipenko 
234904d5d5dfSDmitry Osipenko 	return overlap_mask;
235004d5d5dfSDmitry Osipenko }
235104d5d5dfSDmitry Osipenko 
235204d5d5dfSDmitry Osipenko static int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc,
235304d5d5dfSDmitry Osipenko 						 struct drm_atomic_state *state)
235404d5d5dfSDmitry Osipenko {
235504d5d5dfSDmitry Osipenko 	ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask;
235604d5d5dfSDmitry Osipenko 	u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {};
235704d5d5dfSDmitry Osipenko 	bool all_planes_overlap_simultaneously = true;
235804d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *tegra_state;
235904d5d5dfSDmitry Osipenko 	const struct drm_plane_state *plane_state;
236004d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
236104d5d5dfSDmitry Osipenko 	const struct drm_crtc_state *old_state;
236204d5d5dfSDmitry Osipenko 	struct drm_crtc_state *new_state;
236304d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
236404d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
236504d5d5dfSDmitry Osipenko 
236604d5d5dfSDmitry Osipenko 	/*
236704d5d5dfSDmitry Osipenko 	 * The nv-display uses shared planes.  The algorithm below assumes
236804d5d5dfSDmitry Osipenko 	 * maximum 3 planes per-CRTC, this assumption isn't applicable to
236904d5d5dfSDmitry Osipenko 	 * the nv-display.  Note that T124 support has additional windows,
237004d5d5dfSDmitry Osipenko 	 * but currently they aren't supported by the driver.
237104d5d5dfSDmitry Osipenko 	 */
237204d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
237304d5d5dfSDmitry Osipenko 		return 0;
237404d5d5dfSDmitry Osipenko 
237504d5d5dfSDmitry Osipenko 	new_state = drm_atomic_get_new_crtc_state(state, crtc);
237604d5d5dfSDmitry Osipenko 	old_state = drm_atomic_get_old_crtc_state(state, crtc);
237704d5d5dfSDmitry Osipenko 
237804d5d5dfSDmitry Osipenko 	/*
237904d5d5dfSDmitry Osipenko 	 * For overlapping planes pixel's data is fetched for each plane at
238004d5d5dfSDmitry Osipenko 	 * the same time, hence bandwidths are accumulated in this case.
238104d5d5dfSDmitry Osipenko 	 * This needs to be taken into account for calculating total bandwidth
238204d5d5dfSDmitry Osipenko 	 * consumed by all planes.
238304d5d5dfSDmitry Osipenko 	 *
238404d5d5dfSDmitry Osipenko 	 * Here we get the overlapping state of each plane, which is a
238504d5d5dfSDmitry Osipenko 	 * bitmask of plane indices telling with what planes there is an
238604d5d5dfSDmitry Osipenko 	 * overlap. Note that bitmask[plane] includes BIT(plane) in order
238704d5d5dfSDmitry Osipenko 	 * to make further code nicer and simpler.
238804d5d5dfSDmitry Osipenko 	 */
238904d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
239004d5d5dfSDmitry Osipenko 		tegra_state = to_const_tegra_plane_state(plane_state);
239104d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
239204d5d5dfSDmitry Osipenko 
239304d5d5dfSDmitry Osipenko 		if (WARN_ON_ONCE(tegra->index >= TEGRA_DC_LEGACY_PLANES_NUM))
239404d5d5dfSDmitry Osipenko 			return -EINVAL;
239504d5d5dfSDmitry Osipenko 
239604d5d5dfSDmitry Osipenko 		plane_peak_bw[tegra->index] = tegra_state->peak_memory_bandwidth;
239704d5d5dfSDmitry Osipenko 		mask = tegra_plane_overlap_mask(new_state, plane_state);
239804d5d5dfSDmitry Osipenko 		overlap_mask[tegra->index] = mask;
239904d5d5dfSDmitry Osipenko 
240004d5d5dfSDmitry Osipenko 		if (hweight_long(mask) != 3)
240104d5d5dfSDmitry Osipenko 			all_planes_overlap_simultaneously = false;
240204d5d5dfSDmitry Osipenko 	}
240304d5d5dfSDmitry Osipenko 
240404d5d5dfSDmitry Osipenko 	/*
240504d5d5dfSDmitry Osipenko 	 * Then we calculate maximum bandwidth of each plane state.
240604d5d5dfSDmitry Osipenko 	 * The bandwidth includes the plane BW + BW of the "simultaneously"
240704d5d5dfSDmitry Osipenko 	 * overlapping planes, where "simultaneously" means areas where DC
240804d5d5dfSDmitry Osipenko 	 * fetches from the planes simultaneously during of scan-out process.
240904d5d5dfSDmitry Osipenko 	 *
241004d5d5dfSDmitry Osipenko 	 * For example, if plane A overlaps with planes B and C, but B and C
241104d5d5dfSDmitry Osipenko 	 * don't overlap, then the peak bandwidth will be either in area where
241204d5d5dfSDmitry Osipenko 	 * A-and-B or A-and-C planes overlap.
241304d5d5dfSDmitry Osipenko 	 *
241404d5d5dfSDmitry Osipenko 	 * The plane_peak_bw[] contains peak memory bandwidth values of
241504d5d5dfSDmitry Osipenko 	 * each plane, this information is needed by interconnect provider
241604d5d5dfSDmitry Osipenko 	 * in order to set up latency allowance based on the peak BW, see
241704d5d5dfSDmitry Osipenko 	 * tegra_crtc_update_memory_bandwidth().
241804d5d5dfSDmitry Osipenko 	 */
241904d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
242004d5d5dfSDmitry Osipenko 		u32 i, old_peak_bw, new_peak_bw, overlap_bw = 0;
242104d5d5dfSDmitry Osipenko 
242204d5d5dfSDmitry Osipenko 		/*
242304d5d5dfSDmitry Osipenko 		 * Note that plane's atomic check doesn't touch the
242404d5d5dfSDmitry Osipenko 		 * total_peak_memory_bandwidth of enabled plane, hence the
242504d5d5dfSDmitry Osipenko 		 * current state contains the old bandwidth state from the
242604d5d5dfSDmitry Osipenko 		 * previous CRTC commit.
242704d5d5dfSDmitry Osipenko 		 */
242804d5d5dfSDmitry Osipenko 		tegra_state = to_const_tegra_plane_state(plane_state);
242904d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
243004d5d5dfSDmitry Osipenko 
243104d5d5dfSDmitry Osipenko 		for_each_set_bit(i, &overlap_mask[tegra->index], 3) {
243204d5d5dfSDmitry Osipenko 			if (i == tegra->index)
243304d5d5dfSDmitry Osipenko 				continue;
243404d5d5dfSDmitry Osipenko 
243504d5d5dfSDmitry Osipenko 			if (all_planes_overlap_simultaneously)
243604d5d5dfSDmitry Osipenko 				overlap_bw += plane_peak_bw[i];
243704d5d5dfSDmitry Osipenko 			else
243804d5d5dfSDmitry Osipenko 				overlap_bw = max(overlap_bw, plane_peak_bw[i]);
243904d5d5dfSDmitry Osipenko 		}
244004d5d5dfSDmitry Osipenko 
244104d5d5dfSDmitry Osipenko 		new_peak_bw = plane_peak_bw[tegra->index] + overlap_bw;
244204d5d5dfSDmitry Osipenko 		old_peak_bw = tegra_state->total_peak_memory_bandwidth;
244304d5d5dfSDmitry Osipenko 
244404d5d5dfSDmitry Osipenko 		/*
244504d5d5dfSDmitry Osipenko 		 * If plane's peak bandwidth changed (for example plane isn't
244604d5d5dfSDmitry Osipenko 		 * overlapped anymore) and plane isn't in the atomic state,
244704d5d5dfSDmitry Osipenko 		 * then add plane to the state in order to have the bandwidth
244804d5d5dfSDmitry Osipenko 		 * updated.
244904d5d5dfSDmitry Osipenko 		 */
245004d5d5dfSDmitry Osipenko 		if (old_peak_bw != new_peak_bw) {
245104d5d5dfSDmitry Osipenko 			struct tegra_plane_state *new_tegra_state;
245204d5d5dfSDmitry Osipenko 			struct drm_plane_state *new_plane_state;
245304d5d5dfSDmitry Osipenko 
245404d5d5dfSDmitry Osipenko 			new_plane_state = drm_atomic_get_plane_state(state, plane);
245504d5d5dfSDmitry Osipenko 			if (IS_ERR(new_plane_state))
245604d5d5dfSDmitry Osipenko 				return PTR_ERR(new_plane_state);
245704d5d5dfSDmitry Osipenko 
245804d5d5dfSDmitry Osipenko 			new_tegra_state = to_tegra_plane_state(new_plane_state);
245904d5d5dfSDmitry Osipenko 			new_tegra_state->total_peak_memory_bandwidth = new_peak_bw;
246004d5d5dfSDmitry Osipenko 		}
246104d5d5dfSDmitry Osipenko 	}
246204d5d5dfSDmitry Osipenko 
246304d5d5dfSDmitry Osipenko 	return 0;
246404d5d5dfSDmitry Osipenko }
246504d5d5dfSDmitry Osipenko 
246604d5d5dfSDmitry Osipenko static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
246704d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state)
246804d5d5dfSDmitry Osipenko {
246904d5d5dfSDmitry Osipenko 	int err;
247004d5d5dfSDmitry Osipenko 
247104d5d5dfSDmitry Osipenko 	err = tegra_crtc_calculate_memory_bandwidth(crtc, state);
247204d5d5dfSDmitry Osipenko 	if (err)
247304d5d5dfSDmitry Osipenko 		return err;
247404d5d5dfSDmitry Osipenko 
247504d5d5dfSDmitry Osipenko 	return 0;
247604d5d5dfSDmitry Osipenko }
247704d5d5dfSDmitry Osipenko 
247804d5d5dfSDmitry Osipenko void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc,
247904d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state)
248004d5d5dfSDmitry Osipenko {
248104d5d5dfSDmitry Osipenko 	/*
248204d5d5dfSDmitry Osipenko 	 * Display bandwidth is allowed to go down only once hardware state
248304d5d5dfSDmitry Osipenko 	 * is known to be armed, i.e. state was committed and VBLANK event
248404d5d5dfSDmitry Osipenko 	 * received.
248504d5d5dfSDmitry Osipenko 	 */
248604d5d5dfSDmitry Osipenko 	tegra_crtc_update_memory_bandwidth(crtc, state, false);
248704d5d5dfSDmitry Osipenko }
248804d5d5dfSDmitry Osipenko 
2489dee8268fSThierry Reding static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
249004d5d5dfSDmitry Osipenko 	.atomic_check = tegra_crtc_atomic_check,
24914aa3df71SThierry Reding 	.atomic_begin = tegra_crtc_atomic_begin,
24924aa3df71SThierry Reding 	.atomic_flush = tegra_crtc_atomic_flush,
24930b20a0f8SLaurent Pinchart 	.atomic_enable = tegra_crtc_atomic_enable,
249464581714SLaurent Pinchart 	.atomic_disable = tegra_crtc_atomic_disable,
2495dee8268fSThierry Reding };
2496dee8268fSThierry Reding 
2497dee8268fSThierry Reding static irqreturn_t tegra_dc_irq(int irq, void *data)
2498dee8268fSThierry Reding {
2499dee8268fSThierry Reding 	struct tegra_dc *dc = data;
2500dee8268fSThierry Reding 	unsigned long status;
2501dee8268fSThierry Reding 
2502dee8268fSThierry Reding 	status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
2503dee8268fSThierry Reding 	tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
2504dee8268fSThierry Reding 
2505dee8268fSThierry Reding 	if (status & FRAME_END_INT) {
2506dee8268fSThierry Reding 		/*
2507dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): frame end\n", __func__);
2508dee8268fSThierry Reding 		*/
2509ad85b084SDmitry Osipenko 		dc->stats.frames_total++;
2510791ddb1eSThierry Reding 		dc->stats.frames++;
2511dee8268fSThierry Reding 	}
2512dee8268fSThierry Reding 
2513dee8268fSThierry Reding 	if (status & VBLANK_INT) {
2514dee8268fSThierry Reding 		/*
2515dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
2516dee8268fSThierry Reding 		*/
2517ed7dae58SThierry Reding 		drm_crtc_handle_vblank(&dc->base);
2518ad85b084SDmitry Osipenko 		dc->stats.vblank_total++;
2519791ddb1eSThierry Reding 		dc->stats.vblank++;
2520dee8268fSThierry Reding 	}
2521dee8268fSThierry Reding 
2522dee8268fSThierry Reding 	if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
2523dee8268fSThierry Reding 		/*
2524dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): underflow\n", __func__);
2525dee8268fSThierry Reding 		*/
2526ad85b084SDmitry Osipenko 		dc->stats.underflow_total++;
2527791ddb1eSThierry Reding 		dc->stats.underflow++;
2528791ddb1eSThierry Reding 	}
2529791ddb1eSThierry Reding 
2530791ddb1eSThierry Reding 	if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) {
2531791ddb1eSThierry Reding 		/*
2532791ddb1eSThierry Reding 		dev_dbg(dc->dev, "%s(): overflow\n", __func__);
2533791ddb1eSThierry Reding 		*/
2534ad85b084SDmitry Osipenko 		dc->stats.overflow_total++;
2535791ddb1eSThierry Reding 		dc->stats.overflow++;
2536dee8268fSThierry Reding 	}
2537dee8268fSThierry Reding 
253847307954SThierry Reding 	if (status & HEAD_UF_INT) {
253947307954SThierry Reding 		dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
2540ad85b084SDmitry Osipenko 		dc->stats.underflow_total++;
254147307954SThierry Reding 		dc->stats.underflow++;
254247307954SThierry Reding 	}
254347307954SThierry Reding 
2544dee8268fSThierry Reding 	return IRQ_HANDLED;
2545dee8268fSThierry Reding }
2546dee8268fSThierry Reding 
2547e75d0477SThierry Reding static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
2548e75d0477SThierry Reding {
2549e75d0477SThierry Reding 	unsigned int i;
2550e75d0477SThierry Reding 
2551e75d0477SThierry Reding 	if (!dc->soc->wgrps)
2552e75d0477SThierry Reding 		return true;
2553e75d0477SThierry Reding 
2554e75d0477SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
2555e75d0477SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
2556e75d0477SThierry Reding 
2557e75d0477SThierry Reding 		if (wgrp->dc == dc->pipe && wgrp->num_windows > 0)
2558e75d0477SThierry Reding 			return true;
2559e75d0477SThierry Reding 	}
2560e75d0477SThierry Reding 
2561e75d0477SThierry Reding 	return false;
2562e75d0477SThierry Reding }
2563e75d0477SThierry Reding 
256405d1adfeSThierry Reding static int tegra_dc_early_init(struct host1x_client *client)
256505d1adfeSThierry Reding {
256605d1adfeSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
256705d1adfeSThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
256805d1adfeSThierry Reding 
256905d1adfeSThierry Reding 	tegra->num_crtcs++;
257005d1adfeSThierry Reding 
257105d1adfeSThierry Reding 	return 0;
257205d1adfeSThierry Reding }
257305d1adfeSThierry Reding 
2574dee8268fSThierry Reding static int tegra_dc_init(struct host1x_client *client)
2575dee8268fSThierry Reding {
2576608f43adSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
25772bcdcbfaSThierry Reding 	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
2578dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2579d1f3e1e0SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
2580c7679306SThierry Reding 	struct drm_plane *primary = NULL;
2581c7679306SThierry Reding 	struct drm_plane *cursor = NULL;
2582dee8268fSThierry Reding 	int err;
2583dee8268fSThierry Reding 
2584759d706fSThierry Reding 	/*
2585f5ba33fbSMikko Perttunen 	 * DC has been reset by now, so VBLANK syncpoint can be released
2586f5ba33fbSMikko Perttunen 	 * for general use.
2587f5ba33fbSMikko Perttunen 	 */
2588f5ba33fbSMikko Perttunen 	host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);
2589f5ba33fbSMikko Perttunen 
2590f5ba33fbSMikko Perttunen 	/*
2591759d706fSThierry Reding 	 * XXX do not register DCs with no window groups because we cannot
2592759d706fSThierry Reding 	 * assign a primary plane to them, which in turn will cause KMS to
2593759d706fSThierry Reding 	 * crash.
2594759d706fSThierry Reding 	 */
2595e75d0477SThierry Reding 	if (!tegra_dc_has_window_groups(dc))
2596759d706fSThierry Reding 		return 0;
2597759d706fSThierry Reding 
2598fd67e9c6SThierry Reding 	/*
2599fd67e9c6SThierry Reding 	 * Set the display hub as the host1x client parent for the display
2600fd67e9c6SThierry Reding 	 * controller. This is needed for the runtime reference counting that
2601fd67e9c6SThierry Reding 	 * ensures the display hub is always powered when any of the display
2602fd67e9c6SThierry Reding 	 * controllers are.
2603fd67e9c6SThierry Reding 	 */
2604fd67e9c6SThierry Reding 	if (dc->soc->has_nvdisplay)
2605fd67e9c6SThierry Reding 		client->parent = &tegra->hub->client;
2606fd67e9c6SThierry Reding 
2607617dd7ccSThierry Reding 	dc->syncpt = host1x_syncpt_request(client, flags);
26082bcdcbfaSThierry Reding 	if (!dc->syncpt)
26092bcdcbfaSThierry Reding 		dev_warn(dc->dev, "failed to allocate syncpoint\n");
26102bcdcbfaSThierry Reding 
26117edd7961SThierry Reding 	err = host1x_client_iommu_attach(client);
2612a8817489SThierry Reding 	if (err < 0 && err != -ENODEV) {
26130c407de5SThierry Reding 		dev_err(client->dev, "failed to attach to domain: %d\n", err);
2614df06b759SThierry Reding 		return err;
2615df06b759SThierry Reding 	}
2616df06b759SThierry Reding 
261747307954SThierry Reding 	if (dc->soc->wgrps)
261847307954SThierry Reding 		primary = tegra_dc_add_shared_planes(drm, dc);
261947307954SThierry Reding 	else
262047307954SThierry Reding 		primary = tegra_dc_add_planes(drm, dc);
262147307954SThierry Reding 
2622c7679306SThierry Reding 	if (IS_ERR(primary)) {
2623c7679306SThierry Reding 		err = PTR_ERR(primary);
2624c7679306SThierry Reding 		goto cleanup;
2625c7679306SThierry Reding 	}
2626c7679306SThierry Reding 
2627c7679306SThierry Reding 	if (dc->soc->supports_cursor) {
2628c7679306SThierry Reding 		cursor = tegra_dc_cursor_plane_create(drm, dc);
2629c7679306SThierry Reding 		if (IS_ERR(cursor)) {
2630c7679306SThierry Reding 			err = PTR_ERR(cursor);
2631c7679306SThierry Reding 			goto cleanup;
2632c7679306SThierry Reding 		}
26339f446d83SDmitry Osipenko 	} else {
26349f446d83SDmitry Osipenko 		/* dedicate one overlay to mouse cursor */
26359f446d83SDmitry Osipenko 		cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true);
26369f446d83SDmitry Osipenko 		if (IS_ERR(cursor)) {
26379f446d83SDmitry Osipenko 			err = PTR_ERR(cursor);
26389f446d83SDmitry Osipenko 			goto cleanup;
26399f446d83SDmitry Osipenko 		}
2640c7679306SThierry Reding 	}
2641c7679306SThierry Reding 
2642c7679306SThierry Reding 	err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
2643f9882876SVille Syrjälä 					&tegra_crtc_funcs, NULL);
2644c7679306SThierry Reding 	if (err < 0)
2645c7679306SThierry Reding 		goto cleanup;
2646c7679306SThierry Reding 
2647dee8268fSThierry Reding 	drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
2648dee8268fSThierry Reding 
2649d1f3e1e0SThierry Reding 	/*
2650d1f3e1e0SThierry Reding 	 * Keep track of the minimum pitch alignment across all display
2651d1f3e1e0SThierry Reding 	 * controllers.
2652d1f3e1e0SThierry Reding 	 */
2653d1f3e1e0SThierry Reding 	if (dc->soc->pitch_align > tegra->pitch_align)
2654d1f3e1e0SThierry Reding 		tegra->pitch_align = dc->soc->pitch_align;
2655d1f3e1e0SThierry Reding 
2656042c0bd7SThierry Reding 	/* track maximum resolution */
2657042c0bd7SThierry Reding 	if (dc->soc->has_nvdisplay)
2658042c0bd7SThierry Reding 		drm->mode_config.max_width = drm->mode_config.max_height = 16384;
2659042c0bd7SThierry Reding 	else
2660042c0bd7SThierry Reding 		drm->mode_config.max_width = drm->mode_config.max_height = 4096;
2661042c0bd7SThierry Reding 
26629910f5c4SThierry Reding 	err = tegra_dc_rgb_init(drm, dc);
2663dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
2664dee8268fSThierry Reding 		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
2665c7679306SThierry Reding 		goto cleanup;
2666dee8268fSThierry Reding 	}
2667dee8268fSThierry Reding 
2668dee8268fSThierry Reding 	err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
2669dee8268fSThierry Reding 			       dev_name(dc->dev), dc);
2670dee8268fSThierry Reding 	if (err < 0) {
2671dee8268fSThierry Reding 		dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
2672dee8268fSThierry Reding 			err);
2673c7679306SThierry Reding 		goto cleanup;
2674dee8268fSThierry Reding 	}
2675dee8268fSThierry Reding 
267647b15779SThierry Reding 	/*
267747b15779SThierry Reding 	 * Inherit the DMA parameters (such as maximum segment size) from the
2678608f43adSThierry Reding 	 * parent host1x device.
267947b15779SThierry Reding 	 */
2680608f43adSThierry Reding 	client->dev->dma_parms = client->host->dma_parms;
268147b15779SThierry Reding 
2682dee8268fSThierry Reding 	return 0;
2683c7679306SThierry Reding 
2684c7679306SThierry Reding cleanup:
268547307954SThierry Reding 	if (!IS_ERR_OR_NULL(cursor))
2686c7679306SThierry Reding 		drm_plane_cleanup(cursor);
2687c7679306SThierry Reding 
268847307954SThierry Reding 	if (!IS_ERR(primary))
2689c7679306SThierry Reding 		drm_plane_cleanup(primary);
2690c7679306SThierry Reding 
2691aacdf198SThierry Reding 	host1x_client_iommu_detach(client);
26922aed4f5aSMikko Perttunen 	host1x_syncpt_put(dc->syncpt);
2693fd5ec0dcSThierry Reding 
2694c7679306SThierry Reding 	return err;
2695dee8268fSThierry Reding }
2696dee8268fSThierry Reding 
2697dee8268fSThierry Reding static int tegra_dc_exit(struct host1x_client *client)
2698dee8268fSThierry Reding {
2699dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2700dee8268fSThierry Reding 	int err;
2701dee8268fSThierry Reding 
2702e75d0477SThierry Reding 	if (!tegra_dc_has_window_groups(dc))
2703e75d0477SThierry Reding 		return 0;
2704e75d0477SThierry Reding 
270547b15779SThierry Reding 	/* avoid a dangling pointer just in case this disappears */
270647b15779SThierry Reding 	client->dev->dma_parms = NULL;
270747b15779SThierry Reding 
2708dee8268fSThierry Reding 	devm_free_irq(dc->dev, dc->irq, dc);
2709dee8268fSThierry Reding 
2710dee8268fSThierry Reding 	err = tegra_dc_rgb_exit(dc);
2711dee8268fSThierry Reding 	if (err) {
2712dee8268fSThierry Reding 		dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err);
2713dee8268fSThierry Reding 		return err;
2714dee8268fSThierry Reding 	}
2715dee8268fSThierry Reding 
2716aacdf198SThierry Reding 	host1x_client_iommu_detach(client);
27172aed4f5aSMikko Perttunen 	host1x_syncpt_put(dc->syncpt);
27182bcdcbfaSThierry Reding 
2719dee8268fSThierry Reding 	return 0;
2720dee8268fSThierry Reding }
2721dee8268fSThierry Reding 
272205d1adfeSThierry Reding static int tegra_dc_late_exit(struct host1x_client *client)
272305d1adfeSThierry Reding {
272405d1adfeSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
272505d1adfeSThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
272605d1adfeSThierry Reding 
272705d1adfeSThierry Reding 	tegra->num_crtcs--;
2728dee8268fSThierry Reding 
2729dee8268fSThierry Reding 	return 0;
2730dee8268fSThierry Reding }
2731dee8268fSThierry Reding 
2732fd67e9c6SThierry Reding static int tegra_dc_runtime_suspend(struct host1x_client *client)
2733fd67e9c6SThierry Reding {
2734fd67e9c6SThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2735fd67e9c6SThierry Reding 	struct device *dev = client->dev;
2736fd67e9c6SThierry Reding 	int err;
2737fd67e9c6SThierry Reding 
2738fd67e9c6SThierry Reding 	err = reset_control_assert(dc->rst);
2739fd67e9c6SThierry Reding 	if (err < 0) {
2740fd67e9c6SThierry Reding 		dev_err(dev, "failed to assert reset: %d\n", err);
2741fd67e9c6SThierry Reding 		return err;
2742fd67e9c6SThierry Reding 	}
2743fd67e9c6SThierry Reding 
2744fd67e9c6SThierry Reding 	if (dc->soc->has_powergate)
2745fd67e9c6SThierry Reding 		tegra_powergate_power_off(dc->powergate);
2746fd67e9c6SThierry Reding 
2747fd67e9c6SThierry Reding 	clk_disable_unprepare(dc->clk);
2748fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
2749fd67e9c6SThierry Reding 
2750fd67e9c6SThierry Reding 	return 0;
2751fd67e9c6SThierry Reding }
2752fd67e9c6SThierry Reding 
2753fd67e9c6SThierry Reding static int tegra_dc_runtime_resume(struct host1x_client *client)
2754fd67e9c6SThierry Reding {
2755fd67e9c6SThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2756fd67e9c6SThierry Reding 	struct device *dev = client->dev;
2757fd67e9c6SThierry Reding 	int err;
2758fd67e9c6SThierry Reding 
2759dcdfe271SQinglang Miao 	err = pm_runtime_resume_and_get(dev);
2760fd67e9c6SThierry Reding 	if (err < 0) {
2761fd67e9c6SThierry Reding 		dev_err(dev, "failed to get runtime PM: %d\n", err);
2762fd67e9c6SThierry Reding 		return err;
2763fd67e9c6SThierry Reding 	}
2764fd67e9c6SThierry Reding 
2765fd67e9c6SThierry Reding 	if (dc->soc->has_powergate) {
2766fd67e9c6SThierry Reding 		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
2767fd67e9c6SThierry Reding 							dc->rst);
2768fd67e9c6SThierry Reding 		if (err < 0) {
2769fd67e9c6SThierry Reding 			dev_err(dev, "failed to power partition: %d\n", err);
2770fd67e9c6SThierry Reding 			goto put_rpm;
2771fd67e9c6SThierry Reding 		}
2772fd67e9c6SThierry Reding 	} else {
2773fd67e9c6SThierry Reding 		err = clk_prepare_enable(dc->clk);
2774fd67e9c6SThierry Reding 		if (err < 0) {
2775fd67e9c6SThierry Reding 			dev_err(dev, "failed to enable clock: %d\n", err);
2776fd67e9c6SThierry Reding 			goto put_rpm;
2777fd67e9c6SThierry Reding 		}
2778fd67e9c6SThierry Reding 
2779fd67e9c6SThierry Reding 		err = reset_control_deassert(dc->rst);
2780fd67e9c6SThierry Reding 		if (err < 0) {
2781fd67e9c6SThierry Reding 			dev_err(dev, "failed to deassert reset: %d\n", err);
2782fd67e9c6SThierry Reding 			goto disable_clk;
2783fd67e9c6SThierry Reding 		}
2784fd67e9c6SThierry Reding 	}
2785fd67e9c6SThierry Reding 
2786fd67e9c6SThierry Reding 	return 0;
2787fd67e9c6SThierry Reding 
2788fd67e9c6SThierry Reding disable_clk:
2789fd67e9c6SThierry Reding 	clk_disable_unprepare(dc->clk);
2790fd67e9c6SThierry Reding put_rpm:
2791fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
2792fd67e9c6SThierry Reding 	return err;
2793fd67e9c6SThierry Reding }
2794fd67e9c6SThierry Reding 
2795dee8268fSThierry Reding static const struct host1x_client_ops dc_client_ops = {
279605d1adfeSThierry Reding 	.early_init = tegra_dc_early_init,
2797dee8268fSThierry Reding 	.init = tegra_dc_init,
2798dee8268fSThierry Reding 	.exit = tegra_dc_exit,
279905d1adfeSThierry Reding 	.late_exit = tegra_dc_late_exit,
2800fd67e9c6SThierry Reding 	.suspend = tegra_dc_runtime_suspend,
2801fd67e9c6SThierry Reding 	.resume = tegra_dc_runtime_resume,
2802dee8268fSThierry Reding };
2803dee8268fSThierry Reding 
28048620fc62SThierry Reding static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
28057116e9a8SThierry Reding 	.supports_background_color = false,
28068620fc62SThierry Reding 	.supports_interlacing = false,
2807e687651bSThierry Reding 	.supports_cursor = false,
2808c134f019SThierry Reding 	.supports_block_linear = false,
28097b6f8467SThierry Reding 	.supports_sector_layout = false,
2810a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
2811d1f3e1e0SThierry Reding 	.pitch_align = 8,
28129c012700SThierry Reding 	.has_powergate = false,
2813f68ba691SDmitry Osipenko 	.coupled_pm = true,
281447307954SThierry Reding 	.has_nvdisplay = false,
2815511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
2816511c7023SThierry Reding 	.primary_formats = tegra20_primary_formats,
2817511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
2818511c7023SThierry Reding 	.overlay_formats = tegra20_overlay_formats,
2819e90124cbSThierry Reding 	.modifiers = tegra20_modifiers,
2820acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = true,
282104d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = true,
2822acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = true,
282304d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
28240c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
28258620fc62SThierry Reding };
28268620fc62SThierry Reding 
28278620fc62SThierry Reding static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
28287116e9a8SThierry Reding 	.supports_background_color = false,
28298620fc62SThierry Reding 	.supports_interlacing = false,
2830e687651bSThierry Reding 	.supports_cursor = false,
2831c134f019SThierry Reding 	.supports_block_linear = false,
28327b6f8467SThierry Reding 	.supports_sector_layout = false,
2833a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
2834d1f3e1e0SThierry Reding 	.pitch_align = 8,
28359c012700SThierry Reding 	.has_powergate = false,
2836f68ba691SDmitry Osipenko 	.coupled_pm = false,
283747307954SThierry Reding 	.has_nvdisplay = false,
2838511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
2839511c7023SThierry Reding 	.primary_formats = tegra20_primary_formats,
2840511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
2841511c7023SThierry Reding 	.overlay_formats = tegra20_overlay_formats,
2842e90124cbSThierry Reding 	.modifiers = tegra20_modifiers,
2843acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
284404d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = true,
2845acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
284604d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = true,
28470c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
2848d1f3e1e0SThierry Reding };
2849d1f3e1e0SThierry Reding 
2850d1f3e1e0SThierry Reding static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
28517116e9a8SThierry Reding 	.supports_background_color = false,
2852d1f3e1e0SThierry Reding 	.supports_interlacing = false,
2853d1f3e1e0SThierry Reding 	.supports_cursor = false,
2854d1f3e1e0SThierry Reding 	.supports_block_linear = false,
28557b6f8467SThierry Reding 	.supports_sector_layout = false,
2856a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
2857d1f3e1e0SThierry Reding 	.pitch_align = 64,
28589c012700SThierry Reding 	.has_powergate = true,
2859f68ba691SDmitry Osipenko 	.coupled_pm = false,
286047307954SThierry Reding 	.has_nvdisplay = false,
2861511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
2862511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
2863511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
2864511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
2865e90124cbSThierry Reding 	.modifiers = tegra20_modifiers,
2866acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
286704d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = false,
2868acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
286904d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = true,
28700c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
28718620fc62SThierry Reding };
28728620fc62SThierry Reding 
28738620fc62SThierry Reding static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
28747116e9a8SThierry Reding 	.supports_background_color = true,
28758620fc62SThierry Reding 	.supports_interlacing = true,
2876e687651bSThierry Reding 	.supports_cursor = true,
2877c134f019SThierry Reding 	.supports_block_linear = true,
28787b6f8467SThierry Reding 	.supports_sector_layout = false,
2879a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
2880d1f3e1e0SThierry Reding 	.pitch_align = 64,
28819c012700SThierry Reding 	.has_powergate = true,
2882f68ba691SDmitry Osipenko 	.coupled_pm = false,
288347307954SThierry Reding 	.has_nvdisplay = false,
2884511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra124_primary_formats),
28859a02d3afSStefan Agner 	.primary_formats = tegra124_primary_formats,
2886511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
28879a02d3afSStefan Agner 	.overlay_formats = tegra124_overlay_formats,
2888e90124cbSThierry Reding 	.modifiers = tegra124_modifiers,
2889acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
289004d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = false,
2891acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
289204d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
28930c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
28948620fc62SThierry Reding };
28958620fc62SThierry Reding 
28965b4f516fSThierry Reding static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
28977116e9a8SThierry Reding 	.supports_background_color = true,
28985b4f516fSThierry Reding 	.supports_interlacing = true,
28995b4f516fSThierry Reding 	.supports_cursor = true,
29005b4f516fSThierry Reding 	.supports_block_linear = true,
29017b6f8467SThierry Reding 	.supports_sector_layout = false,
2902a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
29035b4f516fSThierry Reding 	.pitch_align = 64,
29045b4f516fSThierry Reding 	.has_powergate = true,
2905f68ba691SDmitry Osipenko 	.coupled_pm = false,
290647307954SThierry Reding 	.has_nvdisplay = false,
2907511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
2908511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
2909511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
2910511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
2911e90124cbSThierry Reding 	.modifiers = tegra124_modifiers,
2912acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
291304d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = false,
2914acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
291504d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
29160c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
291747307954SThierry Reding };
291847307954SThierry Reding 
291947307954SThierry Reding static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
292047307954SThierry Reding 	{
292147307954SThierry Reding 		.index = 0,
292247307954SThierry Reding 		.dc = 0,
292347307954SThierry Reding 		.windows = (const unsigned int[]) { 0 },
292447307954SThierry Reding 		.num_windows = 1,
292547307954SThierry Reding 	}, {
292647307954SThierry Reding 		.index = 1,
292747307954SThierry Reding 		.dc = 1,
292847307954SThierry Reding 		.windows = (const unsigned int[]) { 1 },
292947307954SThierry Reding 		.num_windows = 1,
293047307954SThierry Reding 	}, {
293147307954SThierry Reding 		.index = 2,
293247307954SThierry Reding 		.dc = 1,
293347307954SThierry Reding 		.windows = (const unsigned int[]) { 2 },
293447307954SThierry Reding 		.num_windows = 1,
293547307954SThierry Reding 	}, {
293647307954SThierry Reding 		.index = 3,
293747307954SThierry Reding 		.dc = 2,
293847307954SThierry Reding 		.windows = (const unsigned int[]) { 3 },
293947307954SThierry Reding 		.num_windows = 1,
294047307954SThierry Reding 	}, {
294147307954SThierry Reding 		.index = 4,
294247307954SThierry Reding 		.dc = 2,
294347307954SThierry Reding 		.windows = (const unsigned int[]) { 4 },
294447307954SThierry Reding 		.num_windows = 1,
294547307954SThierry Reding 	}, {
294647307954SThierry Reding 		.index = 5,
294747307954SThierry Reding 		.dc = 2,
294847307954SThierry Reding 		.windows = (const unsigned int[]) { 5 },
294947307954SThierry Reding 		.num_windows = 1,
295047307954SThierry Reding 	},
295147307954SThierry Reding };
295247307954SThierry Reding 
295347307954SThierry Reding static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
295447307954SThierry Reding 	.supports_background_color = true,
295547307954SThierry Reding 	.supports_interlacing = true,
295647307954SThierry Reding 	.supports_cursor = true,
295747307954SThierry Reding 	.supports_block_linear = true,
29587b6f8467SThierry Reding 	.supports_sector_layout = false,
2959a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
296047307954SThierry Reding 	.pitch_align = 64,
296147307954SThierry Reding 	.has_powergate = false,
2962f68ba691SDmitry Osipenko 	.coupled_pm = false,
296347307954SThierry Reding 	.has_nvdisplay = true,
296447307954SThierry Reding 	.wgrps = tegra186_dc_wgrps,
296547307954SThierry Reding 	.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
296604d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
29670c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
29685b4f516fSThierry Reding };
29695b4f516fSThierry Reding 
297047443196SThierry Reding static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
297147443196SThierry Reding 	{
297247443196SThierry Reding 		.index = 0,
297347443196SThierry Reding 		.dc = 0,
297447443196SThierry Reding 		.windows = (const unsigned int[]) { 0 },
297547443196SThierry Reding 		.num_windows = 1,
297647443196SThierry Reding 	}, {
297747443196SThierry Reding 		.index = 1,
297847443196SThierry Reding 		.dc = 1,
297947443196SThierry Reding 		.windows = (const unsigned int[]) { 1 },
298047443196SThierry Reding 		.num_windows = 1,
298147443196SThierry Reding 	}, {
298247443196SThierry Reding 		.index = 2,
298347443196SThierry Reding 		.dc = 1,
298447443196SThierry Reding 		.windows = (const unsigned int[]) { 2 },
298547443196SThierry Reding 		.num_windows = 1,
298647443196SThierry Reding 	}, {
298747443196SThierry Reding 		.index = 3,
298847443196SThierry Reding 		.dc = 2,
298947443196SThierry Reding 		.windows = (const unsigned int[]) { 3 },
299047443196SThierry Reding 		.num_windows = 1,
299147443196SThierry Reding 	}, {
299247443196SThierry Reding 		.index = 4,
299347443196SThierry Reding 		.dc = 2,
299447443196SThierry Reding 		.windows = (const unsigned int[]) { 4 },
299547443196SThierry Reding 		.num_windows = 1,
299647443196SThierry Reding 	}, {
299747443196SThierry Reding 		.index = 5,
299847443196SThierry Reding 		.dc = 2,
299947443196SThierry Reding 		.windows = (const unsigned int[]) { 5 },
300047443196SThierry Reding 		.num_windows = 1,
300147443196SThierry Reding 	},
300247443196SThierry Reding };
300347443196SThierry Reding 
300447443196SThierry Reding static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
300547443196SThierry Reding 	.supports_background_color = true,
300647443196SThierry Reding 	.supports_interlacing = true,
300747443196SThierry Reding 	.supports_cursor = true,
300847443196SThierry Reding 	.supports_block_linear = true,
30097b6f8467SThierry Reding 	.supports_sector_layout = true,
301047443196SThierry Reding 	.has_legacy_blending = false,
301147443196SThierry Reding 	.pitch_align = 64,
301247443196SThierry Reding 	.has_powergate = false,
301347443196SThierry Reding 	.coupled_pm = false,
301447443196SThierry Reding 	.has_nvdisplay = true,
301547443196SThierry Reding 	.wgrps = tegra194_dc_wgrps,
301647443196SThierry Reding 	.num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
301704d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
30180c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
301947443196SThierry Reding };
302047443196SThierry Reding 
30218620fc62SThierry Reding static const struct of_device_id tegra_dc_of_match[] = {
30228620fc62SThierry Reding 	{
302347443196SThierry Reding 		.compatible = "nvidia,tegra194-dc",
302447443196SThierry Reding 		.data = &tegra194_dc_soc_info,
302547443196SThierry Reding 	}, {
302647307954SThierry Reding 		.compatible = "nvidia,tegra186-dc",
302747307954SThierry Reding 		.data = &tegra186_dc_soc_info,
302847307954SThierry Reding 	}, {
30295b4f516fSThierry Reding 		.compatible = "nvidia,tegra210-dc",
30305b4f516fSThierry Reding 		.data = &tegra210_dc_soc_info,
30315b4f516fSThierry Reding 	}, {
30328620fc62SThierry Reding 		.compatible = "nvidia,tegra124-dc",
30338620fc62SThierry Reding 		.data = &tegra124_dc_soc_info,
30348620fc62SThierry Reding 	}, {
30359c012700SThierry Reding 		.compatible = "nvidia,tegra114-dc",
30369c012700SThierry Reding 		.data = &tegra114_dc_soc_info,
30379c012700SThierry Reding 	}, {
30388620fc62SThierry Reding 		.compatible = "nvidia,tegra30-dc",
30398620fc62SThierry Reding 		.data = &tegra30_dc_soc_info,
30408620fc62SThierry Reding 	}, {
30418620fc62SThierry Reding 		.compatible = "nvidia,tegra20-dc",
30428620fc62SThierry Reding 		.data = &tegra20_dc_soc_info,
30438620fc62SThierry Reding 	}, {
30448620fc62SThierry Reding 		/* sentinel */
30458620fc62SThierry Reding 	}
30468620fc62SThierry Reding };
3047ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_dc_of_match);
30488620fc62SThierry Reding 
304913411dddSThierry Reding static int tegra_dc_parse_dt(struct tegra_dc *dc)
305013411dddSThierry Reding {
305113411dddSThierry Reding 	struct device_node *np;
305213411dddSThierry Reding 	u32 value = 0;
305313411dddSThierry Reding 	int err;
305413411dddSThierry Reding 
305513411dddSThierry Reding 	err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value);
305613411dddSThierry Reding 	if (err < 0) {
305713411dddSThierry Reding 		dev_err(dc->dev, "missing \"nvidia,head\" property\n");
305813411dddSThierry Reding 
305913411dddSThierry Reding 		/*
306013411dddSThierry Reding 		 * If the nvidia,head property isn't present, try to find the
306113411dddSThierry Reding 		 * correct head number by looking up the position of this
306213411dddSThierry Reding 		 * display controller's node within the device tree. Assuming
306313411dddSThierry Reding 		 * that the nodes are ordered properly in the DTS file and
306413411dddSThierry Reding 		 * that the translation into a flattened device tree blob
306513411dddSThierry Reding 		 * preserves that ordering this will actually yield the right
306613411dddSThierry Reding 		 * head number.
306713411dddSThierry Reding 		 *
306813411dddSThierry Reding 		 * If those assumptions don't hold, this will still work for
306913411dddSThierry Reding 		 * cases where only a single display controller is used.
307013411dddSThierry Reding 		 */
307113411dddSThierry Reding 		for_each_matching_node(np, tegra_dc_of_match) {
3072cf6b1744SJulia Lawall 			if (np == dc->dev->of_node) {
3073cf6b1744SJulia Lawall 				of_node_put(np);
307413411dddSThierry Reding 				break;
3075cf6b1744SJulia Lawall 			}
307613411dddSThierry Reding 
307713411dddSThierry Reding 			value++;
307813411dddSThierry Reding 		}
307913411dddSThierry Reding 	}
308013411dddSThierry Reding 
308113411dddSThierry Reding 	dc->pipe = value;
308213411dddSThierry Reding 
308313411dddSThierry Reding 	return 0;
308413411dddSThierry Reding }
308513411dddSThierry Reding 
308692ce7e83SSuzuki K Poulose static int tegra_dc_match_by_pipe(struct device *dev, const void *data)
3087f68ba691SDmitry Osipenko {
3088f68ba691SDmitry Osipenko 	struct tegra_dc *dc = dev_get_drvdata(dev);
308992ce7e83SSuzuki K Poulose 	unsigned int pipe = (unsigned long)(void *)data;
3090f68ba691SDmitry Osipenko 
3091f68ba691SDmitry Osipenko 	return dc->pipe == pipe;
3092f68ba691SDmitry Osipenko }
3093f68ba691SDmitry Osipenko 
3094f68ba691SDmitry Osipenko static int tegra_dc_couple(struct tegra_dc *dc)
3095f68ba691SDmitry Osipenko {
3096f68ba691SDmitry Osipenko 	/*
3097f68ba691SDmitry Osipenko 	 * On Tegra20, DC1 requires DC0 to be taken out of reset in order to
3098f68ba691SDmitry Osipenko 	 * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND /
3099f68ba691SDmitry Osipenko 	 * POWER_CONTROL registers during CRTC enabling.
3100f68ba691SDmitry Osipenko 	 */
3101f68ba691SDmitry Osipenko 	if (dc->soc->coupled_pm && dc->pipe == 1) {
3102a31500feSThierry Reding 		struct device *companion;
3103a31500feSThierry Reding 		struct tegra_dc *parent;
3104f68ba691SDmitry Osipenko 
3105a31500feSThierry Reding 		companion = driver_find_device(dc->dev->driver, NULL, (const void *)0,
3106f68ba691SDmitry Osipenko 					       tegra_dc_match_by_pipe);
3107a31500feSThierry Reding 		if (!companion)
3108f68ba691SDmitry Osipenko 			return -EPROBE_DEFER;
3109f68ba691SDmitry Osipenko 
3110a31500feSThierry Reding 		parent = dev_get_drvdata(companion);
3111a31500feSThierry Reding 		dc->client.parent = &parent->client;
3112f68ba691SDmitry Osipenko 
3113a31500feSThierry Reding 		dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion));
3114f68ba691SDmitry Osipenko 	}
3115f68ba691SDmitry Osipenko 
3116f68ba691SDmitry Osipenko 	return 0;
3117f68ba691SDmitry Osipenko }
3118f68ba691SDmitry Osipenko 
3119*4ce3048cSDmitry Osipenko static int tegra_dc_init_opp_table(struct tegra_dc *dc)
3120*4ce3048cSDmitry Osipenko {
3121*4ce3048cSDmitry Osipenko 	struct tegra_core_opp_params opp_params = {};
3122*4ce3048cSDmitry Osipenko 	int err;
3123*4ce3048cSDmitry Osipenko 
3124*4ce3048cSDmitry Osipenko 	err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
3125*4ce3048cSDmitry Osipenko 	if (err && err != -ENODEV)
3126*4ce3048cSDmitry Osipenko 		return err;
3127*4ce3048cSDmitry Osipenko 
3128*4ce3048cSDmitry Osipenko 	if (err)
3129*4ce3048cSDmitry Osipenko 		dc->has_opp_table = false;
3130*4ce3048cSDmitry Osipenko 	else
3131*4ce3048cSDmitry Osipenko 		dc->has_opp_table = true;
3132*4ce3048cSDmitry Osipenko 
3133*4ce3048cSDmitry Osipenko 	return 0;
3134*4ce3048cSDmitry Osipenko }
3135*4ce3048cSDmitry Osipenko 
3136dee8268fSThierry Reding static int tegra_dc_probe(struct platform_device *pdev)
3137dee8268fSThierry Reding {
313886044e74SThierry Reding 	u64 dma_mask = dma_get_mask(pdev->dev.parent);
3139dee8268fSThierry Reding 	struct tegra_dc *dc;
3140dee8268fSThierry Reding 	int err;
3141dee8268fSThierry Reding 
314286044e74SThierry Reding 	err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
314386044e74SThierry Reding 	if (err < 0) {
314486044e74SThierry Reding 		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
314586044e74SThierry Reding 		return err;
314686044e74SThierry Reding 	}
314786044e74SThierry Reding 
3148dee8268fSThierry Reding 	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
3149dee8268fSThierry Reding 	if (!dc)
3150dee8268fSThierry Reding 		return -ENOMEM;
3151dee8268fSThierry Reding 
3152b9ff7aeaSThierry Reding 	dc->soc = of_device_get_match_data(&pdev->dev);
31538620fc62SThierry Reding 
3154dee8268fSThierry Reding 	INIT_LIST_HEAD(&dc->list);
3155dee8268fSThierry Reding 	dc->dev = &pdev->dev;
3156dee8268fSThierry Reding 
315713411dddSThierry Reding 	err = tegra_dc_parse_dt(dc);
315813411dddSThierry Reding 	if (err < 0)
315913411dddSThierry Reding 		return err;
316013411dddSThierry Reding 
3161f68ba691SDmitry Osipenko 	err = tegra_dc_couple(dc);
3162f68ba691SDmitry Osipenko 	if (err < 0)
3163f68ba691SDmitry Osipenko 		return err;
3164f68ba691SDmitry Osipenko 
3165dee8268fSThierry Reding 	dc->clk = devm_clk_get(&pdev->dev, NULL);
3166dee8268fSThierry Reding 	if (IS_ERR(dc->clk)) {
3167dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to get clock\n");
3168dee8268fSThierry Reding 		return PTR_ERR(dc->clk);
3169dee8268fSThierry Reding 	}
3170dee8268fSThierry Reding 
3171ca48080aSStephen Warren 	dc->rst = devm_reset_control_get(&pdev->dev, "dc");
3172ca48080aSStephen Warren 	if (IS_ERR(dc->rst)) {
3173ca48080aSStephen Warren 		dev_err(&pdev->dev, "failed to get reset\n");
3174ca48080aSStephen Warren 		return PTR_ERR(dc->rst);
3175ca48080aSStephen Warren 	}
3176ca48080aSStephen Warren 
3177a2f2f740SThierry Reding 	/* assert reset and disable clock */
3178a2f2f740SThierry Reding 	err = clk_prepare_enable(dc->clk);
3179a2f2f740SThierry Reding 	if (err < 0)
3180a2f2f740SThierry Reding 		return err;
3181a2f2f740SThierry Reding 
3182a2f2f740SThierry Reding 	usleep_range(2000, 4000);
3183a2f2f740SThierry Reding 
3184a2f2f740SThierry Reding 	err = reset_control_assert(dc->rst);
3185a2f2f740SThierry Reding 	if (err < 0)
3186a2f2f740SThierry Reding 		return err;
3187a2f2f740SThierry Reding 
3188a2f2f740SThierry Reding 	usleep_range(2000, 4000);
3189a2f2f740SThierry Reding 
3190a2f2f740SThierry Reding 	clk_disable_unprepare(dc->clk);
319133a8eb8dSThierry Reding 
31929c012700SThierry Reding 	if (dc->soc->has_powergate) {
31939c012700SThierry Reding 		if (dc->pipe == 0)
31949c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DIS;
31959c012700SThierry Reding 		else
31969c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DISB;
31979c012700SThierry Reding 
319833a8eb8dSThierry Reding 		tegra_powergate_power_off(dc->powergate);
31999c012700SThierry Reding 	}
3200dee8268fSThierry Reding 
3201*4ce3048cSDmitry Osipenko 	err = tegra_dc_init_opp_table(dc);
3202*4ce3048cSDmitry Osipenko 	if (err < 0)
3203*4ce3048cSDmitry Osipenko 		return err;
3204*4ce3048cSDmitry Osipenko 
3205a858ac8fSDmitry Osipenko 	dc->regs = devm_platform_ioremap_resource(pdev, 0);
3206dee8268fSThierry Reding 	if (IS_ERR(dc->regs))
3207dee8268fSThierry Reding 		return PTR_ERR(dc->regs);
3208dee8268fSThierry Reding 
3209dee8268fSThierry Reding 	dc->irq = platform_get_irq(pdev, 0);
32105f1df70fSTang Bin 	if (dc->irq < 0)
3211dee8268fSThierry Reding 		return -ENXIO;
3212dee8268fSThierry Reding 
3213dee8268fSThierry Reding 	err = tegra_dc_rgb_probe(dc);
3214dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
32158f839fb6SDmitry Osipenko 		const char *level = KERN_ERR;
32168f839fb6SDmitry Osipenko 
32178f839fb6SDmitry Osipenko 		if (err == -EPROBE_DEFER)
32188f839fb6SDmitry Osipenko 			level = KERN_DEBUG;
32198f839fb6SDmitry Osipenko 
32208f839fb6SDmitry Osipenko 		dev_printk(level, dc->dev, "failed to probe RGB output: %d\n",
32218f839fb6SDmitry Osipenko 			   err);
3222dee8268fSThierry Reding 		return err;
3223dee8268fSThierry Reding 	}
3224dee8268fSThierry Reding 
322533a8eb8dSThierry Reding 	platform_set_drvdata(pdev, dc);
322633a8eb8dSThierry Reding 	pm_runtime_enable(&pdev->dev);
322733a8eb8dSThierry Reding 
322833a8eb8dSThierry Reding 	INIT_LIST_HEAD(&dc->client.list);
322933a8eb8dSThierry Reding 	dc->client.ops = &dc_client_ops;
323033a8eb8dSThierry Reding 	dc->client.dev = &pdev->dev;
323133a8eb8dSThierry Reding 
3232dee8268fSThierry Reding 	err = host1x_client_register(&dc->client);
3233dee8268fSThierry Reding 	if (err < 0) {
3234dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
3235dee8268fSThierry Reding 			err);
32360411ea89SDmitry Osipenko 		goto disable_pm;
3237dee8268fSThierry Reding 	}
3238dee8268fSThierry Reding 
3239dee8268fSThierry Reding 	return 0;
32400411ea89SDmitry Osipenko 
32410411ea89SDmitry Osipenko disable_pm:
32420411ea89SDmitry Osipenko 	pm_runtime_disable(&pdev->dev);
32430411ea89SDmitry Osipenko 	tegra_dc_rgb_remove(dc);
32440411ea89SDmitry Osipenko 
32450411ea89SDmitry Osipenko 	return err;
3246dee8268fSThierry Reding }
3247dee8268fSThierry Reding 
3248dee8268fSThierry Reding static int tegra_dc_remove(struct platform_device *pdev)
3249dee8268fSThierry Reding {
3250dee8268fSThierry Reding 	struct tegra_dc *dc = platform_get_drvdata(pdev);
3251dee8268fSThierry Reding 	int err;
3252dee8268fSThierry Reding 
3253dee8268fSThierry Reding 	err = host1x_client_unregister(&dc->client);
3254dee8268fSThierry Reding 	if (err < 0) {
3255dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
3256dee8268fSThierry Reding 			err);
3257dee8268fSThierry Reding 		return err;
3258dee8268fSThierry Reding 	}
3259dee8268fSThierry Reding 
326059d29c0eSThierry Reding 	err = tegra_dc_rgb_remove(dc);
326159d29c0eSThierry Reding 	if (err < 0) {
326259d29c0eSThierry Reding 		dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
326359d29c0eSThierry Reding 		return err;
326459d29c0eSThierry Reding 	}
326559d29c0eSThierry Reding 
326633a8eb8dSThierry Reding 	pm_runtime_disable(&pdev->dev);
326733a8eb8dSThierry Reding 
326833a8eb8dSThierry Reding 	return 0;
326933a8eb8dSThierry Reding }
327033a8eb8dSThierry Reding 
3271dee8268fSThierry Reding struct platform_driver tegra_dc_driver = {
3272dee8268fSThierry Reding 	.driver = {
3273dee8268fSThierry Reding 		.name = "tegra-dc",
3274dee8268fSThierry Reding 		.of_match_table = tegra_dc_of_match,
3275dee8268fSThierry Reding 	},
3276dee8268fSThierry Reding 	.probe = tegra_dc_probe,
3277dee8268fSThierry Reding 	.remove = tegra_dc_remove,
3278dee8268fSThierry Reding };
3279