xref: /openbmc/linux/drivers/gpu/drm/tegra/dc.c (revision 30d92e0fcd902bb8b1e465f5a0c7ea9596a9e381)
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>
107ac1a36aSRobin Murphy #include <linux/dma-mapping.h>
11df06b759SThierry Reding #include <linux/iommu.h>
1204d5d5dfSDmitry Osipenko #include <linux/interconnect.h>
13eb1df694SSam Ravnborg #include <linux/module.h>
14b9ff7aeaSThierry Reding #include <linux/of_device.h>
154ce3048cSDmitry Osipenko #include <linux/pm_domain.h>
164ce3048cSDmitry Osipenko #include <linux/pm_opp.h>
1733a8eb8dSThierry Reding #include <linux/pm_runtime.h>
18ca48080aSStephen Warren #include <linux/reset.h>
19dee8268fSThierry Reding 
204ce3048cSDmitry Osipenko #include <soc/tegra/common.h>
219c012700SThierry Reding #include <soc/tegra/pmc.h>
229c012700SThierry Reding 
23eb1df694SSam Ravnborg #include <drm/drm_atomic.h>
24eb1df694SSam Ravnborg #include <drm/drm_atomic_helper.h>
2590bb087fSVille Syrjälä #include <drm/drm_blend.h>
26eb1df694SSam Ravnborg #include <drm/drm_debugfs.h>
27eb1df694SSam Ravnborg #include <drm/drm_fourcc.h>
28720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
29eb1df694SSam Ravnborg #include <drm/drm_vblank.h>
30eb1df694SSam Ravnborg 
31dee8268fSThierry Reding #include "dc.h"
32dee8268fSThierry Reding #include "drm.h"
33dee8268fSThierry Reding #include "gem.h"
3447307954SThierry Reding #include "hub.h"
355acd3514SThierry Reding #include "plane.h"
36dee8268fSThierry Reding 
37b7e0b04aSMaarten Lankhorst static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
38b7e0b04aSMaarten Lankhorst 					    struct drm_crtc_state *state);
39b7e0b04aSMaarten Lankhorst 
40791ddb1eSThierry Reding static void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
41791ddb1eSThierry Reding {
42791ddb1eSThierry Reding 	stats->frames = 0;
43791ddb1eSThierry Reding 	stats->vblank = 0;
44791ddb1eSThierry Reding 	stats->underflow = 0;
45791ddb1eSThierry Reding 	stats->overflow = 0;
46791ddb1eSThierry Reding }
47791ddb1eSThierry Reding 
481087fac1SThierry Reding /* Reads the active copy of a register. */
4986df256fSThierry Reding static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
5086df256fSThierry Reding {
5186df256fSThierry Reding 	u32 value;
5286df256fSThierry Reding 
5386df256fSThierry Reding 	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
5486df256fSThierry Reding 	value = tegra_dc_readl(dc, offset);
5586df256fSThierry Reding 	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
5686df256fSThierry Reding 
5786df256fSThierry Reding 	return value;
5886df256fSThierry Reding }
5986df256fSThierry Reding 
601087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
611087fac1SThierry Reding 					      unsigned int offset)
621087fac1SThierry Reding {
631087fac1SThierry Reding 	if (offset >= 0x500 && offset <= 0x638) {
641087fac1SThierry Reding 		offset = 0x000 + (offset - 0x500);
651087fac1SThierry Reding 		return plane->offset + offset;
661087fac1SThierry Reding 	}
671087fac1SThierry Reding 
681087fac1SThierry Reding 	if (offset >= 0x700 && offset <= 0x719) {
691087fac1SThierry Reding 		offset = 0x180 + (offset - 0x700);
701087fac1SThierry Reding 		return plane->offset + offset;
711087fac1SThierry Reding 	}
721087fac1SThierry Reding 
731087fac1SThierry Reding 	if (offset >= 0x800 && offset <= 0x839) {
741087fac1SThierry Reding 		offset = 0x1c0 + (offset - 0x800);
751087fac1SThierry Reding 		return plane->offset + offset;
761087fac1SThierry Reding 	}
771087fac1SThierry Reding 
781087fac1SThierry Reding 	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
791087fac1SThierry Reding 
801087fac1SThierry Reding 	return plane->offset + offset;
811087fac1SThierry Reding }
821087fac1SThierry Reding 
831087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane,
841087fac1SThierry Reding 				    unsigned int offset)
851087fac1SThierry Reding {
861087fac1SThierry Reding 	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
871087fac1SThierry Reding }
881087fac1SThierry Reding 
891087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
901087fac1SThierry Reding 				      unsigned int offset)
911087fac1SThierry Reding {
921087fac1SThierry Reding 	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
931087fac1SThierry Reding }
941087fac1SThierry Reding 
95c57997bcSThierry Reding bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
96c57997bcSThierry Reding {
97c57997bcSThierry Reding 	struct device_node *np = dc->dev->of_node;
98c57997bcSThierry Reding 	struct of_phandle_iterator it;
99c57997bcSThierry Reding 	int err;
100c57997bcSThierry Reding 
101c57997bcSThierry Reding 	of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
102c57997bcSThierry Reding 		if (it.node == dev->of_node)
103c57997bcSThierry Reding 			return true;
104c57997bcSThierry Reding 
105c57997bcSThierry Reding 	return false;
106c57997bcSThierry Reding }
107c57997bcSThierry Reding 
10886df256fSThierry Reding /*
109d700ba7aSThierry Reding  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
110d700ba7aSThierry Reding  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
111d700ba7aSThierry Reding  * Latching happens mmediately if the display controller is in STOP mode or
112d700ba7aSThierry Reding  * on the next frame boundary otherwise.
113d700ba7aSThierry Reding  *
114d700ba7aSThierry Reding  * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The
115d700ba7aSThierry Reding  * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits
116d700ba7aSThierry Reding  * are written. When the *_ACT_REQ bits are written, the ARM copy is latched
117d700ba7aSThierry Reding  * into the ACTIVE copy, either immediately if the display controller is in
118d700ba7aSThierry Reding  * STOP mode, or at the next frame boundary otherwise.
119d700ba7aSThierry Reding  */
12062b9e063SThierry Reding void tegra_dc_commit(struct tegra_dc *dc)
121205d48edSThierry Reding {
122205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
123205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
124205d48edSThierry Reding }
125205d48edSThierry Reding 
12610288eeaSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
12710288eeaSThierry Reding 				  unsigned int bpp)
12810288eeaSThierry Reding {
12910288eeaSThierry Reding 	fixed20_12 outf = dfixed_init(out);
13010288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
13110288eeaSThierry Reding 	u32 dda_inc;
13210288eeaSThierry Reding 	int max;
13310288eeaSThierry Reding 
13410288eeaSThierry Reding 	if (v)
13510288eeaSThierry Reding 		max = 15;
13610288eeaSThierry Reding 	else {
13710288eeaSThierry Reding 		switch (bpp) {
13810288eeaSThierry Reding 		case 2:
13910288eeaSThierry Reding 			max = 8;
14010288eeaSThierry Reding 			break;
14110288eeaSThierry Reding 
14210288eeaSThierry Reding 		default:
14310288eeaSThierry Reding 			WARN_ON_ONCE(1);
144df561f66SGustavo A. R. Silva 			fallthrough;
14510288eeaSThierry Reding 		case 4:
14610288eeaSThierry Reding 			max = 4;
14710288eeaSThierry Reding 			break;
14810288eeaSThierry Reding 		}
14910288eeaSThierry Reding 	}
15010288eeaSThierry Reding 
15110288eeaSThierry Reding 	outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
15210288eeaSThierry Reding 	inf.full -= dfixed_const(1);
15310288eeaSThierry Reding 
15410288eeaSThierry Reding 	dda_inc = dfixed_div(inf, outf);
15510288eeaSThierry Reding 	dda_inc = min_t(u32, dda_inc, dfixed_const(max));
15610288eeaSThierry Reding 
15710288eeaSThierry Reding 	return dda_inc;
15810288eeaSThierry Reding }
15910288eeaSThierry Reding 
16010288eeaSThierry Reding static inline u32 compute_initial_dda(unsigned int in)
16110288eeaSThierry Reding {
16210288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
16310288eeaSThierry Reding 	return dfixed_frac(inf);
16410288eeaSThierry Reding }
16510288eeaSThierry Reding 
166ab7d3f58SThierry Reding static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
167ab7d3f58SThierry Reding {
168ebae8d07SThierry Reding 	u32 background[3] = {
169ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
170ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
171ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
172ebae8d07SThierry Reding 	};
173ebae8d07SThierry Reding 	u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
174ebae8d07SThierry Reding 			 BLEND_COLOR_KEY_NONE;
175ebae8d07SThierry Reding 	u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
176ebae8d07SThierry Reding 	struct tegra_plane_state *state;
1773dae08bcSDmitry Osipenko 	u32 blending[2];
178ebae8d07SThierry Reding 	unsigned int i;
179ebae8d07SThierry Reding 
1803dae08bcSDmitry Osipenko 	/* disable blending for non-overlapping case */
181ebae8d07SThierry Reding 	tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
182ebae8d07SThierry Reding 	tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
183ab7d3f58SThierry Reding 
1843dae08bcSDmitry Osipenko 	state = to_tegra_plane_state(plane->base.state);
1853dae08bcSDmitry Osipenko 
1863dae08bcSDmitry Osipenko 	if (state->opaque) {
1873dae08bcSDmitry Osipenko 		/*
1883dae08bcSDmitry Osipenko 		 * Since custom fix-weight blending isn't utilized and weight
1893dae08bcSDmitry Osipenko 		 * of top window is set to max, we can enforce dependent
1903dae08bcSDmitry Osipenko 		 * blending which in this case results in transparent bottom
1913dae08bcSDmitry Osipenko 		 * window if top window is opaque and if top window enables
1923dae08bcSDmitry Osipenko 		 * alpha blending, then bottom window is getting alpha value
1933dae08bcSDmitry Osipenko 		 * of 1 minus the sum of alpha components of the overlapping
1943dae08bcSDmitry Osipenko 		 * plane.
1953dae08bcSDmitry Osipenko 		 */
1963dae08bcSDmitry Osipenko 		background[0] |= BLEND_CONTROL_DEPENDENT;
1973dae08bcSDmitry Osipenko 		background[1] |= BLEND_CONTROL_DEPENDENT;
1983dae08bcSDmitry Osipenko 
1993dae08bcSDmitry Osipenko 		/*
2003dae08bcSDmitry Osipenko 		 * The region where three windows overlap is the intersection
2013dae08bcSDmitry Osipenko 		 * of the two regions where two windows overlap. It contributes
2023dae08bcSDmitry Osipenko 		 * to the area if all of the windows on top of it have an alpha
2033dae08bcSDmitry Osipenko 		 * component.
2043dae08bcSDmitry Osipenko 		 */
2053dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2063dae08bcSDmitry Osipenko 		case 0:
2073dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2083dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2093dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2103dae08bcSDmitry Osipenko 			break;
2113dae08bcSDmitry Osipenko 
2123dae08bcSDmitry Osipenko 		case 1:
2133dae08bcSDmitry Osipenko 			background[2] |= BLEND_CONTROL_DEPENDENT;
2143dae08bcSDmitry Osipenko 			break;
2153dae08bcSDmitry Osipenko 		}
2163dae08bcSDmitry Osipenko 	} else {
2173dae08bcSDmitry Osipenko 		/*
2183dae08bcSDmitry Osipenko 		 * Enable alpha blending if pixel format has an alpha
2193dae08bcSDmitry Osipenko 		 * component.
2203dae08bcSDmitry Osipenko 		 */
2213dae08bcSDmitry Osipenko 		foreground |= BLEND_CONTROL_ALPHA;
2223dae08bcSDmitry Osipenko 
2233dae08bcSDmitry Osipenko 		/*
2243dae08bcSDmitry Osipenko 		 * If any of the windows on top of this window is opaque, it
2253dae08bcSDmitry Osipenko 		 * will completely conceal this window within that area. If
2263dae08bcSDmitry Osipenko 		 * top window has an alpha component, it is blended over the
2273dae08bcSDmitry Osipenko 		 * bottom window.
2283dae08bcSDmitry Osipenko 		 */
2293dae08bcSDmitry Osipenko 		for (i = 0; i < 2; i++) {
2303dae08bcSDmitry Osipenko 			if (state->blending[i].alpha &&
2313dae08bcSDmitry Osipenko 			    state->blending[i].top)
2323dae08bcSDmitry Osipenko 				background[i] |= BLEND_CONTROL_DEPENDENT;
2333dae08bcSDmitry Osipenko 		}
2343dae08bcSDmitry Osipenko 
2353dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2363dae08bcSDmitry Osipenko 		case 0:
2373dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2383dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2393dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2403dae08bcSDmitry Osipenko 			break;
2413dae08bcSDmitry Osipenko 
2423dae08bcSDmitry Osipenko 		case 1:
2433dae08bcSDmitry Osipenko 			/*
2443dae08bcSDmitry Osipenko 			 * When both middle and topmost windows have an alpha,
2453dae08bcSDmitry Osipenko 			 * these windows a mixed together and then the result
2463dae08bcSDmitry Osipenko 			 * is blended over the bottom window.
2473dae08bcSDmitry Osipenko 			 */
2483dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2493dae08bcSDmitry Osipenko 			    state->blending[0].top)
2503dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2513dae08bcSDmitry Osipenko 
2523dae08bcSDmitry Osipenko 			if (state->blending[1].alpha &&
2533dae08bcSDmitry Osipenko 			    state->blending[1].top)
2543dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2553dae08bcSDmitry Osipenko 			break;
2563dae08bcSDmitry Osipenko 		}
2573dae08bcSDmitry Osipenko 	}
2583dae08bcSDmitry Osipenko 
2593dae08bcSDmitry Osipenko 	switch (state->base.normalized_zpos) {
260ab7d3f58SThierry Reding 	case 0:
261ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
262ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
263ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
264ab7d3f58SThierry Reding 		break;
265ab7d3f58SThierry Reding 
266ab7d3f58SThierry Reding 	case 1:
2673dae08bcSDmitry Osipenko 		/*
2683dae08bcSDmitry Osipenko 		 * If window B / C is topmost, then X / Y registers are
2693dae08bcSDmitry Osipenko 		 * matching the order of blending[...] state indices,
2703dae08bcSDmitry Osipenko 		 * otherwise a swap is required.
2713dae08bcSDmitry Osipenko 		 */
2723dae08bcSDmitry Osipenko 		if (!state->blending[0].top && state->blending[1].top) {
2733dae08bcSDmitry Osipenko 			blending[0] = foreground;
2743dae08bcSDmitry Osipenko 			blending[1] = background[1];
2753dae08bcSDmitry Osipenko 		} else {
2763dae08bcSDmitry Osipenko 			blending[0] = background[0];
2773dae08bcSDmitry Osipenko 			blending[1] = foreground;
2783dae08bcSDmitry Osipenko 		}
2793dae08bcSDmitry Osipenko 
2803dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
2813dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
282ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
283ab7d3f58SThierry Reding 		break;
284ab7d3f58SThierry Reding 
285ab7d3f58SThierry Reding 	case 2:
286ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
287ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
288ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
289ab7d3f58SThierry Reding 		break;
290ab7d3f58SThierry Reding 	}
291ab7d3f58SThierry Reding }
292ab7d3f58SThierry Reding 
293ab7d3f58SThierry Reding static void tegra_plane_setup_blending(struct tegra_plane *plane,
294ab7d3f58SThierry Reding 				       const struct tegra_dc_window *window)
295ab7d3f58SThierry Reding {
296ab7d3f58SThierry Reding 	u32 value;
297ab7d3f58SThierry Reding 
298ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
299ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
300ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
301ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT);
302ab7d3f58SThierry Reding 
303ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
304ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
305ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
306ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT);
307ab7d3f58SThierry Reding 
308ab7d3f58SThierry Reding 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos);
309ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
310ab7d3f58SThierry Reding }
311ab7d3f58SThierry Reding 
312acc6a3a9SDmitry Osipenko static bool
313acc6a3a9SDmitry Osipenko tegra_plane_use_horizontal_filtering(struct tegra_plane *plane,
314acc6a3a9SDmitry Osipenko 				     const struct tegra_dc_window *window)
315acc6a3a9SDmitry Osipenko {
316acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
317acc6a3a9SDmitry Osipenko 
318acc6a3a9SDmitry Osipenko 	if (window->src.w == window->dst.w)
319acc6a3a9SDmitry Osipenko 		return false;
320acc6a3a9SDmitry Osipenko 
321acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
322acc6a3a9SDmitry Osipenko 		return false;
323acc6a3a9SDmitry Osipenko 
324acc6a3a9SDmitry Osipenko 	return true;
325acc6a3a9SDmitry Osipenko }
326acc6a3a9SDmitry Osipenko 
327acc6a3a9SDmitry Osipenko static bool
328acc6a3a9SDmitry Osipenko tegra_plane_use_vertical_filtering(struct tegra_plane *plane,
329acc6a3a9SDmitry Osipenko 				   const struct tegra_dc_window *window)
330acc6a3a9SDmitry Osipenko {
331acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
332acc6a3a9SDmitry Osipenko 
333acc6a3a9SDmitry Osipenko 	if (window->src.h == window->dst.h)
334acc6a3a9SDmitry Osipenko 		return false;
335acc6a3a9SDmitry Osipenko 
336acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
337acc6a3a9SDmitry Osipenko 		return false;
338acc6a3a9SDmitry Osipenko 
339acc6a3a9SDmitry Osipenko 	if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter)
340acc6a3a9SDmitry Osipenko 		return false;
341acc6a3a9SDmitry Osipenko 
342acc6a3a9SDmitry Osipenko 	return true;
343acc6a3a9SDmitry Osipenko }
344acc6a3a9SDmitry Osipenko 
3451087fac1SThierry Reding static void tegra_dc_setup_window(struct tegra_plane *plane,
34610288eeaSThierry Reding 				  const struct tegra_dc_window *window)
34710288eeaSThierry Reding {
34810288eeaSThierry Reding 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
3491087fac1SThierry Reding 	struct tegra_dc *dc = plane->dc;
350a649b133SThierry Reding 	unsigned int planes;
3511087fac1SThierry Reding 	u32 value;
352a649b133SThierry Reding 	bool yuv;
35310288eeaSThierry Reding 
35410288eeaSThierry Reding 	/*
35510288eeaSThierry Reding 	 * For YUV planar modes, the number of bytes per pixel takes into
35610288eeaSThierry Reding 	 * account only the luma component and therefore is 1.
35710288eeaSThierry Reding 	 */
358a649b133SThierry Reding 	yuv = tegra_plane_format_is_yuv(window->format, &planes, NULL);
35910288eeaSThierry Reding 	if (!yuv)
36010288eeaSThierry Reding 		bpp = window->bits_per_pixel / 8;
36110288eeaSThierry Reding 	else
362a649b133SThierry Reding 		bpp = (planes > 1) ? 1 : 2;
36310288eeaSThierry Reding 
3641087fac1SThierry Reding 	tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH);
3651087fac1SThierry Reding 	tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP);
36610288eeaSThierry Reding 
36710288eeaSThierry Reding 	value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
3681087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_POSITION);
36910288eeaSThierry Reding 
37010288eeaSThierry Reding 	value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
3711087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_SIZE);
37210288eeaSThierry Reding 
37310288eeaSThierry Reding 	h_offset = window->src.x * bpp;
37410288eeaSThierry Reding 	v_offset = window->src.y;
37510288eeaSThierry Reding 	h_size = window->src.w * bpp;
37610288eeaSThierry Reding 	v_size = window->src.h;
37710288eeaSThierry Reding 
378cd740777SDmitry Osipenko 	if (window->reflect_x)
379cd740777SDmitry Osipenko 		h_offset += (window->src.w - 1) * bpp;
380cd740777SDmitry Osipenko 
381cd740777SDmitry Osipenko 	if (window->reflect_y)
382cd740777SDmitry Osipenko 		v_offset += window->src.h - 1;
383cd740777SDmitry Osipenko 
38410288eeaSThierry Reding 	value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
3851087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE);
38610288eeaSThierry Reding 
38710288eeaSThierry Reding 	/*
38810288eeaSThierry Reding 	 * For DDA computations the number of bytes per pixel for YUV planar
38910288eeaSThierry Reding 	 * modes needs to take into account all Y, U and V components.
39010288eeaSThierry Reding 	 */
391a649b133SThierry Reding 	if (yuv && planes > 1)
39210288eeaSThierry Reding 		bpp = 2;
39310288eeaSThierry Reding 
39410288eeaSThierry Reding 	h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
39510288eeaSThierry Reding 	v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
39610288eeaSThierry Reding 
39710288eeaSThierry Reding 	value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
3981087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_DDA_INC);
39910288eeaSThierry Reding 
40010288eeaSThierry Reding 	h_dda = compute_initial_dda(window->src.x);
40110288eeaSThierry Reding 	v_dda = compute_initial_dda(window->src.y);
40210288eeaSThierry Reding 
4031087fac1SThierry Reding 	tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA);
4041087fac1SThierry Reding 	tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA);
40510288eeaSThierry Reding 
4061087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE);
4071087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE);
40810288eeaSThierry Reding 
4091087fac1SThierry Reding 	tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR);
41010288eeaSThierry Reding 
411a649b133SThierry Reding 	if (yuv && planes > 1) {
4121087fac1SThierry Reding 		tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U);
413a649b133SThierry Reding 
414a649b133SThierry Reding 		if (planes > 2)
4151087fac1SThierry Reding 			tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V);
416a649b133SThierry Reding 
41710288eeaSThierry Reding 		value = window->stride[1] << 16 | window->stride[0];
4181087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE);
41910288eeaSThierry Reding 	} else {
4201087fac1SThierry Reding 		tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE);
42110288eeaSThierry Reding 	}
42210288eeaSThierry Reding 
4231087fac1SThierry Reding 	tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET);
4241087fac1SThierry Reding 	tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET);
42510288eeaSThierry Reding 
426c134f019SThierry Reding 	if (dc->soc->supports_block_linear) {
427c134f019SThierry Reding 		unsigned long height = window->tiling.value;
428c134f019SThierry Reding 
429c134f019SThierry Reding 		switch (window->tiling.mode) {
430c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
431c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_PITCH;
432c134f019SThierry Reding 			break;
433c134f019SThierry Reding 
434c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
435c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_TILED;
436c134f019SThierry Reding 			break;
437c134f019SThierry Reding 
438c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
439c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
440c134f019SThierry Reding 				DC_WINBUF_SURFACE_KIND_BLOCK;
441c134f019SThierry Reding 			break;
442c134f019SThierry Reding 		}
443c134f019SThierry Reding 
4441087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND);
44510288eeaSThierry Reding 	} else {
446c134f019SThierry Reding 		switch (window->tiling.mode) {
447c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
44810288eeaSThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
44910288eeaSThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
450c134f019SThierry Reding 			break;
451c134f019SThierry Reding 
452c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
453c134f019SThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
454c134f019SThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_TILE;
455c134f019SThierry Reding 			break;
456c134f019SThierry Reding 
457c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
4584aa3df71SThierry Reding 			/*
4594aa3df71SThierry Reding 			 * No need to handle this here because ->atomic_check
4604aa3df71SThierry Reding 			 * will already have filtered it out.
4614aa3df71SThierry Reding 			 */
4624aa3df71SThierry Reding 			break;
46310288eeaSThierry Reding 		}
46410288eeaSThierry Reding 
4651087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE);
466c134f019SThierry Reding 	}
46710288eeaSThierry Reding 
46810288eeaSThierry Reding 	value = WIN_ENABLE;
46910288eeaSThierry Reding 
47010288eeaSThierry Reding 	if (yuv) {
47110288eeaSThierry Reding 		/* setup default colorspace conversion coefficients */
4721087fac1SThierry Reding 		tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF);
4731087fac1SThierry Reding 		tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB);
4741087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR);
4751087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR);
4761087fac1SThierry Reding 		tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG);
4771087fac1SThierry Reding 		tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG);
4781087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB);
4791087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB);
48010288eeaSThierry Reding 
48110288eeaSThierry Reding 		value |= CSC_ENABLE;
48210288eeaSThierry Reding 	} else if (window->bits_per_pixel < 24) {
48310288eeaSThierry Reding 		value |= COLOR_EXPAND;
48410288eeaSThierry Reding 	}
48510288eeaSThierry Reding 
486cd740777SDmitry Osipenko 	if (window->reflect_x)
487cd740777SDmitry Osipenko 		value |= H_DIRECTION;
488cd740777SDmitry Osipenko 
489e9e476f7SDmitry Osipenko 	if (window->reflect_y)
49010288eeaSThierry Reding 		value |= V_DIRECTION;
49110288eeaSThierry Reding 
492acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_horizontal_filtering(plane, window)) {
493acc6a3a9SDmitry Osipenko 		/*
494acc6a3a9SDmitry Osipenko 		 * Enable horizontal 6-tap filter and set filtering
495acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
496acc6a3a9SDmitry Osipenko 		 */
497acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0));
498acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1));
499acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2));
500acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3));
501acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4));
502acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5));
503acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6));
504acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7));
505acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8));
506acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9));
507acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10));
508acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11));
509acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12));
510acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13));
511acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14));
512acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15));
513acc6a3a9SDmitry Osipenko 
514acc6a3a9SDmitry Osipenko 		value |= H_FILTER;
515acc6a3a9SDmitry Osipenko 	}
516acc6a3a9SDmitry Osipenko 
517acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_vertical_filtering(plane, window)) {
518acc6a3a9SDmitry Osipenko 		unsigned int i, k;
519acc6a3a9SDmitry Osipenko 
520acc6a3a9SDmitry Osipenko 		/*
521acc6a3a9SDmitry Osipenko 		 * Enable vertical 2-tap filter and set filtering
522acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
523acc6a3a9SDmitry Osipenko 		 */
524acc6a3a9SDmitry Osipenko 		for (i = 0, k = 128; i < 16; i++, k -= 8)
525acc6a3a9SDmitry Osipenko 			tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i));
526acc6a3a9SDmitry Osipenko 
527acc6a3a9SDmitry Osipenko 		value |= V_FILTER;
528acc6a3a9SDmitry Osipenko 	}
529acc6a3a9SDmitry Osipenko 
5301087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
53110288eeaSThierry Reding 
532a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending)
533ab7d3f58SThierry Reding 		tegra_plane_setup_blending_legacy(plane);
534a43d0a00SDmitry Osipenko 	else
535a43d0a00SDmitry Osipenko 		tegra_plane_setup_blending(plane, window);
536c7679306SThierry Reding }
537c7679306SThierry Reding 
538511c7023SThierry Reding static const u32 tegra20_primary_formats[] = {
539511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
540511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
541c7679306SThierry Reding 	DRM_FORMAT_RGB565,
542511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
543511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
544511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
545ebae8d07SThierry Reding 	/* non-native formats */
546ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
547ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
548ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
549ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
550511c7023SThierry Reding };
551511c7023SThierry Reding 
552e90124cbSThierry Reding static const u64 tegra20_modifiers[] = {
553e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
554e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED,
555e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
556e90124cbSThierry Reding };
557e90124cbSThierry Reding 
558511c7023SThierry Reding static const u32 tegra114_primary_formats[] = {
559511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
560511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
561511c7023SThierry Reding 	DRM_FORMAT_RGB565,
562511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
563511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
564511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
565511c7023SThierry Reding 	/* new on Tegra114 */
566511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
567511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
568511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
569511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
570511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
571511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
572511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
573511c7023SThierry Reding 	DRM_FORMAT_BGR565,
574511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
575511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
576511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
577511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
578511c7023SThierry Reding };
579511c7023SThierry Reding 
580511c7023SThierry Reding static const u32 tegra124_primary_formats[] = {
581511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
582511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
583511c7023SThierry Reding 	DRM_FORMAT_RGB565,
584511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
585511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
586511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
587511c7023SThierry Reding 	/* new on Tegra114 */
588511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
589511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
590511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
591511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
592511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
593511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
594511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
595511c7023SThierry Reding 	DRM_FORMAT_BGR565,
596511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
597511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
598511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
599511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
600511c7023SThierry Reding 	/* new on Tegra124 */
601511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
602511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
603c7679306SThierry Reding };
604c7679306SThierry Reding 
605e90124cbSThierry Reding static const u64 tegra124_modifiers[] = {
606e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
607e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
608e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
609e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
610e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
611e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
612e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
613e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
614e90124cbSThierry Reding };
615e90124cbSThierry Reding 
6164aa3df71SThierry Reding static int tegra_plane_atomic_check(struct drm_plane *plane,
6177c11b99aSMaxime Ripard 				    struct drm_atomic_state *state)
6184aa3df71SThierry Reding {
6197c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
6207c11b99aSMaxime Ripard 										 plane);
621ba5c1649SMaxime Ripard 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
622cd740777SDmitry Osipenko 	unsigned int supported_rotation = DRM_MODE_ROTATE_0 |
623cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_X |
624cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_Y;
625ba5c1649SMaxime Ripard 	unsigned int rotation = new_plane_state->rotation;
6268f604f8cSThierry Reding 	struct tegra_bo_tiling *tiling = &plane_state->tiling;
62747802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
628ba5c1649SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
629c7679306SThierry Reding 	int err;
630c7679306SThierry Reding 
63104d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
63204d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
63304d5d5dfSDmitry Osipenko 
6344aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
63504d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
63604d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
6374aa3df71SThierry Reding 		return 0;
63804d5d5dfSDmitry Osipenko 	}
6394aa3df71SThierry Reding 
640ba5c1649SMaxime Ripard 	err = tegra_plane_format(new_plane_state->fb->format->format,
6413dae08bcSDmitry Osipenko 				 &plane_state->format,
6428f604f8cSThierry Reding 				 &plane_state->swap);
6434aa3df71SThierry Reding 	if (err < 0)
6444aa3df71SThierry Reding 		return err;
6454aa3df71SThierry Reding 
646ebae8d07SThierry Reding 	/*
647ebae8d07SThierry Reding 	 * Tegra20 and Tegra30 are special cases here because they support
648ebae8d07SThierry Reding 	 * only variants of specific formats with an alpha component, but not
649ebae8d07SThierry Reding 	 * the corresponding opaque formats. However, the opaque formats can
650ebae8d07SThierry Reding 	 * be emulated by disabling alpha blending for the plane.
651ebae8d07SThierry Reding 	 */
652a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending) {
6533dae08bcSDmitry Osipenko 		err = tegra_plane_setup_legacy_state(tegra, plane_state);
654ebae8d07SThierry Reding 		if (err < 0)
655ebae8d07SThierry Reding 			return err;
656ebae8d07SThierry Reding 	}
657ebae8d07SThierry Reding 
658ba5c1649SMaxime Ripard 	err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
6598f604f8cSThierry Reding 	if (err < 0)
6608f604f8cSThierry Reding 		return err;
6618f604f8cSThierry Reding 
6628f604f8cSThierry Reding 	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
6634aa3df71SThierry Reding 	    !dc->soc->supports_block_linear) {
6644aa3df71SThierry Reding 		DRM_ERROR("hardware doesn't support block linear mode\n");
6654aa3df71SThierry Reding 		return -EINVAL;
6664aa3df71SThierry Reding 	}
6674aa3df71SThierry Reding 
668cd740777SDmitry Osipenko 	/*
669cd740777SDmitry Osipenko 	 * Older userspace used custom BO flag in order to specify the Y
670cd740777SDmitry Osipenko 	 * reflection, while modern userspace uses the generic DRM rotation
671cd740777SDmitry Osipenko 	 * property in order to achieve the same result.  The legacy BO flag
672cd740777SDmitry Osipenko 	 * duplicates the DRM rotation property when both are set.
673cd740777SDmitry Osipenko 	 */
674ba5c1649SMaxime Ripard 	if (tegra_fb_is_bottom_up(new_plane_state->fb))
675cd740777SDmitry Osipenko 		rotation |= DRM_MODE_REFLECT_Y;
676cd740777SDmitry Osipenko 
677cd740777SDmitry Osipenko 	rotation = drm_rotation_simplify(rotation, supported_rotation);
678cd740777SDmitry Osipenko 
679cd740777SDmitry Osipenko 	if (rotation & DRM_MODE_REFLECT_X)
680cd740777SDmitry Osipenko 		plane_state->reflect_x = true;
681cd740777SDmitry Osipenko 	else
682cd740777SDmitry Osipenko 		plane_state->reflect_x = false;
683995c5a50SThierry Reding 
684995c5a50SThierry Reding 	if (rotation & DRM_MODE_REFLECT_Y)
685e9e476f7SDmitry Osipenko 		plane_state->reflect_y = true;
686995c5a50SThierry Reding 	else
687e9e476f7SDmitry Osipenko 		plane_state->reflect_y = false;
688995c5a50SThierry Reding 
6894aa3df71SThierry Reding 	/*
6904aa3df71SThierry Reding 	 * Tegra doesn't support different strides for U and V planes so we
6914aa3df71SThierry Reding 	 * error out if the user tries to display a framebuffer with such a
6924aa3df71SThierry Reding 	 * configuration.
6934aa3df71SThierry Reding 	 */
694ba5c1649SMaxime Ripard 	if (new_plane_state->fb->format->num_planes > 2) {
695ba5c1649SMaxime Ripard 		if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
6964aa3df71SThierry Reding 			DRM_ERROR("unsupported UV-plane configuration\n");
6974aa3df71SThierry Reding 			return -EINVAL;
6984aa3df71SThierry Reding 		}
6994aa3df71SThierry Reding 	}
7004aa3df71SThierry Reding 
701ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
70247802b09SThierry Reding 	if (err < 0)
70347802b09SThierry Reding 		return err;
70447802b09SThierry Reding 
7054aa3df71SThierry Reding 	return 0;
7064aa3df71SThierry Reding }
7074aa3df71SThierry Reding 
708a4bfa096SThierry Reding static void tegra_plane_atomic_disable(struct drm_plane *plane,
709977697e2SMaxime Ripard 				       struct drm_atomic_state *state)
71080d3eef1SDmitry Osipenko {
711977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
712977697e2SMaxime Ripard 									   plane);
713a4bfa096SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
71480d3eef1SDmitry Osipenko 	u32 value;
71580d3eef1SDmitry Osipenko 
716a4bfa096SThierry Reding 	/* rien ne va plus */
717a4bfa096SThierry Reding 	if (!old_state || !old_state->crtc)
718a4bfa096SThierry Reding 		return;
719a4bfa096SThierry Reding 
7201087fac1SThierry Reding 	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
72180d3eef1SDmitry Osipenko 	value &= ~WIN_ENABLE;
7221087fac1SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
72380d3eef1SDmitry Osipenko }
72480d3eef1SDmitry Osipenko 
7254aa3df71SThierry Reding static void tegra_plane_atomic_update(struct drm_plane *plane,
726977697e2SMaxime Ripard 				      struct drm_atomic_state *state)
7274aa3df71SThierry Reding {
72837418bf1SMaxime Ripard 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
72937418bf1SMaxime Ripard 									   plane);
73041016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
731e05162c0SMaxime Ripard 	struct drm_framebuffer *fb = new_state->fb;
7324aa3df71SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
7334aa3df71SThierry Reding 	struct tegra_dc_window window;
7344aa3df71SThierry Reding 	unsigned int i;
7354aa3df71SThierry Reding 
7364aa3df71SThierry Reding 	/* rien ne va plus */
737e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
7384aa3df71SThierry Reding 		return;
7394aa3df71SThierry Reding 
740e05162c0SMaxime Ripard 	if (!new_state->visible)
741977697e2SMaxime Ripard 		return tegra_plane_atomic_disable(plane, state);
74280d3eef1SDmitry Osipenko 
743c7679306SThierry Reding 	memset(&window, 0, sizeof(window));
744e05162c0SMaxime Ripard 	window.src.x = new_state->src.x1 >> 16;
745e05162c0SMaxime Ripard 	window.src.y = new_state->src.y1 >> 16;
746e05162c0SMaxime Ripard 	window.src.w = drm_rect_width(&new_state->src) >> 16;
747e05162c0SMaxime Ripard 	window.src.h = drm_rect_height(&new_state->src) >> 16;
748e05162c0SMaxime Ripard 	window.dst.x = new_state->dst.x1;
749e05162c0SMaxime Ripard 	window.dst.y = new_state->dst.y1;
750e05162c0SMaxime Ripard 	window.dst.w = drm_rect_width(&new_state->dst);
751e05162c0SMaxime Ripard 	window.dst.h = drm_rect_height(&new_state->dst);
752272725c7SVille Syrjälä 	window.bits_per_pixel = fb->format->cpp[0] * 8;
75341016fe1SMaxime Ripard 	window.reflect_x = tegra_plane_state->reflect_x;
75441016fe1SMaxime Ripard 	window.reflect_y = tegra_plane_state->reflect_y;
755c7679306SThierry Reding 
7568f604f8cSThierry Reding 	/* copy from state */
757e05162c0SMaxime Ripard 	window.zpos = new_state->normalized_zpos;
75841016fe1SMaxime Ripard 	window.tiling = tegra_plane_state->tiling;
75941016fe1SMaxime Ripard 	window.format = tegra_plane_state->format;
76041016fe1SMaxime Ripard 	window.swap = tegra_plane_state->swap;
761c7679306SThierry Reding 
762bcb0b461SVille Syrjälä 	for (i = 0; i < fb->format->num_planes; i++) {
76341016fe1SMaxime Ripard 		window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i];
76408ee0178SDmitry Osipenko 
76508ee0178SDmitry Osipenko 		/*
76608ee0178SDmitry Osipenko 		 * Tegra uses a shared stride for UV planes. Framebuffers are
76708ee0178SDmitry Osipenko 		 * already checked for this in the tegra_plane_atomic_check()
76808ee0178SDmitry Osipenko 		 * function, so it's safe to ignore the V-plane pitch here.
76908ee0178SDmitry Osipenko 		 */
77008ee0178SDmitry Osipenko 		if (i < 2)
7714aa3df71SThierry Reding 			window.stride[i] = fb->pitches[i];
772c7679306SThierry Reding 	}
773c7679306SThierry Reding 
7741087fac1SThierry Reding 	tegra_dc_setup_window(p, &window);
7754aa3df71SThierry Reding }
7764aa3df71SThierry Reding 
777a4bfa096SThierry Reding static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
7782e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
7792e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
7804aa3df71SThierry Reding 	.atomic_check = tegra_plane_atomic_check,
7814aa3df71SThierry Reding 	.atomic_disable = tegra_plane_atomic_disable,
782a4bfa096SThierry Reding 	.atomic_update = tegra_plane_atomic_update,
783c7679306SThierry Reding };
784c7679306SThierry Reding 
78589f65018SThierry Reding static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm)
786c7679306SThierry Reding {
787518e6227SThierry Reding 	/*
788518e6227SThierry Reding 	 * Ideally this would use drm_crtc_mask(), but that would require the
789518e6227SThierry Reding 	 * CRTC to already be in the mode_config's list of CRTCs. However, it
790518e6227SThierry Reding 	 * will only be added to that list in the drm_crtc_init_with_planes()
791518e6227SThierry Reding 	 * (in tegra_dc_init()), which in turn requires registration of these
792518e6227SThierry Reding 	 * planes. So we have ourselves a nice little chicken and egg problem
793518e6227SThierry Reding 	 * here.
794518e6227SThierry Reding 	 *
795518e6227SThierry Reding 	 * We work around this by manually creating the mask from the number
796518e6227SThierry Reding 	 * of CRTCs that have been registered, and should therefore always be
797518e6227SThierry Reding 	 * the same as drm_crtc_index() after registration.
798518e6227SThierry Reding 	 */
79989f65018SThierry Reding 	return 1 << drm->mode_config.num_crtc;
80089f65018SThierry Reding }
80189f65018SThierry Reding 
80289f65018SThierry Reding static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
80389f65018SThierry Reding 						    struct tegra_dc *dc)
80489f65018SThierry Reding {
80589f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
80647307954SThierry Reding 	enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
807c7679306SThierry Reding 	struct tegra_plane *plane;
808c7679306SThierry Reding 	unsigned int num_formats;
809e90124cbSThierry Reding 	const u64 *modifiers;
810c7679306SThierry Reding 	const u32 *formats;
811c7679306SThierry Reding 	int err;
812c7679306SThierry Reding 
813c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
814c7679306SThierry Reding 	if (!plane)
815c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
816c7679306SThierry Reding 
8171087fac1SThierry Reding 	/* Always use window A as primary window */
8181087fac1SThierry Reding 	plane->offset = 0xa00;
819c4755fb9SThierry Reding 	plane->index = 0;
8201087fac1SThierry Reding 	plane->dc = dc;
8211087fac1SThierry Reding 
8221087fac1SThierry Reding 	num_formats = dc->soc->num_primary_formats;
8231087fac1SThierry Reding 	formats = dc->soc->primary_formats;
824e90124cbSThierry Reding 	modifiers = dc->soc->modifiers;
825c4755fb9SThierry Reding 
82604d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
82704d5d5dfSDmitry Osipenko 	if (err) {
82804d5d5dfSDmitry Osipenko 		kfree(plane);
82904d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
83004d5d5dfSDmitry Osipenko 	}
83104d5d5dfSDmitry Osipenko 
832518e6227SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
833c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
834e90124cbSThierry Reding 				       num_formats, modifiers, type, NULL);
835c7679306SThierry Reding 	if (err < 0) {
836c7679306SThierry Reding 		kfree(plane);
837c7679306SThierry Reding 		return ERR_PTR(err);
838c7679306SThierry Reding 	}
839c7679306SThierry Reding 
840a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
8413dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
842ab7d3f58SThierry Reding 
843995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
844995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
845995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
8464fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
847cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
848995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
849995c5a50SThierry Reding 	if (err < 0)
850995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
851995c5a50SThierry Reding 			err);
852995c5a50SThierry Reding 
853c7679306SThierry Reding 	return &plane->base;
854c7679306SThierry Reding }
855c7679306SThierry Reding 
856d5ec699dSThierry Reding static const u32 tegra_legacy_cursor_plane_formats[] = {
857c7679306SThierry Reding 	DRM_FORMAT_RGBA8888,
858c7679306SThierry Reding };
859c7679306SThierry Reding 
860d5ec699dSThierry Reding static const u32 tegra_cursor_plane_formats[] = {
861d5ec699dSThierry Reding 	DRM_FORMAT_ARGB8888,
862d5ec699dSThierry Reding };
863d5ec699dSThierry Reding 
8644aa3df71SThierry Reding static int tegra_cursor_atomic_check(struct drm_plane *plane,
8657c11b99aSMaxime Ripard 				     struct drm_atomic_state *state)
866c7679306SThierry Reding {
8677c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
8687c11b99aSMaxime Ripard 										 plane);
86904d5d5dfSDmitry Osipenko 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
87047802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
87147802b09SThierry Reding 	int err;
87247802b09SThierry Reding 
87304d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
87404d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
87504d5d5dfSDmitry Osipenko 
8764aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
87704d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
87804d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
8794aa3df71SThierry Reding 		return 0;
88004d5d5dfSDmitry Osipenko 	}
881c7679306SThierry Reding 
882c7679306SThierry Reding 	/* scaling not supported for cursor */
883ba5c1649SMaxime Ripard 	if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) ||
884ba5c1649SMaxime Ripard 	    (new_plane_state->src_h >> 16 != new_plane_state->crtc_h))
885c7679306SThierry Reding 		return -EINVAL;
886c7679306SThierry Reding 
887c7679306SThierry Reding 	/* only square cursors supported */
888ba5c1649SMaxime Ripard 	if (new_plane_state->src_w != new_plane_state->src_h)
889c7679306SThierry Reding 		return -EINVAL;
890c7679306SThierry Reding 
891ba5c1649SMaxime Ripard 	if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 &&
892ba5c1649SMaxime Ripard 	    new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256)
8934aa3df71SThierry Reding 		return -EINVAL;
8944aa3df71SThierry Reding 
895ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
89647802b09SThierry Reding 	if (err < 0)
89747802b09SThierry Reding 		return err;
89847802b09SThierry Reding 
8994aa3df71SThierry Reding 	return 0;
9004aa3df71SThierry Reding }
9014aa3df71SThierry Reding 
902cae7472eSThierry Reding static void __tegra_cursor_atomic_update(struct drm_plane *plane,
903cae7472eSThierry Reding 					 struct drm_plane_state *new_state)
9044aa3df71SThierry Reding {
90541016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
906e05162c0SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
907d5ec699dSThierry Reding 	struct tegra_drm *tegra = plane->dev->dev_private;
908d5ec699dSThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
909d5ec699dSThierry Reding 	u64 dma_mask = *dc->dev->dma_mask;
910d5ec699dSThierry Reding #endif
911d5ec699dSThierry Reding 	unsigned int x, y;
912d5ec699dSThierry Reding 	u32 value = 0;
9134aa3df71SThierry Reding 
9144aa3df71SThierry Reding 	/* rien ne va plus */
915e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
9164aa3df71SThierry Reding 		return;
9174aa3df71SThierry Reding 
918d5ec699dSThierry Reding 	/*
919d5ec699dSThierry Reding 	 * Legacy display supports hardware clipping of the cursor, but
920d5ec699dSThierry Reding 	 * nvdisplay relies on software to clip the cursor to the screen.
921d5ec699dSThierry Reding 	 */
922d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay)
923d5ec699dSThierry Reding 		value |= CURSOR_CLIP_DISPLAY;
924d5ec699dSThierry Reding 
925e05162c0SMaxime Ripard 	switch (new_state->crtc_w) {
926c7679306SThierry Reding 	case 32:
927c7679306SThierry Reding 		value |= CURSOR_SIZE_32x32;
928c7679306SThierry Reding 		break;
929c7679306SThierry Reding 
930c7679306SThierry Reding 	case 64:
931c7679306SThierry Reding 		value |= CURSOR_SIZE_64x64;
932c7679306SThierry Reding 		break;
933c7679306SThierry Reding 
934c7679306SThierry Reding 	case 128:
935c7679306SThierry Reding 		value |= CURSOR_SIZE_128x128;
936c7679306SThierry Reding 		break;
937c7679306SThierry Reding 
938c7679306SThierry Reding 	case 256:
939c7679306SThierry Reding 		value |= CURSOR_SIZE_256x256;
940c7679306SThierry Reding 		break;
941c7679306SThierry Reding 
942c7679306SThierry Reding 	default:
943c52e167bSThierry Reding 		WARN(1, "cursor size %ux%u not supported\n",
944e05162c0SMaxime Ripard 		     new_state->crtc_w, new_state->crtc_h);
9454aa3df71SThierry Reding 		return;
946c7679306SThierry Reding 	}
947c7679306SThierry Reding 
94841016fe1SMaxime Ripard 	value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff;
949c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
950c7679306SThierry Reding 
951c7679306SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
952d5ec699dSThierry Reding 	value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
953c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
954c7679306SThierry Reding #endif
955c7679306SThierry Reding 
956c7679306SThierry Reding 	/* enable cursor and set blend mode */
957c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
958c7679306SThierry Reding 	value |= CURSOR_ENABLE;
959c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
960c7679306SThierry Reding 
961c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
962c7679306SThierry Reding 	value &= ~CURSOR_DST_BLEND_MASK;
963c7679306SThierry Reding 	value &= ~CURSOR_SRC_BLEND_MASK;
964d5ec699dSThierry Reding 
965d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay)
966d5ec699dSThierry Reding 		value &= ~CURSOR_COMPOSITION_MODE_XOR;
967d5ec699dSThierry Reding 	else
968c7679306SThierry Reding 		value |= CURSOR_MODE_NORMAL;
969d5ec699dSThierry Reding 
970c7679306SThierry Reding 	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
971c7679306SThierry Reding 	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
972c7679306SThierry Reding 	value |= CURSOR_ALPHA;
973c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
974c7679306SThierry Reding 
975d5ec699dSThierry Reding 	/* nvdisplay relies on software for clipping */
976d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay) {
977d5ec699dSThierry Reding 		struct drm_rect src;
978d5ec699dSThierry Reding 
979d5ec699dSThierry Reding 		x = new_state->dst.x1;
980d5ec699dSThierry Reding 		y = new_state->dst.y1;
981d5ec699dSThierry Reding 
982d5ec699dSThierry Reding 		drm_rect_fp_to_int(&src, &new_state->src);
983d5ec699dSThierry Reding 
984d5ec699dSThierry Reding 		value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
985d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
986d5ec699dSThierry Reding 
987d5ec699dSThierry Reding 		value = (drm_rect_height(&src) & tegra->vmask) << 16 |
988d5ec699dSThierry Reding 			(drm_rect_width(&src) & tegra->hmask);
989d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
990d5ec699dSThierry Reding 	} else {
991d5ec699dSThierry Reding 		x = new_state->crtc_x;
992d5ec699dSThierry Reding 		y = new_state->crtc_y;
993d5ec699dSThierry Reding 	}
994d5ec699dSThierry Reding 
995c7679306SThierry Reding 	/* position the cursor */
996d5ec699dSThierry Reding 	value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
997c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
998c7679306SThierry Reding }
999c7679306SThierry Reding 
1000cae7472eSThierry Reding static void tegra_cursor_atomic_update(struct drm_plane *plane,
1001cae7472eSThierry Reding 				       struct drm_atomic_state *state)
1002cae7472eSThierry Reding {
1003cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1004cae7472eSThierry Reding 
1005cae7472eSThierry Reding 	__tegra_cursor_atomic_update(plane, new_state);
1006cae7472eSThierry Reding }
1007cae7472eSThierry Reding 
10084aa3df71SThierry Reding static void tegra_cursor_atomic_disable(struct drm_plane *plane,
1009977697e2SMaxime Ripard 					struct drm_atomic_state *state)
1010c7679306SThierry Reding {
1011977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
1012977697e2SMaxime Ripard 									   plane);
10134aa3df71SThierry Reding 	struct tegra_dc *dc;
1014c7679306SThierry Reding 	u32 value;
1015c7679306SThierry Reding 
10164aa3df71SThierry Reding 	/* rien ne va plus */
10174aa3df71SThierry Reding 	if (!old_state || !old_state->crtc)
10184aa3df71SThierry Reding 		return;
10194aa3df71SThierry Reding 
10204aa3df71SThierry Reding 	dc = to_tegra_dc(old_state->crtc);
1021c7679306SThierry Reding 
1022c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
1023c7679306SThierry Reding 	value &= ~CURSOR_ENABLE;
1024c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
1025c7679306SThierry Reding }
1026c7679306SThierry Reding 
1027cae7472eSThierry Reding static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state)
1028cae7472eSThierry Reding {
1029cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1030cae7472eSThierry Reding 	struct drm_crtc_state *crtc_state;
1031cae7472eSThierry Reding 	int min_scale, max_scale;
1032cae7472eSThierry Reding 	int err;
1033cae7472eSThierry Reding 
1034cae7472eSThierry Reding 	crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
1035cae7472eSThierry Reding 	if (WARN_ON(!crtc_state))
1036cae7472eSThierry Reding 		return -EINVAL;
1037cae7472eSThierry Reding 
1038cae7472eSThierry Reding 	if (!crtc_state->active)
1039cae7472eSThierry Reding 		return -EINVAL;
1040cae7472eSThierry Reding 
1041cae7472eSThierry Reding 	if (plane->state->crtc != new_state->crtc ||
1042cae7472eSThierry Reding 	    plane->state->src_w != new_state->src_w ||
1043cae7472eSThierry Reding 	    plane->state->src_h != new_state->src_h ||
1044cae7472eSThierry Reding 	    plane->state->crtc_w != new_state->crtc_w ||
1045cae7472eSThierry Reding 	    plane->state->crtc_h != new_state->crtc_h ||
1046cae7472eSThierry Reding 	    plane->state->fb != new_state->fb ||
1047cae7472eSThierry Reding 	    plane->state->fb == NULL)
1048cae7472eSThierry Reding 		return -EINVAL;
1049cae7472eSThierry Reding 
1050cae7472eSThierry Reding 	min_scale = (1 << 16) / 8;
1051cae7472eSThierry Reding 	max_scale = (8 << 16) / 1;
1052cae7472eSThierry Reding 
1053cae7472eSThierry Reding 	err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale,
1054cae7472eSThierry Reding 						  true, true);
1055cae7472eSThierry Reding 	if (err < 0)
1056cae7472eSThierry Reding 		return err;
1057cae7472eSThierry Reding 
1058cae7472eSThierry Reding 	if (new_state->visible != plane->state->visible)
1059cae7472eSThierry Reding 		return -EINVAL;
1060cae7472eSThierry Reding 
1061cae7472eSThierry Reding 	return 0;
1062cae7472eSThierry Reding }
1063cae7472eSThierry Reding 
1064cae7472eSThierry Reding static void tegra_cursor_atomic_async_update(struct drm_plane *plane,
1065cae7472eSThierry Reding 					     struct drm_atomic_state *state)
1066cae7472eSThierry Reding {
1067cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1068cae7472eSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
1069cae7472eSThierry Reding 
1070cae7472eSThierry Reding 	plane->state->src_x = new_state->src_x;
1071cae7472eSThierry Reding 	plane->state->src_y = new_state->src_y;
1072cae7472eSThierry Reding 	plane->state->crtc_x = new_state->crtc_x;
1073cae7472eSThierry Reding 	plane->state->crtc_y = new_state->crtc_y;
1074cae7472eSThierry Reding 
1075cae7472eSThierry Reding 	if (new_state->visible) {
1076cae7472eSThierry Reding 		struct tegra_plane *p = to_tegra_plane(plane);
1077cae7472eSThierry Reding 		u32 value;
1078cae7472eSThierry Reding 
1079cae7472eSThierry Reding 		__tegra_cursor_atomic_update(plane, new_state);
1080cae7472eSThierry Reding 
1081cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE;
1082cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1083cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1084cae7472eSThierry Reding 
1085cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ;
1086cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1087cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1088cae7472eSThierry Reding 	}
1089cae7472eSThierry Reding }
1090cae7472eSThierry Reding 
10914aa3df71SThierry Reding static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
10922e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
10932e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
10944aa3df71SThierry Reding 	.atomic_check = tegra_cursor_atomic_check,
10954aa3df71SThierry Reding 	.atomic_update = tegra_cursor_atomic_update,
10964aa3df71SThierry Reding 	.atomic_disable = tegra_cursor_atomic_disable,
1097cae7472eSThierry Reding 	.atomic_async_check = tegra_cursor_atomic_async_check,
1098cae7472eSThierry Reding 	.atomic_async_update = tegra_cursor_atomic_async_update,
1099c7679306SThierry Reding };
1100c7679306SThierry Reding 
1101be4306adSDaniel Vetter static const uint64_t linear_modifiers[] = {
1102be4306adSDaniel Vetter 	DRM_FORMAT_MOD_LINEAR,
1103be4306adSDaniel Vetter 	DRM_FORMAT_MOD_INVALID
1104be4306adSDaniel Vetter };
1105be4306adSDaniel Vetter 
1106c7679306SThierry Reding static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
1107c7679306SThierry Reding 						      struct tegra_dc *dc)
1108c7679306SThierry Reding {
110989f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1110c7679306SThierry Reding 	struct tegra_plane *plane;
1111c7679306SThierry Reding 	unsigned int num_formats;
1112c7679306SThierry Reding 	const u32 *formats;
1113c7679306SThierry Reding 	int err;
1114c7679306SThierry Reding 
1115c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1116c7679306SThierry Reding 	if (!plane)
1117c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1118c7679306SThierry Reding 
111947802b09SThierry Reding 	/*
1120a1df3b24SThierry Reding 	 * This index is kind of fake. The cursor isn't a regular plane, but
1121a1df3b24SThierry Reding 	 * its update and activation request bits in DC_CMD_STATE_CONTROL do
1122a1df3b24SThierry Reding 	 * use the same programming. Setting this fake index here allows the
1123a1df3b24SThierry Reding 	 * code in tegra_add_plane_state() to do the right thing without the
1124a1df3b24SThierry Reding 	 * need to special-casing the cursor plane.
112547802b09SThierry Reding 	 */
112647802b09SThierry Reding 	plane->index = 6;
11271087fac1SThierry Reding 	plane->dc = dc;
112847802b09SThierry Reding 
1129d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay) {
1130d5ec699dSThierry Reding 		num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
1131d5ec699dSThierry Reding 		formats = tegra_legacy_cursor_plane_formats;
113204d5d5dfSDmitry Osipenko 
113304d5d5dfSDmitry Osipenko 		err = tegra_plane_interconnect_init(plane);
113404d5d5dfSDmitry Osipenko 		if (err) {
113504d5d5dfSDmitry Osipenko 			kfree(plane);
113604d5d5dfSDmitry Osipenko 			return ERR_PTR(err);
113704d5d5dfSDmitry Osipenko 		}
1138d5ec699dSThierry Reding 	} else {
1139c7679306SThierry Reding 		num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
1140c7679306SThierry Reding 		formats = tegra_cursor_plane_formats;
1141d5ec699dSThierry Reding 	}
1142c7679306SThierry Reding 
114389f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1144c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
1145be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1146e6fc3b68SBen Widawsky 				       DRM_PLANE_TYPE_CURSOR, NULL);
1147c7679306SThierry Reding 	if (err < 0) {
1148c7679306SThierry Reding 		kfree(plane);
1149c7679306SThierry Reding 		return ERR_PTR(err);
1150c7679306SThierry Reding 	}
1151c7679306SThierry Reding 
11524aa3df71SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
1153fce3a51dSThierry Reding 	drm_plane_create_zpos_immutable_property(&plane->base, 255);
11544aa3df71SThierry Reding 
1155c7679306SThierry Reding 	return &plane->base;
1156c7679306SThierry Reding }
1157c7679306SThierry Reding 
1158511c7023SThierry Reding static const u32 tegra20_overlay_formats[] = {
1159511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1160511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1161dee8268fSThierry Reding 	DRM_FORMAT_RGB565,
1162511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1163511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1164511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1165ebae8d07SThierry Reding 	/* non-native formats */
1166ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
1167ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
1168ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
1169ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
1170511c7023SThierry Reding 	/* planar formats */
1171511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1172511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1173511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1174511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1175511c7023SThierry Reding };
1176511c7023SThierry Reding 
1177511c7023SThierry Reding static const u32 tegra114_overlay_formats[] = {
1178511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1179511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1180511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1181511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1182511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1183511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1184511c7023SThierry Reding 	/* new on Tegra114 */
1185511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1186511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1187511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1188511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1189511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1190511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1191511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1192511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1193511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1194511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1195511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1196511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1197511c7023SThierry Reding 	/* planar formats */
1198511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1199511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1200511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1201511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1202a649b133SThierry Reding 	/* semi-planar formats */
1203a649b133SThierry Reding 	DRM_FORMAT_NV12,
1204a649b133SThierry Reding 	DRM_FORMAT_NV21,
1205a649b133SThierry Reding 	DRM_FORMAT_NV16,
1206a649b133SThierry Reding 	DRM_FORMAT_NV61,
1207a649b133SThierry Reding 	DRM_FORMAT_NV24,
1208a649b133SThierry Reding 	DRM_FORMAT_NV42,
1209511c7023SThierry Reding };
1210511c7023SThierry Reding 
1211511c7023SThierry Reding static const u32 tegra124_overlay_formats[] = {
1212511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1213511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1214511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1215511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1216511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1217511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1218511c7023SThierry Reding 	/* new on Tegra114 */
1219511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1220511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1221511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1222511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1223511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1224511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1225511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1226511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1227511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1228511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1229511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1230511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1231511c7023SThierry Reding 	/* new on Tegra124 */
1232511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
1233511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
1234511c7023SThierry Reding 	/* planar formats */
1235dee8268fSThierry Reding 	DRM_FORMAT_UYVY,
1236f925390eSThierry Reding 	DRM_FORMAT_YUYV,
1237cf5086d3SThierry Reding 	DRM_FORMAT_YVYU,
1238cf5086d3SThierry Reding 	DRM_FORMAT_VYUY,
1239cf5086d3SThierry Reding 	DRM_FORMAT_YUV420, /* YU12 */
1240cf5086d3SThierry Reding 	DRM_FORMAT_YUV422, /* YU16 */
1241cf5086d3SThierry Reding 	DRM_FORMAT_YUV444, /* YU24 */
1242a649b133SThierry Reding 	/* semi-planar formats */
1243a649b133SThierry Reding 	DRM_FORMAT_NV12,
1244a649b133SThierry Reding 	DRM_FORMAT_NV21,
1245a649b133SThierry Reding 	DRM_FORMAT_NV16,
1246a649b133SThierry Reding 	DRM_FORMAT_NV61,
1247a649b133SThierry Reding 	DRM_FORMAT_NV24,
1248a649b133SThierry Reding 	DRM_FORMAT_NV42,
1249dee8268fSThierry Reding };
1250dee8268fSThierry Reding 
1251c7679306SThierry Reding static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
1252c7679306SThierry Reding 						       struct tegra_dc *dc,
12539f446d83SDmitry Osipenko 						       unsigned int index,
12549f446d83SDmitry Osipenko 						       bool cursor)
1255dee8268fSThierry Reding {
125689f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1257dee8268fSThierry Reding 	struct tegra_plane *plane;
1258c7679306SThierry Reding 	unsigned int num_formats;
12599f446d83SDmitry Osipenko 	enum drm_plane_type type;
1260c7679306SThierry Reding 	const u32 *formats;
1261c7679306SThierry Reding 	int err;
1262dee8268fSThierry Reding 
1263f002abc1SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1264dee8268fSThierry Reding 	if (!plane)
1265c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1266dee8268fSThierry Reding 
12671087fac1SThierry Reding 	plane->offset = 0xa00 + 0x200 * index;
1268c7679306SThierry Reding 	plane->index = index;
12691087fac1SThierry Reding 	plane->dc = dc;
1270dee8268fSThierry Reding 
1271511c7023SThierry Reding 	num_formats = dc->soc->num_overlay_formats;
1272511c7023SThierry Reding 	formats = dc->soc->overlay_formats;
1273c7679306SThierry Reding 
127404d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
127504d5d5dfSDmitry Osipenko 	if (err) {
127604d5d5dfSDmitry Osipenko 		kfree(plane);
127704d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
127804d5d5dfSDmitry Osipenko 	}
127904d5d5dfSDmitry Osipenko 
12809f446d83SDmitry Osipenko 	if (!cursor)
12819f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_OVERLAY;
12829f446d83SDmitry Osipenko 	else
12839f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_CURSOR;
12849f446d83SDmitry Osipenko 
128589f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1286301e0ddbSThierry Reding 				       &tegra_plane_funcs, formats,
1287be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1288be4306adSDaniel Vetter 				       type, NULL);
1289f002abc1SThierry Reding 	if (err < 0) {
1290f002abc1SThierry Reding 		kfree(plane);
1291c7679306SThierry Reding 		return ERR_PTR(err);
1292dee8268fSThierry Reding 	}
1293c7679306SThierry Reding 
1294a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
12953dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
1296ab7d3f58SThierry Reding 
1297995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
1298995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
1299995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
13004fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
1301cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
1302995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
1303995c5a50SThierry Reding 	if (err < 0)
1304995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
1305995c5a50SThierry Reding 			err);
1306995c5a50SThierry Reding 
1307c7679306SThierry Reding 	return &plane->base;
1308c7679306SThierry Reding }
1309c7679306SThierry Reding 
131047307954SThierry Reding static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
131147307954SThierry Reding 						    struct tegra_dc *dc)
1312c7679306SThierry Reding {
131347307954SThierry Reding 	struct drm_plane *plane, *primary = NULL;
131447307954SThierry Reding 	unsigned int i, j;
131547307954SThierry Reding 
131647307954SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
131747307954SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
131847307954SThierry Reding 
131947307954SThierry Reding 		if (wgrp->dc == dc->pipe) {
132047307954SThierry Reding 			for (j = 0; j < wgrp->num_windows; j++) {
132147307954SThierry Reding 				unsigned int index = wgrp->windows[j];
132247307954SThierry Reding 
132347307954SThierry Reding 				plane = tegra_shared_plane_create(drm, dc,
132447307954SThierry Reding 								  wgrp->index,
132547307954SThierry Reding 								  index);
132647307954SThierry Reding 				if (IS_ERR(plane))
132747307954SThierry Reding 					return plane;
132847307954SThierry Reding 
132947307954SThierry Reding 				/*
133047307954SThierry Reding 				 * Choose the first shared plane owned by this
133147307954SThierry Reding 				 * head as the primary plane.
133247307954SThierry Reding 				 */
133347307954SThierry Reding 				if (!primary) {
133447307954SThierry Reding 					plane->type = DRM_PLANE_TYPE_PRIMARY;
133547307954SThierry Reding 					primary = plane;
133647307954SThierry Reding 				}
133747307954SThierry Reding 			}
133847307954SThierry Reding 		}
133947307954SThierry Reding 	}
134047307954SThierry Reding 
134147307954SThierry Reding 	return primary;
134247307954SThierry Reding }
134347307954SThierry Reding 
134447307954SThierry Reding static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
134547307954SThierry Reding 					     struct tegra_dc *dc)
134647307954SThierry Reding {
13478f62142eSThierry Reding 	struct drm_plane *planes[2], *primary;
13489f446d83SDmitry Osipenko 	unsigned int planes_num;
1349c7679306SThierry Reding 	unsigned int i;
13508f62142eSThierry Reding 	int err;
1351c7679306SThierry Reding 
135247307954SThierry Reding 	primary = tegra_primary_plane_create(drm, dc);
135347307954SThierry Reding 	if (IS_ERR(primary))
135447307954SThierry Reding 		return primary;
135547307954SThierry Reding 
13569f446d83SDmitry Osipenko 	if (dc->soc->supports_cursor)
13579f446d83SDmitry Osipenko 		planes_num = 2;
13589f446d83SDmitry Osipenko 	else
13599f446d83SDmitry Osipenko 		planes_num = 1;
13609f446d83SDmitry Osipenko 
13619f446d83SDmitry Osipenko 	for (i = 0; i < planes_num; i++) {
13629f446d83SDmitry Osipenko 		planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i,
13639f446d83SDmitry Osipenko 							  false);
13648f62142eSThierry Reding 		if (IS_ERR(planes[i])) {
13658f62142eSThierry Reding 			err = PTR_ERR(planes[i]);
13668f62142eSThierry Reding 
13678f62142eSThierry Reding 			while (i--)
136840dc962dSThierry Reding 				planes[i]->funcs->destroy(planes[i]);
13698f62142eSThierry Reding 
137040dc962dSThierry Reding 			primary->funcs->destroy(primary);
13718f62142eSThierry Reding 			return ERR_PTR(err);
137247307954SThierry Reding 		}
1373f002abc1SThierry Reding 	}
1374dee8268fSThierry Reding 
137547307954SThierry Reding 	return primary;
1376dee8268fSThierry Reding }
1377dee8268fSThierry Reding 
1378f002abc1SThierry Reding static void tegra_dc_destroy(struct drm_crtc *crtc)
1379f002abc1SThierry Reding {
1380f002abc1SThierry Reding 	drm_crtc_cleanup(crtc);
1381f002abc1SThierry Reding }
1382f002abc1SThierry Reding 
1383ca915b10SThierry Reding static void tegra_crtc_reset(struct drm_crtc *crtc)
1384ca915b10SThierry Reding {
1385b7e0b04aSMaarten Lankhorst 	struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
1386ca915b10SThierry Reding 
13873b59b7acSThierry Reding 	if (crtc->state)
1388b7e0b04aSMaarten Lankhorst 		tegra_crtc_atomic_destroy_state(crtc, crtc->state);
13893b59b7acSThierry Reding 
1390b7e0b04aSMaarten Lankhorst 	__drm_atomic_helper_crtc_reset(crtc, &state->base);
1391ca915b10SThierry Reding }
1392ca915b10SThierry Reding 
1393ca915b10SThierry Reding static struct drm_crtc_state *
1394ca915b10SThierry Reding tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
1395ca915b10SThierry Reding {
1396ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc->state);
1397ca915b10SThierry Reding 	struct tegra_dc_state *copy;
1398ca915b10SThierry Reding 
13993b59b7acSThierry Reding 	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
1400ca915b10SThierry Reding 	if (!copy)
1401ca915b10SThierry Reding 		return NULL;
1402ca915b10SThierry Reding 
14033b59b7acSThierry Reding 	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
14043b59b7acSThierry Reding 	copy->clk = state->clk;
14053b59b7acSThierry Reding 	copy->pclk = state->pclk;
14063b59b7acSThierry Reding 	copy->div = state->div;
14073b59b7acSThierry Reding 	copy->planes = state->planes;
1408ca915b10SThierry Reding 
1409ca915b10SThierry Reding 	return &copy->base;
1410ca915b10SThierry Reding }
1411ca915b10SThierry Reding 
1412ca915b10SThierry Reding static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
1413ca915b10SThierry Reding 					    struct drm_crtc_state *state)
1414ca915b10SThierry Reding {
1415ec2dc6a0SDaniel Vetter 	__drm_atomic_helper_crtc_destroy_state(state);
1416ca915b10SThierry Reding 	kfree(state);
1417ca915b10SThierry Reding }
1418ca915b10SThierry Reding 
1419b95800eeSThierry Reding #define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
1420b95800eeSThierry Reding 
1421b95800eeSThierry Reding static const struct debugfs_reg32 tegra_dc_regs[] = {
1422b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT),
1423b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL),
1424b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR),
1425b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT),
1426b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL),
1427b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR),
1428b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT),
1429b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL),
1430b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR),
1431b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT),
1432b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL),
1433b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR),
1434b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC),
1435b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0),
1436b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND),
1437b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE),
1438b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL),
1439b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_STATUS),
1440b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_MASK),
1441b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_ENABLE),
1442b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_TYPE),
1443b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_POLARITY),
1444b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1),
1445b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2),
1446b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3),
1447b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_ACCESS),
1448b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_CONTROL),
1449b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER),
1450b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL),
1451b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CONTROL),
1452b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM),
1453b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)),
1454b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)),
1455b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)),
1456b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)),
1457b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)),
1458b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)),
1459b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)),
1460b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)),
1461b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)),
1462b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)),
1463b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)),
1464b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)),
1465b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)),
1466b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)),
1467b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)),
1468b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)),
1469b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)),
1470b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)),
1471b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)),
1472b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)),
1473b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)),
1474b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)),
1475b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)),
1476b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
1477b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
1478b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
1479b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
1480b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
1481b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
1482b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
1483b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_CONTROL),
1484b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
1485b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
1486b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD),
1487b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_CS_DC),
1488b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A),
1489b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B),
1490b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_CTRL),
1491b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER),
1492b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED),
1493b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0),
1494b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1),
1495b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS),
1496b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY),
1497b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER),
1498b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS),
1499b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_REF_TO_SYNC),
1500b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SYNC_WIDTH),
1501b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BACK_PORCH),
1502b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_ACTIVE),
1503b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_FRONT_PORCH),
1504b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL),
1505b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A),
1506b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B),
1507b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C),
1508b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D),
1509b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL),
1510b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A),
1511b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B),
1512b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C),
1513b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D),
1514b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL),
1515b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A),
1516b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B),
1517b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C),
1518b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D),
1519b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL),
1520b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A),
1521b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B),
1522b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C),
1523b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL),
1524b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A),
1525b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B),
1526b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C),
1527b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL),
1528b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A),
1529b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL),
1530b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A),
1531b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M0_CONTROL),
1532b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M1_CONTROL),
1533b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DI_CONTROL),
1534b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_CONTROL),
1535b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_A),
1536b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_B),
1537b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_C),
1538b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_D),
1539b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL),
1540b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL),
1541b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL),
1542b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS),
1543b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS),
1544b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS),
1545b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS),
1546b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BORDER_COLOR),
1547b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER),
1548b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER),
1549b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER),
1550b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER),
1551b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND),
1552b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND),
1553b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR),
1554b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS),
1555b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION),
1556b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS),
1557b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL),
1558b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A),
1559b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B),
1560b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C),
1561b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D),
1562b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL),
1563b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST),
1564b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST),
1565b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST),
1566b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST),
1567b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL),
1568b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL),
1569b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CONTROL),
1570b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF),
1571b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(0)),
1572b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(1)),
1573b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(2)),
1574b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(3)),
1575b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(4)),
1576b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(5)),
1577b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(6)),
1578b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(7)),
1579b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(8)),
1580b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL),
1581b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT),
1582b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)),
1583b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)),
1584b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)),
1585b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)),
1586b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)),
1587b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)),
1588b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)),
1589b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)),
1590b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)),
1591b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)),
1592b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)),
1593b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)),
1594b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL),
1595b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES),
1596b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES),
1597b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI),
1598b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL),
1599b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_WIN_OPTIONS),
1600b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BYTE_SWAP),
1601b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL),
1602b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_COLOR_DEPTH),
1603b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_POSITION),
1604b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_SIZE),
1605b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE),
1606b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA),
1607b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA),
1608b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DDA_INC),
1609b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_LINE_STRIDE),
1610b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUF_STRIDE),
1611b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE),
1612b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE),
1613b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DV_CONTROL),
1614b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_NOKEY),
1615b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_1WIN),
1616b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X),
1617b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y),
1618b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY),
1619b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL),
1620b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR),
1621b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS),
1622b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U),
1623b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS),
1624b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V),
1625b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS),
1626b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET),
1627b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS),
1628b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET),
1629b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS),
1630b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS),
1631b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS),
1632b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS),
1633b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS),
1634b95800eeSThierry Reding };
1635b95800eeSThierry Reding 
1636b95800eeSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data)
1637b95800eeSThierry Reding {
1638b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1639b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1640b95800eeSThierry Reding 	unsigned int i;
1641b95800eeSThierry Reding 	int err = 0;
1642b95800eeSThierry Reding 
1643b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1644b95800eeSThierry Reding 
1645b95800eeSThierry Reding 	if (!dc->base.state->active) {
1646b95800eeSThierry Reding 		err = -EBUSY;
1647b95800eeSThierry Reding 		goto unlock;
1648b95800eeSThierry Reding 	}
1649b95800eeSThierry Reding 
1650b95800eeSThierry Reding 	for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) {
1651b95800eeSThierry Reding 		unsigned int offset = tegra_dc_regs[i].offset;
1652b95800eeSThierry Reding 
1653b95800eeSThierry Reding 		seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name,
1654b95800eeSThierry Reding 			   offset, tegra_dc_readl(dc, offset));
1655b95800eeSThierry Reding 	}
1656b95800eeSThierry Reding 
1657b95800eeSThierry Reding unlock:
1658b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1659b95800eeSThierry Reding 	return err;
1660b95800eeSThierry Reding }
1661b95800eeSThierry Reding 
1662b95800eeSThierry Reding static int tegra_dc_show_crc(struct seq_file *s, void *data)
1663b95800eeSThierry Reding {
1664b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1665b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1666b95800eeSThierry Reding 	int err = 0;
1667b95800eeSThierry Reding 	u32 value;
1668b95800eeSThierry Reding 
1669b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1670b95800eeSThierry Reding 
1671b95800eeSThierry Reding 	if (!dc->base.state->active) {
1672b95800eeSThierry Reding 		err = -EBUSY;
1673b95800eeSThierry Reding 		goto unlock;
1674b95800eeSThierry Reding 	}
1675b95800eeSThierry Reding 
1676b95800eeSThierry Reding 	value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
1677b95800eeSThierry Reding 	tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
1678b95800eeSThierry Reding 	tegra_dc_commit(dc);
1679b95800eeSThierry Reding 
1680b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1681b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1682b95800eeSThierry Reding 
1683b95800eeSThierry Reding 	value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM);
1684b95800eeSThierry Reding 	seq_printf(s, "%08x\n", value);
1685b95800eeSThierry Reding 
1686b95800eeSThierry Reding 	tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
1687b95800eeSThierry Reding 
1688b95800eeSThierry Reding unlock:
1689b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1690b95800eeSThierry Reding 	return err;
1691b95800eeSThierry Reding }
1692b95800eeSThierry Reding 
1693b95800eeSThierry Reding static int tegra_dc_show_stats(struct seq_file *s, void *data)
1694b95800eeSThierry Reding {
1695b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1696b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1697b95800eeSThierry Reding 
1698b95800eeSThierry Reding 	seq_printf(s, "frames: %lu\n", dc->stats.frames);
1699b95800eeSThierry Reding 	seq_printf(s, "vblank: %lu\n", dc->stats.vblank);
1700b95800eeSThierry Reding 	seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
1701b95800eeSThierry Reding 	seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
1702b95800eeSThierry Reding 
1703ad85b084SDmitry Osipenko 	seq_printf(s, "frames total: %lu\n", dc->stats.frames_total);
1704ad85b084SDmitry Osipenko 	seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total);
1705ad85b084SDmitry Osipenko 	seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total);
1706ad85b084SDmitry Osipenko 	seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total);
1707ad85b084SDmitry Osipenko 
1708b95800eeSThierry Reding 	return 0;
1709b95800eeSThierry Reding }
1710b95800eeSThierry Reding 
1711b95800eeSThierry Reding static struct drm_info_list debugfs_files[] = {
1712b95800eeSThierry Reding 	{ "regs", tegra_dc_show_regs, 0, NULL },
1713b95800eeSThierry Reding 	{ "crc", tegra_dc_show_crc, 0, NULL },
1714b95800eeSThierry Reding 	{ "stats", tegra_dc_show_stats, 0, NULL },
1715b95800eeSThierry Reding };
1716b95800eeSThierry Reding 
1717b95800eeSThierry Reding static int tegra_dc_late_register(struct drm_crtc *crtc)
1718b95800eeSThierry Reding {
1719b95800eeSThierry Reding 	unsigned int i, count = ARRAY_SIZE(debugfs_files);
1720b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
172139f55c61SArnd Bergmann 	struct dentry *root;
1722b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1723b95800eeSThierry Reding 
172439f55c61SArnd Bergmann #ifdef CONFIG_DEBUG_FS
172539f55c61SArnd Bergmann 	root = crtc->debugfs_entry;
172639f55c61SArnd Bergmann #else
172739f55c61SArnd Bergmann 	root = NULL;
172839f55c61SArnd Bergmann #endif
172939f55c61SArnd Bergmann 
1730b95800eeSThierry Reding 	dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
1731b95800eeSThierry Reding 				    GFP_KERNEL);
1732b95800eeSThierry Reding 	if (!dc->debugfs_files)
1733b95800eeSThierry Reding 		return -ENOMEM;
1734b95800eeSThierry Reding 
1735b95800eeSThierry Reding 	for (i = 0; i < count; i++)
1736b95800eeSThierry Reding 		dc->debugfs_files[i].data = dc;
1737b95800eeSThierry Reding 
1738ad6d94f2SWambui Karuga 	drm_debugfs_create_files(dc->debugfs_files, count, root, minor);
1739b95800eeSThierry Reding 
1740b95800eeSThierry Reding 	return 0;
1741b95800eeSThierry Reding }
1742b95800eeSThierry Reding 
1743b95800eeSThierry Reding static void tegra_dc_early_unregister(struct drm_crtc *crtc)
1744b95800eeSThierry Reding {
1745b95800eeSThierry Reding 	unsigned int count = ARRAY_SIZE(debugfs_files);
1746b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
1747b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1748b95800eeSThierry Reding 
1749b95800eeSThierry Reding 	drm_debugfs_remove_files(dc->debugfs_files, count, minor);
1750b95800eeSThierry Reding 	kfree(dc->debugfs_files);
1751b95800eeSThierry Reding 	dc->debugfs_files = NULL;
1752b95800eeSThierry Reding }
1753b95800eeSThierry Reding 
1754c49c81e2SThierry Reding static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
1755c49c81e2SThierry Reding {
1756c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1757c49c81e2SThierry Reding 
175847307954SThierry Reding 	/* XXX vblank syncpoints don't work with nvdisplay yet */
175947307954SThierry Reding 	if (dc->syncpt && !dc->soc->has_nvdisplay)
1760c49c81e2SThierry Reding 		return host1x_syncpt_read(dc->syncpt);
1761c49c81e2SThierry Reding 
1762c49c81e2SThierry Reding 	/* fallback to software emulated VBLANK counter */
17633abe2413SDhinakaran Pandiyan 	return (u32)drm_crtc_vblank_count(&dc->base);
1764c49c81e2SThierry Reding }
1765c49c81e2SThierry Reding 
1766c49c81e2SThierry Reding static int tegra_dc_enable_vblank(struct drm_crtc *crtc)
1767c49c81e2SThierry Reding {
1768c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1769363541e8SThierry Reding 	u32 value;
1770c49c81e2SThierry Reding 
1771c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1772c49c81e2SThierry Reding 	value |= VBLANK_INT;
1773c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1774c49c81e2SThierry Reding 
1775c49c81e2SThierry Reding 	return 0;
1776c49c81e2SThierry Reding }
1777c49c81e2SThierry Reding 
1778c49c81e2SThierry Reding static void tegra_dc_disable_vblank(struct drm_crtc *crtc)
1779c49c81e2SThierry Reding {
1780c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1781363541e8SThierry Reding 	u32 value;
1782c49c81e2SThierry Reding 
1783c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1784c49c81e2SThierry Reding 	value &= ~VBLANK_INT;
1785c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1786c49c81e2SThierry Reding }
1787c49c81e2SThierry Reding 
1788dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = {
17891503ca47SThierry Reding 	.page_flip = drm_atomic_helper_page_flip,
179074f48791SThierry Reding 	.set_config = drm_atomic_helper_set_config,
1791f002abc1SThierry Reding 	.destroy = tegra_dc_destroy,
1792ca915b10SThierry Reding 	.reset = tegra_crtc_reset,
1793ca915b10SThierry Reding 	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
1794ca915b10SThierry Reding 	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
1795b95800eeSThierry Reding 	.late_register = tegra_dc_late_register,
1796b95800eeSThierry Reding 	.early_unregister = tegra_dc_early_unregister,
179710437d9bSShawn Guo 	.get_vblank_counter = tegra_dc_get_vblank_counter,
179810437d9bSShawn Guo 	.enable_vblank = tegra_dc_enable_vblank,
179910437d9bSShawn Guo 	.disable_vblank = tegra_dc_disable_vblank,
1800dee8268fSThierry Reding };
1801dee8268fSThierry Reding 
1802dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc,
1803dee8268fSThierry Reding 				struct drm_display_mode *mode)
1804dee8268fSThierry Reding {
18050444c0ffSThierry Reding 	unsigned int h_ref_to_sync = 1;
18060444c0ffSThierry Reding 	unsigned int v_ref_to_sync = 1;
1807dee8268fSThierry Reding 	unsigned long value;
1808dee8268fSThierry Reding 
180947307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
1810dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
1811dee8268fSThierry Reding 
1812dee8268fSThierry Reding 		value = (v_ref_to_sync << 16) | h_ref_to_sync;
1813dee8268fSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
181447307954SThierry Reding 	}
1815dee8268fSThierry Reding 
1816dee8268fSThierry Reding 	value = ((mode->vsync_end - mode->vsync_start) << 16) |
1817dee8268fSThierry Reding 		((mode->hsync_end - mode->hsync_start) <<  0);
1818dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
1819dee8268fSThierry Reding 
1820dee8268fSThierry Reding 	value = ((mode->vtotal - mode->vsync_end) << 16) |
1821dee8268fSThierry Reding 		((mode->htotal - mode->hsync_end) <<  0);
1822dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
1823dee8268fSThierry Reding 
1824dee8268fSThierry Reding 	value = ((mode->vsync_start - mode->vdisplay) << 16) |
1825dee8268fSThierry Reding 		((mode->hsync_start - mode->hdisplay) <<  0);
1826dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
1827dee8268fSThierry Reding 
1828dee8268fSThierry Reding 	value = (mode->vdisplay << 16) | mode->hdisplay;
1829dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
1830dee8268fSThierry Reding 
1831dee8268fSThierry Reding 	return 0;
1832dee8268fSThierry Reding }
1833dee8268fSThierry Reding 
18349d910b60SThierry Reding /**
18359d910b60SThierry Reding  * tegra_dc_state_setup_clock - check clock settings and store them in atomic
18369d910b60SThierry Reding  *     state
18379d910b60SThierry Reding  * @dc: display controller
18389d910b60SThierry Reding  * @crtc_state: CRTC atomic state
18399d910b60SThierry Reding  * @clk: parent clock for display controller
18409d910b60SThierry Reding  * @pclk: pixel clock
18419d910b60SThierry Reding  * @div: shift clock divider
18429d910b60SThierry Reding  *
18439d910b60SThierry Reding  * Returns:
18449d910b60SThierry Reding  * 0 on success or a negative error-code on failure.
18459d910b60SThierry Reding  */
1846ca915b10SThierry Reding int tegra_dc_state_setup_clock(struct tegra_dc *dc,
1847ca915b10SThierry Reding 			       struct drm_crtc_state *crtc_state,
1848ca915b10SThierry Reding 			       struct clk *clk, unsigned long pclk,
1849ca915b10SThierry Reding 			       unsigned int div)
1850ca915b10SThierry Reding {
1851ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc_state);
1852ca915b10SThierry Reding 
1853d2982748SThierry Reding 	if (!clk_has_parent(dc->clk, clk))
1854d2982748SThierry Reding 		return -EINVAL;
1855d2982748SThierry Reding 
1856ca915b10SThierry Reding 	state->clk = clk;
1857ca915b10SThierry Reding 	state->pclk = pclk;
1858ca915b10SThierry Reding 	state->div = div;
1859ca915b10SThierry Reding 
1860ca915b10SThierry Reding 	return 0;
1861ca915b10SThierry Reding }
1862ca915b10SThierry Reding 
18634ce3048cSDmitry Osipenko static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
18644ce3048cSDmitry Osipenko 					  struct tegra_dc_state *state)
18654ce3048cSDmitry Osipenko {
18664ce3048cSDmitry Osipenko 	unsigned long rate, pstate;
18674ce3048cSDmitry Osipenko 	struct dev_pm_opp *opp;
18684ce3048cSDmitry Osipenko 	int err;
18694ce3048cSDmitry Osipenko 
18704ce3048cSDmitry Osipenko 	if (!dc->has_opp_table)
18714ce3048cSDmitry Osipenko 		return;
18724ce3048cSDmitry Osipenko 
18734ce3048cSDmitry Osipenko 	/* calculate actual pixel clock rate which depends on internal divider */
18744ce3048cSDmitry Osipenko 	rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
18754ce3048cSDmitry Osipenko 
18764ce3048cSDmitry Osipenko 	/* find suitable OPP for the rate */
18774ce3048cSDmitry Osipenko 	opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
18784ce3048cSDmitry Osipenko 
18794ce3048cSDmitry Osipenko 	/*
18804ce3048cSDmitry Osipenko 	 * Very high resolution modes may results in a clock rate that is
18814ce3048cSDmitry Osipenko 	 * above the characterized maximum. In this case it's okay to fall
18824ce3048cSDmitry Osipenko 	 * back to the characterized maximum.
18834ce3048cSDmitry Osipenko 	 */
18844ce3048cSDmitry Osipenko 	if (opp == ERR_PTR(-ERANGE))
18854ce3048cSDmitry Osipenko 		opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
18864ce3048cSDmitry Osipenko 
18874ce3048cSDmitry Osipenko 	if (IS_ERR(opp)) {
18884ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
18894ce3048cSDmitry Osipenko 			rate, opp);
18904ce3048cSDmitry Osipenko 		return;
18914ce3048cSDmitry Osipenko 	}
18924ce3048cSDmitry Osipenko 
18934ce3048cSDmitry Osipenko 	pstate = dev_pm_opp_get_required_pstate(opp, 0);
18944ce3048cSDmitry Osipenko 	dev_pm_opp_put(opp);
18954ce3048cSDmitry Osipenko 
18964ce3048cSDmitry Osipenko 	/*
18974ce3048cSDmitry Osipenko 	 * The minimum core voltage depends on the pixel clock rate (which
18984ce3048cSDmitry Osipenko 	 * depends on internal clock divider of the CRTC) and not on the
18994ce3048cSDmitry Osipenko 	 * rate of the display controller clock. This is why we're not using
19004ce3048cSDmitry Osipenko 	 * dev_pm_opp_set_rate() API and instead controlling the power domain
19014ce3048cSDmitry Osipenko 	 * directly.
19024ce3048cSDmitry Osipenko 	 */
19034ce3048cSDmitry Osipenko 	err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
19044ce3048cSDmitry Osipenko 	if (err)
19054ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
19064ce3048cSDmitry Osipenko 			pstate, err);
19074ce3048cSDmitry Osipenko }
19084ce3048cSDmitry Osipenko 
19090c921b6dSDmitry Osipenko static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
191076d59ed0SThierry Reding 				    struct tegra_dc_state *state)
191176d59ed0SThierry Reding {
191276d59ed0SThierry Reding 	int err;
191376d59ed0SThierry Reding 
191476d59ed0SThierry Reding 	err = clk_set_parent(dc->clk, state->clk);
191576d59ed0SThierry Reding 	if (err < 0)
191676d59ed0SThierry Reding 		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
191776d59ed0SThierry Reding 
191876d59ed0SThierry Reding 	/*
191976d59ed0SThierry Reding 	 * Outputs may not want to change the parent clock rate. This is only
192076d59ed0SThierry Reding 	 * relevant to Tegra20 where only a single display PLL is available.
192176d59ed0SThierry Reding 	 * Since that PLL would typically be used for HDMI, an internal LVDS
192276d59ed0SThierry Reding 	 * panel would need to be driven by some other clock such as PLL_P
192376d59ed0SThierry Reding 	 * which is shared with other peripherals. Changing the clock rate
192476d59ed0SThierry Reding 	 * should therefore be avoided.
192576d59ed0SThierry Reding 	 */
192676d59ed0SThierry Reding 	if (state->pclk > 0) {
192776d59ed0SThierry Reding 		err = clk_set_rate(state->clk, state->pclk);
192876d59ed0SThierry Reding 		if (err < 0)
192976d59ed0SThierry Reding 			dev_err(dc->dev,
193076d59ed0SThierry Reding 				"failed to set clock rate to %lu Hz\n",
193176d59ed0SThierry Reding 				state->pclk);
1932f8fb97c9SDmitry Osipenko 
1933f8fb97c9SDmitry Osipenko 		err = clk_set_rate(dc->clk, state->pclk);
1934f8fb97c9SDmitry Osipenko 		if (err < 0)
1935f8fb97c9SDmitry Osipenko 			dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n",
1936f8fb97c9SDmitry Osipenko 				dc->clk, state->pclk, err);
193776d59ed0SThierry Reding 	}
193876d59ed0SThierry Reding 
193976d59ed0SThierry Reding 	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
194076d59ed0SThierry Reding 		      state->div);
194176d59ed0SThierry Reding 	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
19424ce3048cSDmitry Osipenko 
19434ce3048cSDmitry Osipenko 	tegra_dc_update_voltage_state(dc, state);
194476d59ed0SThierry Reding }
194576d59ed0SThierry Reding 
1946003fc848SThierry Reding static void tegra_dc_stop(struct tegra_dc *dc)
1947003fc848SThierry Reding {
1948003fc848SThierry Reding 	u32 value;
1949003fc848SThierry Reding 
1950003fc848SThierry Reding 	/* stop the display controller */
1951003fc848SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
1952003fc848SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
1953003fc848SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
1954003fc848SThierry Reding 
1955003fc848SThierry Reding 	tegra_dc_commit(dc);
1956003fc848SThierry Reding }
1957003fc848SThierry Reding 
1958003fc848SThierry Reding static bool tegra_dc_idle(struct tegra_dc *dc)
1959003fc848SThierry Reding {
1960003fc848SThierry Reding 	u32 value;
1961003fc848SThierry Reding 
1962003fc848SThierry Reding 	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
1963003fc848SThierry Reding 
1964003fc848SThierry Reding 	return (value & DISP_CTRL_MODE_MASK) == 0;
1965003fc848SThierry Reding }
1966003fc848SThierry Reding 
1967003fc848SThierry Reding static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
1968003fc848SThierry Reding {
1969003fc848SThierry Reding 	timeout = jiffies + msecs_to_jiffies(timeout);
1970003fc848SThierry Reding 
1971003fc848SThierry Reding 	while (time_before(jiffies, timeout)) {
1972003fc848SThierry Reding 		if (tegra_dc_idle(dc))
1973003fc848SThierry Reding 			return 0;
1974003fc848SThierry Reding 
1975003fc848SThierry Reding 		usleep_range(1000, 2000);
1976003fc848SThierry Reding 	}
1977003fc848SThierry Reding 
1978003fc848SThierry Reding 	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
1979003fc848SThierry Reding 	return -ETIMEDOUT;
1980003fc848SThierry Reding }
1981003fc848SThierry Reding 
198204d5d5dfSDmitry Osipenko static void
198304d5d5dfSDmitry Osipenko tegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc,
198404d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state,
198504d5d5dfSDmitry Osipenko 				   bool prepare_bandwidth_transition)
198604d5d5dfSDmitry Osipenko {
198704d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *old_tegra_state, *new_tegra_state;
198804d5d5dfSDmitry Osipenko 	u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw;
198904d5d5dfSDmitry Osipenko 	const struct drm_plane_state *old_plane_state;
199004d5d5dfSDmitry Osipenko 	const struct drm_crtc_state *old_crtc_state;
199104d5d5dfSDmitry Osipenko 	struct tegra_dc_window window, old_window;
199204d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
199304d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
199404d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
199504d5d5dfSDmitry Osipenko 
199604d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
199704d5d5dfSDmitry Osipenko 		return;
199804d5d5dfSDmitry Osipenko 
199904d5d5dfSDmitry Osipenko 	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
200004d5d5dfSDmitry Osipenko 
200104d5d5dfSDmitry Osipenko 	if (!crtc->state->active) {
200204d5d5dfSDmitry Osipenko 		if (!old_crtc_state->active)
200304d5d5dfSDmitry Osipenko 			return;
200404d5d5dfSDmitry Osipenko 
200504d5d5dfSDmitry Osipenko 		/*
200604d5d5dfSDmitry Osipenko 		 * When CRTC is disabled on DPMS, the state of attached planes
200704d5d5dfSDmitry Osipenko 		 * is kept unchanged. Hence we need to enforce removal of the
200804d5d5dfSDmitry Osipenko 		 * bandwidths from the ICC paths.
200904d5d5dfSDmitry Osipenko 		 */
201004d5d5dfSDmitry Osipenko 		drm_atomic_crtc_for_each_plane(plane, crtc) {
201104d5d5dfSDmitry Osipenko 			tegra = to_tegra_plane(plane);
201204d5d5dfSDmitry Osipenko 
201304d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem, 0, 0);
201404d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
201504d5d5dfSDmitry Osipenko 		}
201604d5d5dfSDmitry Osipenko 
201704d5d5dfSDmitry Osipenko 		return;
201804d5d5dfSDmitry Osipenko 	}
201904d5d5dfSDmitry Osipenko 
202004d5d5dfSDmitry Osipenko 	for_each_old_plane_in_state(old_crtc_state->state, plane,
202104d5d5dfSDmitry Osipenko 				    old_plane_state, i) {
202204d5d5dfSDmitry Osipenko 		old_tegra_state = to_const_tegra_plane_state(old_plane_state);
202304d5d5dfSDmitry Osipenko 		new_tegra_state = to_const_tegra_plane_state(plane->state);
202404d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
202504d5d5dfSDmitry Osipenko 
202604d5d5dfSDmitry Osipenko 		/*
202704d5d5dfSDmitry Osipenko 		 * We're iterating over the global atomic state and it contains
202804d5d5dfSDmitry Osipenko 		 * planes from another CRTC, hence we need to filter out the
202904d5d5dfSDmitry Osipenko 		 * planes unrelated to this CRTC.
203004d5d5dfSDmitry Osipenko 		 */
203104d5d5dfSDmitry Osipenko 		if (tegra->dc != dc)
203204d5d5dfSDmitry Osipenko 			continue;
203304d5d5dfSDmitry Osipenko 
203404d5d5dfSDmitry Osipenko 		new_avg_bw = new_tegra_state->avg_memory_bandwidth;
203504d5d5dfSDmitry Osipenko 		old_avg_bw = old_tegra_state->avg_memory_bandwidth;
203604d5d5dfSDmitry Osipenko 
203704d5d5dfSDmitry Osipenko 		new_peak_bw = new_tegra_state->total_peak_memory_bandwidth;
203804d5d5dfSDmitry Osipenko 		old_peak_bw = old_tegra_state->total_peak_memory_bandwidth;
203904d5d5dfSDmitry Osipenko 
204004d5d5dfSDmitry Osipenko 		/*
204104d5d5dfSDmitry Osipenko 		 * See the comment related to !crtc->state->active above,
204204d5d5dfSDmitry Osipenko 		 * which explains why bandwidths need to be updated when
204304d5d5dfSDmitry Osipenko 		 * CRTC is turning ON.
204404d5d5dfSDmitry Osipenko 		 */
204504d5d5dfSDmitry Osipenko 		if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw &&
204604d5d5dfSDmitry Osipenko 		    old_crtc_state->active)
204704d5d5dfSDmitry Osipenko 			continue;
204804d5d5dfSDmitry Osipenko 
204904d5d5dfSDmitry Osipenko 		window.src.h = drm_rect_height(&plane->state->src) >> 16;
205004d5d5dfSDmitry Osipenko 		window.dst.h = drm_rect_height(&plane->state->dst);
205104d5d5dfSDmitry Osipenko 
205204d5d5dfSDmitry Osipenko 		old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16;
205304d5d5dfSDmitry Osipenko 		old_window.dst.h = drm_rect_height(&old_plane_state->dst);
205404d5d5dfSDmitry Osipenko 
205504d5d5dfSDmitry Osipenko 		/*
205604d5d5dfSDmitry Osipenko 		 * During the preparation phase (atomic_begin), the memory
205704d5d5dfSDmitry Osipenko 		 * freq should go high before the DC changes are committed
205804d5d5dfSDmitry Osipenko 		 * if bandwidth requirement goes up, otherwise memory freq
205904d5d5dfSDmitry Osipenko 		 * should to stay high if BW requirement goes down.  The
206004d5d5dfSDmitry Osipenko 		 * opposite applies to the completion phase (post_commit).
206104d5d5dfSDmitry Osipenko 		 */
206204d5d5dfSDmitry Osipenko 		if (prepare_bandwidth_transition) {
206304d5d5dfSDmitry Osipenko 			new_avg_bw = max(old_avg_bw, new_avg_bw);
206404d5d5dfSDmitry Osipenko 			new_peak_bw = max(old_peak_bw, new_peak_bw);
206504d5d5dfSDmitry Osipenko 
206604d5d5dfSDmitry Osipenko 			if (tegra_plane_use_vertical_filtering(tegra, &old_window))
206704d5d5dfSDmitry Osipenko 				window = old_window;
206804d5d5dfSDmitry Osipenko 		}
206904d5d5dfSDmitry Osipenko 
207004d5d5dfSDmitry Osipenko 		icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw);
207104d5d5dfSDmitry Osipenko 
207204d5d5dfSDmitry Osipenko 		if (tegra_plane_use_vertical_filtering(tegra, &window))
207304d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw);
207404d5d5dfSDmitry Osipenko 		else
207504d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
207604d5d5dfSDmitry Osipenko 	}
207704d5d5dfSDmitry Osipenko }
207804d5d5dfSDmitry Osipenko 
207964581714SLaurent Pinchart static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
2080351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
2081003fc848SThierry Reding {
2082003fc848SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2083003fc848SThierry Reding 	u32 value;
2084fd67e9c6SThierry Reding 	int err;
2085003fc848SThierry Reding 
2086003fc848SThierry Reding 	if (!tegra_dc_idle(dc)) {
2087003fc848SThierry Reding 		tegra_dc_stop(dc);
2088003fc848SThierry Reding 
2089003fc848SThierry Reding 		/*
2090003fc848SThierry Reding 		 * Ignore the return value, there isn't anything useful to do
2091003fc848SThierry Reding 		 * in case this fails.
2092003fc848SThierry Reding 		 */
2093003fc848SThierry Reding 		tegra_dc_wait_idle(dc, 100);
2094003fc848SThierry Reding 	}
2095003fc848SThierry Reding 
2096003fc848SThierry Reding 	/*
2097003fc848SThierry Reding 	 * This should really be part of the RGB encoder driver, but clearing
2098003fc848SThierry Reding 	 * these bits has the side-effect of stopping the display controller.
2099003fc848SThierry Reding 	 * When that happens no VBLANK interrupts will be raised. At the same
2100003fc848SThierry Reding 	 * time the encoder is disabled before the display controller, so the
2101003fc848SThierry Reding 	 * above code is always going to timeout waiting for the controller
2102003fc848SThierry Reding 	 * to go idle.
2103003fc848SThierry Reding 	 *
2104003fc848SThierry Reding 	 * Given the close coupling between the RGB encoder and the display
2105003fc848SThierry Reding 	 * controller doing it here is still kind of okay. None of the other
2106003fc848SThierry Reding 	 * encoder drivers require these bits to be cleared.
2107003fc848SThierry Reding 	 *
2108003fc848SThierry Reding 	 * XXX: Perhaps given that the display controller is switched off at
2109003fc848SThierry Reding 	 * this point anyway maybe clearing these bits isn't even useful for
2110003fc848SThierry Reding 	 * the RGB encoder?
2111003fc848SThierry Reding 	 */
2112003fc848SThierry Reding 	if (dc->rgb) {
2113003fc848SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2114003fc848SThierry Reding 		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2115003fc848SThierry Reding 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
2116003fc848SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
2117003fc848SThierry Reding 	}
2118003fc848SThierry Reding 
2119003fc848SThierry Reding 	tegra_dc_stats_reset(&dc->stats);
2120003fc848SThierry Reding 	drm_crtc_vblank_off(crtc);
212133a8eb8dSThierry Reding 
21229d99ab6eSThierry Reding 	spin_lock_irq(&crtc->dev->event_lock);
21239d99ab6eSThierry Reding 
21249d99ab6eSThierry Reding 	if (crtc->state->event) {
21259d99ab6eSThierry Reding 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
21269d99ab6eSThierry Reding 		crtc->state->event = NULL;
21279d99ab6eSThierry Reding 	}
21289d99ab6eSThierry Reding 
21299d99ab6eSThierry Reding 	spin_unlock_irq(&crtc->dev->event_lock);
21309d99ab6eSThierry Reding 
2131fd67e9c6SThierry Reding 	err = host1x_client_suspend(&dc->client);
2132fd67e9c6SThierry Reding 	if (err < 0)
2133fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to suspend: %d\n", err);
21344ce3048cSDmitry Osipenko 
21354ce3048cSDmitry Osipenko 	if (dc->has_opp_table) {
21364ce3048cSDmitry Osipenko 		err = dev_pm_genpd_set_performance_state(dc->dev, 0);
21374ce3048cSDmitry Osipenko 		if (err)
21384ce3048cSDmitry Osipenko 			dev_err(dc->dev,
21394ce3048cSDmitry Osipenko 				"failed to clear power domain state: %d\n", err);
21404ce3048cSDmitry Osipenko 	}
2141003fc848SThierry Reding }
2142003fc848SThierry Reding 
21430b20a0f8SLaurent Pinchart static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
2144351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
2145dee8268fSThierry Reding {
21464aa3df71SThierry Reding 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
2147351f950dSMaxime Ripard 	struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
2148dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2149dbb3f2f7SThierry Reding 	u32 value;
2150fd67e9c6SThierry Reding 	int err;
2151dee8268fSThierry Reding 
21520c921b6dSDmitry Osipenko 	/* apply PLL changes */
21530c921b6dSDmitry Osipenko 	tegra_dc_set_clock_rate(dc, crtc_state);
21540c921b6dSDmitry Osipenko 
2155fd67e9c6SThierry Reding 	err = host1x_client_resume(&dc->client);
2156fd67e9c6SThierry Reding 	if (err < 0) {
2157fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to resume: %d\n", err);
2158fd67e9c6SThierry Reding 		return;
2159fd67e9c6SThierry Reding 	}
216033a8eb8dSThierry Reding 
216133a8eb8dSThierry Reding 	/* initialize display controller */
216233a8eb8dSThierry Reding 	if (dc->syncpt) {
216347307954SThierry Reding 		u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
216447307954SThierry Reding 
216547307954SThierry Reding 		if (dc->soc->has_nvdisplay)
216647307954SThierry Reding 			enable = 1 << 31;
216747307954SThierry Reding 		else
216847307954SThierry Reding 			enable = 1 << 8;
216933a8eb8dSThierry Reding 
217033a8eb8dSThierry Reding 		value = SYNCPT_CNTRL_NO_STALL;
217133a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
217233a8eb8dSThierry Reding 
217347307954SThierry Reding 		value = enable | syncpt;
217433a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
217533a8eb8dSThierry Reding 	}
217633a8eb8dSThierry Reding 
217747307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
217847307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
217947307954SThierry Reding 			DSC_OBUF_UF_INT;
218047307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
218147307954SThierry Reding 
218247307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
218347307954SThierry Reding 			DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
218447307954SThierry Reding 			HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
218547307954SThierry Reding 			REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
218647307954SThierry Reding 			VBLANK_INT | FRAME_END_INT;
218747307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
218847307954SThierry Reding 
218947307954SThierry Reding 		value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
219047307954SThierry Reding 			FRAME_END_INT;
219147307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
219247307954SThierry Reding 
219347307954SThierry Reding 		value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
219447307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
219547307954SThierry Reding 
219647307954SThierry Reding 		tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
219747307954SThierry Reding 	} else {
219833a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
219933a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
220033a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
220133a8eb8dSThierry Reding 
220233a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
220333a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
220433a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
220533a8eb8dSThierry Reding 
220633a8eb8dSThierry Reding 		/* initialize timer */
220733a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
220833a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
220933a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
221033a8eb8dSThierry Reding 
221133a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
221233a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
221333a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
221433a8eb8dSThierry Reding 
221533a8eb8dSThierry Reding 		value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
221633a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
221733a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
221833a8eb8dSThierry Reding 
221933a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
222033a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
222133a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
222247307954SThierry Reding 	}
222333a8eb8dSThierry Reding 
22247116e9a8SThierry Reding 	if (dc->soc->supports_background_color)
22257116e9a8SThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
22267116e9a8SThierry Reding 	else
222733a8eb8dSThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
222833a8eb8dSThierry Reding 
22290c921b6dSDmitry Osipenko 	/* apply pixel clock changes */
22300c921b6dSDmitry Osipenko 	if (!dc->soc->has_nvdisplay) {
22310c921b6dSDmitry Osipenko 		value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1;
22320c921b6dSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
22330c921b6dSDmitry Osipenko 	}
223476d59ed0SThierry Reding 
2235dee8268fSThierry Reding 	/* program display mode */
2236dee8268fSThierry Reding 	tegra_dc_set_timings(dc, mode);
2237dee8268fSThierry Reding 
22388620fc62SThierry Reding 	/* interlacing isn't supported yet, so disable it */
22398620fc62SThierry Reding 	if (dc->soc->supports_interlacing) {
22408620fc62SThierry Reding 		value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
22418620fc62SThierry Reding 		value &= ~INTERLACE_ENABLE;
22428620fc62SThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
22438620fc62SThierry Reding 	}
2244666cb873SThierry Reding 
2245666cb873SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
2246666cb873SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
2247666cb873SThierry Reding 	value |= DISP_CTRL_MODE_C_DISPLAY;
2248666cb873SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
2249666cb873SThierry Reding 
225047307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
2251666cb873SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2252666cb873SThierry Reding 		value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2253666cb873SThierry Reding 			 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
2254666cb873SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
225547307954SThierry Reding 	}
225647307954SThierry Reding 
225747307954SThierry Reding 	/* enable underflow reporting and display red for missing pixels */
225847307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
225947307954SThierry Reding 		value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
226047307954SThierry Reding 		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
226147307954SThierry Reding 	}
2262666cb873SThierry Reding 
2263f7d6c6aeSDmitry Osipenko 	if (dc->rgb) {
2264f7d6c6aeSDmitry Osipenko 		/* XXX: parameterize? */
2265f7d6c6aeSDmitry Osipenko 		value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
2266f7d6c6aeSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
2267f7d6c6aeSDmitry Osipenko 	}
2268f7d6c6aeSDmitry Osipenko 
2269666cb873SThierry Reding 	tegra_dc_commit(dc);
2270dee8268fSThierry Reding 
22718ff64c17SThierry Reding 	drm_crtc_vblank_on(crtc);
2272dee8268fSThierry Reding }
2273dee8268fSThierry Reding 
2274613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
2275f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22764aa3df71SThierry Reding {
22779d99ab6eSThierry Reding 	unsigned long flags;
22781503ca47SThierry Reding 
227904d5d5dfSDmitry Osipenko 	tegra_crtc_update_memory_bandwidth(crtc, state, true);
228004d5d5dfSDmitry Osipenko 
22811503ca47SThierry Reding 	if (crtc->state->event) {
22829d99ab6eSThierry Reding 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
22831503ca47SThierry Reding 
22849d99ab6eSThierry Reding 		if (drm_crtc_vblank_get(crtc) != 0)
22859d99ab6eSThierry Reding 			drm_crtc_send_vblank_event(crtc, crtc->state->event);
22869d99ab6eSThierry Reding 		else
22879d99ab6eSThierry Reding 			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
22881503ca47SThierry Reding 
22899d99ab6eSThierry Reding 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
22909d99ab6eSThierry Reding 
22911503ca47SThierry Reding 		crtc->state->event = NULL;
22921503ca47SThierry Reding 	}
22934aa3df71SThierry Reding }
22944aa3df71SThierry Reding 
2295613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
2296f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22974aa3df71SThierry Reding {
2298253f28b6SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
2299253f28b6SMaxime Ripard 									  crtc);
2300253f28b6SMaxime Ripard 	struct tegra_dc_state *dc_state = to_dc_state(crtc_state);
230147802b09SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
230247307954SThierry Reding 	u32 value;
230347802b09SThierry Reding 
2304253f28b6SMaxime Ripard 	value = dc_state->planes << 8 | GENERAL_UPDATE;
230547307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
230647307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
230747307954SThierry Reding 
2308253f28b6SMaxime Ripard 	value = dc_state->planes | GENERAL_ACT_REQ;
230947307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
231047307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
23114aa3df71SThierry Reding }
23124aa3df71SThierry Reding 
231304d5d5dfSDmitry Osipenko static bool tegra_plane_is_cursor(const struct drm_plane_state *state)
231404d5d5dfSDmitry Osipenko {
231504d5d5dfSDmitry Osipenko 	const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc;
231604d5d5dfSDmitry Osipenko 	const struct drm_format_info *fmt = state->fb->format;
231704d5d5dfSDmitry Osipenko 	unsigned int src_w = drm_rect_width(&state->src) >> 16;
231804d5d5dfSDmitry Osipenko 	unsigned int dst_w = drm_rect_width(&state->dst);
231904d5d5dfSDmitry Osipenko 
232004d5d5dfSDmitry Osipenko 	if (state->plane->type != DRM_PLANE_TYPE_CURSOR)
232104d5d5dfSDmitry Osipenko 		return false;
232204d5d5dfSDmitry Osipenko 
232304d5d5dfSDmitry Osipenko 	if (soc->supports_cursor)
232404d5d5dfSDmitry Osipenko 		return true;
232504d5d5dfSDmitry Osipenko 
232604d5d5dfSDmitry Osipenko 	if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256)
232704d5d5dfSDmitry Osipenko 		return false;
232804d5d5dfSDmitry Osipenko 
232904d5d5dfSDmitry Osipenko 	return true;
233004d5d5dfSDmitry Osipenko }
233104d5d5dfSDmitry Osipenko 
233204d5d5dfSDmitry Osipenko static unsigned long
233304d5d5dfSDmitry Osipenko tegra_plane_overlap_mask(struct drm_crtc_state *state,
233404d5d5dfSDmitry Osipenko 			 const struct drm_plane_state *plane_state)
233504d5d5dfSDmitry Osipenko {
233604d5d5dfSDmitry Osipenko 	const struct drm_plane_state *other_state;
233704d5d5dfSDmitry Osipenko 	const struct tegra_plane *tegra;
233804d5d5dfSDmitry Osipenko 	unsigned long overlap_mask = 0;
233904d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
234004d5d5dfSDmitry Osipenko 	struct drm_rect rect;
234104d5d5dfSDmitry Osipenko 
234204d5d5dfSDmitry Osipenko 	if (!plane_state->visible || !plane_state->fb)
234304d5d5dfSDmitry Osipenko 		return 0;
234404d5d5dfSDmitry Osipenko 
234504d5d5dfSDmitry Osipenko 	/*
234604d5d5dfSDmitry Osipenko 	 * Data-prefetch FIFO will easily help to overcome temporal memory
234704d5d5dfSDmitry Osipenko 	 * pressure if other plane overlaps with the cursor plane.
234804d5d5dfSDmitry Osipenko 	 */
234904d5d5dfSDmitry Osipenko 	if (tegra_plane_is_cursor(plane_state))
235004d5d5dfSDmitry Osipenko 		return 0;
235104d5d5dfSDmitry Osipenko 
235204d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) {
235304d5d5dfSDmitry Osipenko 		rect = plane_state->dst;
235404d5d5dfSDmitry Osipenko 
235504d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(other_state->plane);
235604d5d5dfSDmitry Osipenko 
235704d5d5dfSDmitry Osipenko 		if (!other_state->visible || !other_state->fb)
235804d5d5dfSDmitry Osipenko 			continue;
235904d5d5dfSDmitry Osipenko 
236004d5d5dfSDmitry Osipenko 		/*
236104d5d5dfSDmitry Osipenko 		 * Ignore cursor plane overlaps because it's not practical to
236204d5d5dfSDmitry Osipenko 		 * assume that it contributes to the bandwidth in overlapping
236304d5d5dfSDmitry Osipenko 		 * area if window width is small.
236404d5d5dfSDmitry Osipenko 		 */
236504d5d5dfSDmitry Osipenko 		if (tegra_plane_is_cursor(other_state))
236604d5d5dfSDmitry Osipenko 			continue;
236704d5d5dfSDmitry Osipenko 
236804d5d5dfSDmitry Osipenko 		if (drm_rect_intersect(&rect, &other_state->dst))
236904d5d5dfSDmitry Osipenko 			overlap_mask |= BIT(tegra->index);
237004d5d5dfSDmitry Osipenko 	}
237104d5d5dfSDmitry Osipenko 
237204d5d5dfSDmitry Osipenko 	return overlap_mask;
237304d5d5dfSDmitry Osipenko }
237404d5d5dfSDmitry Osipenko 
237504d5d5dfSDmitry Osipenko static int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc,
237604d5d5dfSDmitry Osipenko 						 struct drm_atomic_state *state)
237704d5d5dfSDmitry Osipenko {
237804d5d5dfSDmitry Osipenko 	ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask;
237904d5d5dfSDmitry Osipenko 	u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {};
238004d5d5dfSDmitry Osipenko 	bool all_planes_overlap_simultaneously = true;
238104d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *tegra_state;
238204d5d5dfSDmitry Osipenko 	const struct drm_plane_state *plane_state;
238304d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
238404d5d5dfSDmitry Osipenko 	const struct drm_crtc_state *old_state;
238504d5d5dfSDmitry Osipenko 	struct drm_crtc_state *new_state;
238604d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
238704d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
238804d5d5dfSDmitry Osipenko 
238904d5d5dfSDmitry Osipenko 	/*
239004d5d5dfSDmitry Osipenko 	 * The nv-display uses shared planes.  The algorithm below assumes
239104d5d5dfSDmitry Osipenko 	 * maximum 3 planes per-CRTC, this assumption isn't applicable to
239204d5d5dfSDmitry Osipenko 	 * the nv-display.  Note that T124 support has additional windows,
239304d5d5dfSDmitry Osipenko 	 * but currently they aren't supported by the driver.
239404d5d5dfSDmitry Osipenko 	 */
239504d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
239604d5d5dfSDmitry Osipenko 		return 0;
239704d5d5dfSDmitry Osipenko 
239804d5d5dfSDmitry Osipenko 	new_state = drm_atomic_get_new_crtc_state(state, crtc);
239904d5d5dfSDmitry Osipenko 	old_state = drm_atomic_get_old_crtc_state(state, crtc);
240004d5d5dfSDmitry Osipenko 
240104d5d5dfSDmitry Osipenko 	/*
240204d5d5dfSDmitry Osipenko 	 * For overlapping planes pixel's data is fetched for each plane at
240304d5d5dfSDmitry Osipenko 	 * the same time, hence bandwidths are accumulated in this case.
240404d5d5dfSDmitry Osipenko 	 * This needs to be taken into account for calculating total bandwidth
240504d5d5dfSDmitry Osipenko 	 * consumed by all planes.
240604d5d5dfSDmitry Osipenko 	 *
240704d5d5dfSDmitry Osipenko 	 * Here we get the overlapping state of each plane, which is a
240804d5d5dfSDmitry Osipenko 	 * bitmask of plane indices telling with what planes there is an
240904d5d5dfSDmitry Osipenko 	 * overlap. Note that bitmask[plane] includes BIT(plane) in order
241004d5d5dfSDmitry Osipenko 	 * to make further code nicer and simpler.
241104d5d5dfSDmitry Osipenko 	 */
241204d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
241304d5d5dfSDmitry Osipenko 		tegra_state = to_const_tegra_plane_state(plane_state);
241404d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
241504d5d5dfSDmitry Osipenko 
241604d5d5dfSDmitry Osipenko 		if (WARN_ON_ONCE(tegra->index >= TEGRA_DC_LEGACY_PLANES_NUM))
241704d5d5dfSDmitry Osipenko 			return -EINVAL;
241804d5d5dfSDmitry Osipenko 
241904d5d5dfSDmitry Osipenko 		plane_peak_bw[tegra->index] = tegra_state->peak_memory_bandwidth;
242004d5d5dfSDmitry Osipenko 		mask = tegra_plane_overlap_mask(new_state, plane_state);
242104d5d5dfSDmitry Osipenko 		overlap_mask[tegra->index] = mask;
242204d5d5dfSDmitry Osipenko 
242304d5d5dfSDmitry Osipenko 		if (hweight_long(mask) != 3)
242404d5d5dfSDmitry Osipenko 			all_planes_overlap_simultaneously = false;
242504d5d5dfSDmitry Osipenko 	}
242604d5d5dfSDmitry Osipenko 
242704d5d5dfSDmitry Osipenko 	/*
242804d5d5dfSDmitry Osipenko 	 * Then we calculate maximum bandwidth of each plane state.
242904d5d5dfSDmitry Osipenko 	 * The bandwidth includes the plane BW + BW of the "simultaneously"
243004d5d5dfSDmitry Osipenko 	 * overlapping planes, where "simultaneously" means areas where DC
243104d5d5dfSDmitry Osipenko 	 * fetches from the planes simultaneously during of scan-out process.
243204d5d5dfSDmitry Osipenko 	 *
243304d5d5dfSDmitry Osipenko 	 * For example, if plane A overlaps with planes B and C, but B and C
243404d5d5dfSDmitry Osipenko 	 * don't overlap, then the peak bandwidth will be either in area where
243504d5d5dfSDmitry Osipenko 	 * A-and-B or A-and-C planes overlap.
243604d5d5dfSDmitry Osipenko 	 *
243704d5d5dfSDmitry Osipenko 	 * The plane_peak_bw[] contains peak memory bandwidth values of
243804d5d5dfSDmitry Osipenko 	 * each plane, this information is needed by interconnect provider
243904d5d5dfSDmitry Osipenko 	 * in order to set up latency allowance based on the peak BW, see
244004d5d5dfSDmitry Osipenko 	 * tegra_crtc_update_memory_bandwidth().
244104d5d5dfSDmitry Osipenko 	 */
244204d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
244304d5d5dfSDmitry Osipenko 		u32 i, old_peak_bw, new_peak_bw, overlap_bw = 0;
244404d5d5dfSDmitry Osipenko 
244504d5d5dfSDmitry Osipenko 		/*
244604d5d5dfSDmitry Osipenko 		 * Note that plane's atomic check doesn't touch the
244704d5d5dfSDmitry Osipenko 		 * total_peak_memory_bandwidth of enabled plane, hence the
244804d5d5dfSDmitry Osipenko 		 * current state contains the old bandwidth state from the
244904d5d5dfSDmitry Osipenko 		 * previous CRTC commit.
245004d5d5dfSDmitry Osipenko 		 */
245104d5d5dfSDmitry Osipenko 		tegra_state = to_const_tegra_plane_state(plane_state);
245204d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
245304d5d5dfSDmitry Osipenko 
245404d5d5dfSDmitry Osipenko 		for_each_set_bit(i, &overlap_mask[tegra->index], 3) {
245504d5d5dfSDmitry Osipenko 			if (i == tegra->index)
245604d5d5dfSDmitry Osipenko 				continue;
245704d5d5dfSDmitry Osipenko 
245804d5d5dfSDmitry Osipenko 			if (all_planes_overlap_simultaneously)
245904d5d5dfSDmitry Osipenko 				overlap_bw += plane_peak_bw[i];
246004d5d5dfSDmitry Osipenko 			else
246104d5d5dfSDmitry Osipenko 				overlap_bw = max(overlap_bw, plane_peak_bw[i]);
246204d5d5dfSDmitry Osipenko 		}
246304d5d5dfSDmitry Osipenko 
246404d5d5dfSDmitry Osipenko 		new_peak_bw = plane_peak_bw[tegra->index] + overlap_bw;
246504d5d5dfSDmitry Osipenko 		old_peak_bw = tegra_state->total_peak_memory_bandwidth;
246604d5d5dfSDmitry Osipenko 
246704d5d5dfSDmitry Osipenko 		/*
246804d5d5dfSDmitry Osipenko 		 * If plane's peak bandwidth changed (for example plane isn't
246904d5d5dfSDmitry Osipenko 		 * overlapped anymore) and plane isn't in the atomic state,
247004d5d5dfSDmitry Osipenko 		 * then add plane to the state in order to have the bandwidth
247104d5d5dfSDmitry Osipenko 		 * updated.
247204d5d5dfSDmitry Osipenko 		 */
247304d5d5dfSDmitry Osipenko 		if (old_peak_bw != new_peak_bw) {
247404d5d5dfSDmitry Osipenko 			struct tegra_plane_state *new_tegra_state;
247504d5d5dfSDmitry Osipenko 			struct drm_plane_state *new_plane_state;
247604d5d5dfSDmitry Osipenko 
247704d5d5dfSDmitry Osipenko 			new_plane_state = drm_atomic_get_plane_state(state, plane);
247804d5d5dfSDmitry Osipenko 			if (IS_ERR(new_plane_state))
247904d5d5dfSDmitry Osipenko 				return PTR_ERR(new_plane_state);
248004d5d5dfSDmitry Osipenko 
248104d5d5dfSDmitry Osipenko 			new_tegra_state = to_tegra_plane_state(new_plane_state);
248204d5d5dfSDmitry Osipenko 			new_tegra_state->total_peak_memory_bandwidth = new_peak_bw;
248304d5d5dfSDmitry Osipenko 		}
248404d5d5dfSDmitry Osipenko 	}
248504d5d5dfSDmitry Osipenko 
248604d5d5dfSDmitry Osipenko 	return 0;
248704d5d5dfSDmitry Osipenko }
248804d5d5dfSDmitry Osipenko 
248904d5d5dfSDmitry Osipenko static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
249004d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state)
249104d5d5dfSDmitry Osipenko {
249204d5d5dfSDmitry Osipenko 	int err;
249304d5d5dfSDmitry Osipenko 
249404d5d5dfSDmitry Osipenko 	err = tegra_crtc_calculate_memory_bandwidth(crtc, state);
249504d5d5dfSDmitry Osipenko 	if (err)
249604d5d5dfSDmitry Osipenko 		return err;
249704d5d5dfSDmitry Osipenko 
249804d5d5dfSDmitry Osipenko 	return 0;
249904d5d5dfSDmitry Osipenko }
250004d5d5dfSDmitry Osipenko 
250104d5d5dfSDmitry Osipenko void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc,
250204d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state)
250304d5d5dfSDmitry Osipenko {
250404d5d5dfSDmitry Osipenko 	/*
250504d5d5dfSDmitry Osipenko 	 * Display bandwidth is allowed to go down only once hardware state
250604d5d5dfSDmitry Osipenko 	 * is known to be armed, i.e. state was committed and VBLANK event
250704d5d5dfSDmitry Osipenko 	 * received.
250804d5d5dfSDmitry Osipenko 	 */
250904d5d5dfSDmitry Osipenko 	tegra_crtc_update_memory_bandwidth(crtc, state, false);
251004d5d5dfSDmitry Osipenko }
251104d5d5dfSDmitry Osipenko 
2512dee8268fSThierry Reding static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
251304d5d5dfSDmitry Osipenko 	.atomic_check = tegra_crtc_atomic_check,
25144aa3df71SThierry Reding 	.atomic_begin = tegra_crtc_atomic_begin,
25154aa3df71SThierry Reding 	.atomic_flush = tegra_crtc_atomic_flush,
25160b20a0f8SLaurent Pinchart 	.atomic_enable = tegra_crtc_atomic_enable,
251764581714SLaurent Pinchart 	.atomic_disable = tegra_crtc_atomic_disable,
2518dee8268fSThierry Reding };
2519dee8268fSThierry Reding 
2520dee8268fSThierry Reding static irqreturn_t tegra_dc_irq(int irq, void *data)
2521dee8268fSThierry Reding {
2522dee8268fSThierry Reding 	struct tegra_dc *dc = data;
2523dee8268fSThierry Reding 	unsigned long status;
2524dee8268fSThierry Reding 
2525dee8268fSThierry Reding 	status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
2526dee8268fSThierry Reding 	tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
2527dee8268fSThierry Reding 
2528dee8268fSThierry Reding 	if (status & FRAME_END_INT) {
2529dee8268fSThierry Reding 		/*
2530dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): frame end\n", __func__);
2531dee8268fSThierry Reding 		*/
2532ad85b084SDmitry Osipenko 		dc->stats.frames_total++;
2533791ddb1eSThierry Reding 		dc->stats.frames++;
2534dee8268fSThierry Reding 	}
2535dee8268fSThierry Reding 
2536dee8268fSThierry Reding 	if (status & VBLANK_INT) {
2537dee8268fSThierry Reding 		/*
2538dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
2539dee8268fSThierry Reding 		*/
2540ed7dae58SThierry Reding 		drm_crtc_handle_vblank(&dc->base);
2541ad85b084SDmitry Osipenko 		dc->stats.vblank_total++;
2542791ddb1eSThierry Reding 		dc->stats.vblank++;
2543dee8268fSThierry Reding 	}
2544dee8268fSThierry Reding 
2545dee8268fSThierry Reding 	if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
2546dee8268fSThierry Reding 		/*
2547dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): underflow\n", __func__);
2548dee8268fSThierry Reding 		*/
2549ad85b084SDmitry Osipenko 		dc->stats.underflow_total++;
2550791ddb1eSThierry Reding 		dc->stats.underflow++;
2551791ddb1eSThierry Reding 	}
2552791ddb1eSThierry Reding 
2553791ddb1eSThierry Reding 	if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) {
2554791ddb1eSThierry Reding 		/*
2555791ddb1eSThierry Reding 		dev_dbg(dc->dev, "%s(): overflow\n", __func__);
2556791ddb1eSThierry Reding 		*/
2557ad85b084SDmitry Osipenko 		dc->stats.overflow_total++;
2558791ddb1eSThierry Reding 		dc->stats.overflow++;
2559dee8268fSThierry Reding 	}
2560dee8268fSThierry Reding 
256147307954SThierry Reding 	if (status & HEAD_UF_INT) {
256247307954SThierry Reding 		dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
2563ad85b084SDmitry Osipenko 		dc->stats.underflow_total++;
256447307954SThierry Reding 		dc->stats.underflow++;
256547307954SThierry Reding 	}
256647307954SThierry Reding 
2567dee8268fSThierry Reding 	return IRQ_HANDLED;
2568dee8268fSThierry Reding }
2569dee8268fSThierry Reding 
2570e75d0477SThierry Reding static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
2571e75d0477SThierry Reding {
2572e75d0477SThierry Reding 	unsigned int i;
2573e75d0477SThierry Reding 
2574e75d0477SThierry Reding 	if (!dc->soc->wgrps)
2575e75d0477SThierry Reding 		return true;
2576e75d0477SThierry Reding 
2577e75d0477SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
2578e75d0477SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
2579e75d0477SThierry Reding 
2580e75d0477SThierry Reding 		if (wgrp->dc == dc->pipe && wgrp->num_windows > 0)
2581e75d0477SThierry Reding 			return true;
2582e75d0477SThierry Reding 	}
2583e75d0477SThierry Reding 
2584e75d0477SThierry Reding 	return false;
2585e75d0477SThierry Reding }
2586e75d0477SThierry Reding 
258705d1adfeSThierry Reding static int tegra_dc_early_init(struct host1x_client *client)
258805d1adfeSThierry Reding {
258905d1adfeSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
259005d1adfeSThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
259105d1adfeSThierry Reding 
259205d1adfeSThierry Reding 	tegra->num_crtcs++;
259305d1adfeSThierry Reding 
259405d1adfeSThierry Reding 	return 0;
259505d1adfeSThierry Reding }
259605d1adfeSThierry Reding 
2597dee8268fSThierry Reding static int tegra_dc_init(struct host1x_client *client)
2598dee8268fSThierry Reding {
2599608f43adSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
26002bcdcbfaSThierry Reding 	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
2601dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2602d1f3e1e0SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
2603c7679306SThierry Reding 	struct drm_plane *primary = NULL;
2604c7679306SThierry Reding 	struct drm_plane *cursor = NULL;
2605dee8268fSThierry Reding 	int err;
2606dee8268fSThierry Reding 
2607759d706fSThierry Reding 	/*
2608f5ba33fbSMikko Perttunen 	 * DC has been reset by now, so VBLANK syncpoint can be released
2609f5ba33fbSMikko Perttunen 	 * for general use.
2610f5ba33fbSMikko Perttunen 	 */
2611f5ba33fbSMikko Perttunen 	host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);
2612f5ba33fbSMikko Perttunen 
2613f5ba33fbSMikko Perttunen 	/*
2614759d706fSThierry Reding 	 * XXX do not register DCs with no window groups because we cannot
2615759d706fSThierry Reding 	 * assign a primary plane to them, which in turn will cause KMS to
2616759d706fSThierry Reding 	 * crash.
2617759d706fSThierry Reding 	 */
2618e75d0477SThierry Reding 	if (!tegra_dc_has_window_groups(dc))
2619759d706fSThierry Reding 		return 0;
2620759d706fSThierry Reding 
2621fd67e9c6SThierry Reding 	/*
2622fd67e9c6SThierry Reding 	 * Set the display hub as the host1x client parent for the display
2623fd67e9c6SThierry Reding 	 * controller. This is needed for the runtime reference counting that
2624fd67e9c6SThierry Reding 	 * ensures the display hub is always powered when any of the display
2625fd67e9c6SThierry Reding 	 * controllers are.
2626fd67e9c6SThierry Reding 	 */
2627fd67e9c6SThierry Reding 	if (dc->soc->has_nvdisplay)
2628fd67e9c6SThierry Reding 		client->parent = &tegra->hub->client;
2629fd67e9c6SThierry Reding 
2630617dd7ccSThierry Reding 	dc->syncpt = host1x_syncpt_request(client, flags);
26312bcdcbfaSThierry Reding 	if (!dc->syncpt)
26322bcdcbfaSThierry Reding 		dev_warn(dc->dev, "failed to allocate syncpoint\n");
26332bcdcbfaSThierry Reding 
26347edd7961SThierry Reding 	err = host1x_client_iommu_attach(client);
2635a8817489SThierry Reding 	if (err < 0 && err != -ENODEV) {
26360c407de5SThierry Reding 		dev_err(client->dev, "failed to attach to domain: %d\n", err);
2637df06b759SThierry Reding 		return err;
2638df06b759SThierry Reding 	}
2639df06b759SThierry Reding 
264047307954SThierry Reding 	if (dc->soc->wgrps)
264147307954SThierry Reding 		primary = tegra_dc_add_shared_planes(drm, dc);
264247307954SThierry Reding 	else
264347307954SThierry Reding 		primary = tegra_dc_add_planes(drm, dc);
264447307954SThierry Reding 
2645c7679306SThierry Reding 	if (IS_ERR(primary)) {
2646c7679306SThierry Reding 		err = PTR_ERR(primary);
2647c7679306SThierry Reding 		goto cleanup;
2648c7679306SThierry Reding 	}
2649c7679306SThierry Reding 
2650c7679306SThierry Reding 	if (dc->soc->supports_cursor) {
2651c7679306SThierry Reding 		cursor = tegra_dc_cursor_plane_create(drm, dc);
2652c7679306SThierry Reding 		if (IS_ERR(cursor)) {
2653c7679306SThierry Reding 			err = PTR_ERR(cursor);
2654c7679306SThierry Reding 			goto cleanup;
2655c7679306SThierry Reding 		}
26569f446d83SDmitry Osipenko 	} else {
26579f446d83SDmitry Osipenko 		/* dedicate one overlay to mouse cursor */
26589f446d83SDmitry Osipenko 		cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true);
26599f446d83SDmitry Osipenko 		if (IS_ERR(cursor)) {
26609f446d83SDmitry Osipenko 			err = PTR_ERR(cursor);
26619f446d83SDmitry Osipenko 			goto cleanup;
26629f446d83SDmitry Osipenko 		}
2663c7679306SThierry Reding 	}
2664c7679306SThierry Reding 
2665c7679306SThierry Reding 	err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
2666f9882876SVille Syrjälä 					&tegra_crtc_funcs, NULL);
2667c7679306SThierry Reding 	if (err < 0)
2668c7679306SThierry Reding 		goto cleanup;
2669c7679306SThierry Reding 
2670dee8268fSThierry Reding 	drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
2671dee8268fSThierry Reding 
2672d1f3e1e0SThierry Reding 	/*
2673d1f3e1e0SThierry Reding 	 * Keep track of the minimum pitch alignment across all display
2674d1f3e1e0SThierry Reding 	 * controllers.
2675d1f3e1e0SThierry Reding 	 */
2676d1f3e1e0SThierry Reding 	if (dc->soc->pitch_align > tegra->pitch_align)
2677d1f3e1e0SThierry Reding 		tegra->pitch_align = dc->soc->pitch_align;
2678d1f3e1e0SThierry Reding 
2679042c0bd7SThierry Reding 	/* track maximum resolution */
2680042c0bd7SThierry Reding 	if (dc->soc->has_nvdisplay)
2681042c0bd7SThierry Reding 		drm->mode_config.max_width = drm->mode_config.max_height = 16384;
2682042c0bd7SThierry Reding 	else
2683042c0bd7SThierry Reding 		drm->mode_config.max_width = drm->mode_config.max_height = 4096;
2684042c0bd7SThierry Reding 
26859910f5c4SThierry Reding 	err = tegra_dc_rgb_init(drm, dc);
2686dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
2687dee8268fSThierry Reding 		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
2688c7679306SThierry Reding 		goto cleanup;
2689dee8268fSThierry Reding 	}
2690dee8268fSThierry Reding 
2691dee8268fSThierry Reding 	err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
2692dee8268fSThierry Reding 			       dev_name(dc->dev), dc);
2693dee8268fSThierry Reding 	if (err < 0) {
2694dee8268fSThierry Reding 		dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
2695dee8268fSThierry Reding 			err);
2696c7679306SThierry Reding 		goto cleanup;
2697dee8268fSThierry Reding 	}
2698dee8268fSThierry Reding 
269947b15779SThierry Reding 	/*
270047b15779SThierry Reding 	 * Inherit the DMA parameters (such as maximum segment size) from the
2701608f43adSThierry Reding 	 * parent host1x device.
270247b15779SThierry Reding 	 */
2703608f43adSThierry Reding 	client->dev->dma_parms = client->host->dma_parms;
270447b15779SThierry Reding 
2705dee8268fSThierry Reding 	return 0;
2706c7679306SThierry Reding 
2707c7679306SThierry Reding cleanup:
270847307954SThierry Reding 	if (!IS_ERR_OR_NULL(cursor))
2709c7679306SThierry Reding 		drm_plane_cleanup(cursor);
2710c7679306SThierry Reding 
271147307954SThierry Reding 	if (!IS_ERR(primary))
2712c7679306SThierry Reding 		drm_plane_cleanup(primary);
2713c7679306SThierry Reding 
2714aacdf198SThierry Reding 	host1x_client_iommu_detach(client);
27152aed4f5aSMikko Perttunen 	host1x_syncpt_put(dc->syncpt);
2716fd5ec0dcSThierry Reding 
2717c7679306SThierry Reding 	return err;
2718dee8268fSThierry Reding }
2719dee8268fSThierry Reding 
2720dee8268fSThierry Reding static int tegra_dc_exit(struct host1x_client *client)
2721dee8268fSThierry Reding {
2722dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2723dee8268fSThierry Reding 	int err;
2724dee8268fSThierry Reding 
2725e75d0477SThierry Reding 	if (!tegra_dc_has_window_groups(dc))
2726e75d0477SThierry Reding 		return 0;
2727e75d0477SThierry Reding 
272847b15779SThierry Reding 	/* avoid a dangling pointer just in case this disappears */
272947b15779SThierry Reding 	client->dev->dma_parms = NULL;
273047b15779SThierry Reding 
2731dee8268fSThierry Reding 	devm_free_irq(dc->dev, dc->irq, dc);
2732dee8268fSThierry Reding 
2733dee8268fSThierry Reding 	err = tegra_dc_rgb_exit(dc);
2734dee8268fSThierry Reding 	if (err) {
2735dee8268fSThierry Reding 		dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err);
2736dee8268fSThierry Reding 		return err;
2737dee8268fSThierry Reding 	}
2738dee8268fSThierry Reding 
2739aacdf198SThierry Reding 	host1x_client_iommu_detach(client);
27402aed4f5aSMikko Perttunen 	host1x_syncpt_put(dc->syncpt);
27412bcdcbfaSThierry Reding 
2742dee8268fSThierry Reding 	return 0;
2743dee8268fSThierry Reding }
2744dee8268fSThierry Reding 
274505d1adfeSThierry Reding static int tegra_dc_late_exit(struct host1x_client *client)
274605d1adfeSThierry Reding {
274705d1adfeSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
274805d1adfeSThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
274905d1adfeSThierry Reding 
275005d1adfeSThierry Reding 	tegra->num_crtcs--;
2751dee8268fSThierry Reding 
2752dee8268fSThierry Reding 	return 0;
2753dee8268fSThierry Reding }
2754dee8268fSThierry Reding 
2755fd67e9c6SThierry Reding static int tegra_dc_runtime_suspend(struct host1x_client *client)
2756fd67e9c6SThierry Reding {
2757fd67e9c6SThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2758fd67e9c6SThierry Reding 	struct device *dev = client->dev;
2759fd67e9c6SThierry Reding 	int err;
2760fd67e9c6SThierry Reding 
2761fd67e9c6SThierry Reding 	err = reset_control_assert(dc->rst);
2762fd67e9c6SThierry Reding 	if (err < 0) {
2763fd67e9c6SThierry Reding 		dev_err(dev, "failed to assert reset: %d\n", err);
2764fd67e9c6SThierry Reding 		return err;
2765fd67e9c6SThierry Reding 	}
2766fd67e9c6SThierry Reding 
2767fd67e9c6SThierry Reding 	if (dc->soc->has_powergate)
2768fd67e9c6SThierry Reding 		tegra_powergate_power_off(dc->powergate);
2769fd67e9c6SThierry Reding 
2770fd67e9c6SThierry Reding 	clk_disable_unprepare(dc->clk);
2771fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
2772fd67e9c6SThierry Reding 
2773fd67e9c6SThierry Reding 	return 0;
2774fd67e9c6SThierry Reding }
2775fd67e9c6SThierry Reding 
2776fd67e9c6SThierry Reding static int tegra_dc_runtime_resume(struct host1x_client *client)
2777fd67e9c6SThierry Reding {
2778fd67e9c6SThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2779fd67e9c6SThierry Reding 	struct device *dev = client->dev;
2780fd67e9c6SThierry Reding 	int err;
2781fd67e9c6SThierry Reding 
2782dcdfe271SQinglang Miao 	err = pm_runtime_resume_and_get(dev);
2783fd67e9c6SThierry Reding 	if (err < 0) {
2784fd67e9c6SThierry Reding 		dev_err(dev, "failed to get runtime PM: %d\n", err);
2785fd67e9c6SThierry Reding 		return err;
2786fd67e9c6SThierry Reding 	}
2787fd67e9c6SThierry Reding 
2788fd67e9c6SThierry Reding 	if (dc->soc->has_powergate) {
2789fd67e9c6SThierry Reding 		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
2790fd67e9c6SThierry Reding 							dc->rst);
2791fd67e9c6SThierry Reding 		if (err < 0) {
2792fd67e9c6SThierry Reding 			dev_err(dev, "failed to power partition: %d\n", err);
2793fd67e9c6SThierry Reding 			goto put_rpm;
2794fd67e9c6SThierry Reding 		}
2795fd67e9c6SThierry Reding 	} else {
2796fd67e9c6SThierry Reding 		err = clk_prepare_enable(dc->clk);
2797fd67e9c6SThierry Reding 		if (err < 0) {
2798fd67e9c6SThierry Reding 			dev_err(dev, "failed to enable clock: %d\n", err);
2799fd67e9c6SThierry Reding 			goto put_rpm;
2800fd67e9c6SThierry Reding 		}
2801fd67e9c6SThierry Reding 
2802fd67e9c6SThierry Reding 		err = reset_control_deassert(dc->rst);
2803fd67e9c6SThierry Reding 		if (err < 0) {
2804fd67e9c6SThierry Reding 			dev_err(dev, "failed to deassert reset: %d\n", err);
2805fd67e9c6SThierry Reding 			goto disable_clk;
2806fd67e9c6SThierry Reding 		}
2807fd67e9c6SThierry Reding 	}
2808fd67e9c6SThierry Reding 
2809fd67e9c6SThierry Reding 	return 0;
2810fd67e9c6SThierry Reding 
2811fd67e9c6SThierry Reding disable_clk:
2812fd67e9c6SThierry Reding 	clk_disable_unprepare(dc->clk);
2813fd67e9c6SThierry Reding put_rpm:
2814fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
2815fd67e9c6SThierry Reding 	return err;
2816fd67e9c6SThierry Reding }
2817fd67e9c6SThierry Reding 
2818dee8268fSThierry Reding static const struct host1x_client_ops dc_client_ops = {
281905d1adfeSThierry Reding 	.early_init = tegra_dc_early_init,
2820dee8268fSThierry Reding 	.init = tegra_dc_init,
2821dee8268fSThierry Reding 	.exit = tegra_dc_exit,
282205d1adfeSThierry Reding 	.late_exit = tegra_dc_late_exit,
2823fd67e9c6SThierry Reding 	.suspend = tegra_dc_runtime_suspend,
2824fd67e9c6SThierry Reding 	.resume = tegra_dc_runtime_resume,
2825dee8268fSThierry Reding };
2826dee8268fSThierry Reding 
28278620fc62SThierry Reding static const struct tegra_dc_soc_info tegra20_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 = true,
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 = true,
284404d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = true,
2845acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = true,
284604d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
28470c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
28488620fc62SThierry Reding };
28498620fc62SThierry Reding 
28508620fc62SThierry Reding static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
28517116e9a8SThierry Reding 	.supports_background_color = false,
28528620fc62SThierry Reding 	.supports_interlacing = false,
2853e687651bSThierry Reding 	.supports_cursor = false,
2854c134f019SThierry Reding 	.supports_block_linear = false,
28557b6f8467SThierry Reding 	.supports_sector_layout = false,
2856a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
2857d1f3e1e0SThierry Reding 	.pitch_align = 8,
28589c012700SThierry Reding 	.has_powergate = false,
2859f68ba691SDmitry Osipenko 	.coupled_pm = false,
286047307954SThierry Reding 	.has_nvdisplay = false,
2861511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
2862511c7023SThierry Reding 	.primary_formats = tegra20_primary_formats,
2863511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
2864511c7023SThierry Reding 	.overlay_formats = tegra20_overlay_formats,
2865e90124cbSThierry Reding 	.modifiers = tegra20_modifiers,
2866acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
286704d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = true,
2868acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
286904d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = true,
28700c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
2871d1f3e1e0SThierry Reding };
2872d1f3e1e0SThierry Reding 
2873d1f3e1e0SThierry Reding static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
28747116e9a8SThierry Reding 	.supports_background_color = false,
2875d1f3e1e0SThierry Reding 	.supports_interlacing = false,
2876d1f3e1e0SThierry Reding 	.supports_cursor = false,
2877d1f3e1e0SThierry Reding 	.supports_block_linear = false,
28787b6f8467SThierry Reding 	.supports_sector_layout = false,
2879a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
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(tegra114_primary_formats),
2885511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
2886511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
2887511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
2888e90124cbSThierry Reding 	.modifiers = tegra20_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 = true,
28930c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
28948620fc62SThierry Reding };
28958620fc62SThierry Reding 
28968620fc62SThierry Reding static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
28977116e9a8SThierry Reding 	.supports_background_color = true,
28988620fc62SThierry Reding 	.supports_interlacing = true,
2899e687651bSThierry Reding 	.supports_cursor = true,
2900c134f019SThierry Reding 	.supports_block_linear = true,
29017b6f8467SThierry Reding 	.supports_sector_layout = false,
2902a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
2903d1f3e1e0SThierry Reding 	.pitch_align = 64,
29049c012700SThierry Reding 	.has_powergate = true,
2905f68ba691SDmitry Osipenko 	.coupled_pm = false,
290647307954SThierry Reding 	.has_nvdisplay = false,
2907511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra124_primary_formats),
29089a02d3afSStefan Agner 	.primary_formats = tegra124_primary_formats,
2909511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
29109a02d3afSStefan Agner 	.overlay_formats = tegra124_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,
29178620fc62SThierry Reding };
29188620fc62SThierry Reding 
29195b4f516fSThierry Reding static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
29207116e9a8SThierry Reding 	.supports_background_color = true,
29215b4f516fSThierry Reding 	.supports_interlacing = true,
29225b4f516fSThierry Reding 	.supports_cursor = true,
29235b4f516fSThierry Reding 	.supports_block_linear = true,
29247b6f8467SThierry Reding 	.supports_sector_layout = false,
2925a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
29265b4f516fSThierry Reding 	.pitch_align = 64,
29275b4f516fSThierry Reding 	.has_powergate = true,
2928f68ba691SDmitry Osipenko 	.coupled_pm = false,
292947307954SThierry Reding 	.has_nvdisplay = false,
2930511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
2931511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
2932511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
2933511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
2934e90124cbSThierry Reding 	.modifiers = tegra124_modifiers,
2935acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
293604d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = false,
2937acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
293804d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
29390c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
294047307954SThierry Reding };
294147307954SThierry Reding 
294247307954SThierry Reding static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
294347307954SThierry Reding 	{
294447307954SThierry Reding 		.index = 0,
294547307954SThierry Reding 		.dc = 0,
294647307954SThierry Reding 		.windows = (const unsigned int[]) { 0 },
294747307954SThierry Reding 		.num_windows = 1,
294847307954SThierry Reding 	}, {
294947307954SThierry Reding 		.index = 1,
295047307954SThierry Reding 		.dc = 1,
295147307954SThierry Reding 		.windows = (const unsigned int[]) { 1 },
295247307954SThierry Reding 		.num_windows = 1,
295347307954SThierry Reding 	}, {
295447307954SThierry Reding 		.index = 2,
295547307954SThierry Reding 		.dc = 1,
295647307954SThierry Reding 		.windows = (const unsigned int[]) { 2 },
295747307954SThierry Reding 		.num_windows = 1,
295847307954SThierry Reding 	}, {
295947307954SThierry Reding 		.index = 3,
296047307954SThierry Reding 		.dc = 2,
296147307954SThierry Reding 		.windows = (const unsigned int[]) { 3 },
296247307954SThierry Reding 		.num_windows = 1,
296347307954SThierry Reding 	}, {
296447307954SThierry Reding 		.index = 4,
296547307954SThierry Reding 		.dc = 2,
296647307954SThierry Reding 		.windows = (const unsigned int[]) { 4 },
296747307954SThierry Reding 		.num_windows = 1,
296847307954SThierry Reding 	}, {
296947307954SThierry Reding 		.index = 5,
297047307954SThierry Reding 		.dc = 2,
297147307954SThierry Reding 		.windows = (const unsigned int[]) { 5 },
297247307954SThierry Reding 		.num_windows = 1,
297347307954SThierry Reding 	},
297447307954SThierry Reding };
297547307954SThierry Reding 
297647307954SThierry Reding static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
297747307954SThierry Reding 	.supports_background_color = true,
297847307954SThierry Reding 	.supports_interlacing = true,
297947307954SThierry Reding 	.supports_cursor = true,
298047307954SThierry Reding 	.supports_block_linear = true,
29817b6f8467SThierry Reding 	.supports_sector_layout = false,
2982a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
298347307954SThierry Reding 	.pitch_align = 64,
298447307954SThierry Reding 	.has_powergate = false,
2985f68ba691SDmitry Osipenko 	.coupled_pm = false,
298647307954SThierry Reding 	.has_nvdisplay = true,
298747307954SThierry Reding 	.wgrps = tegra186_dc_wgrps,
298847307954SThierry Reding 	.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
298904d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
29900c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
29915b4f516fSThierry Reding };
29925b4f516fSThierry Reding 
299347443196SThierry Reding static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
299447443196SThierry Reding 	{
299547443196SThierry Reding 		.index = 0,
299647443196SThierry Reding 		.dc = 0,
299747443196SThierry Reding 		.windows = (const unsigned int[]) { 0 },
299847443196SThierry Reding 		.num_windows = 1,
299947443196SThierry Reding 	}, {
300047443196SThierry Reding 		.index = 1,
300147443196SThierry Reding 		.dc = 1,
300247443196SThierry Reding 		.windows = (const unsigned int[]) { 1 },
300347443196SThierry Reding 		.num_windows = 1,
300447443196SThierry Reding 	}, {
300547443196SThierry Reding 		.index = 2,
300647443196SThierry Reding 		.dc = 1,
300747443196SThierry Reding 		.windows = (const unsigned int[]) { 2 },
300847443196SThierry Reding 		.num_windows = 1,
300947443196SThierry Reding 	}, {
301047443196SThierry Reding 		.index = 3,
301147443196SThierry Reding 		.dc = 2,
301247443196SThierry Reding 		.windows = (const unsigned int[]) { 3 },
301347443196SThierry Reding 		.num_windows = 1,
301447443196SThierry Reding 	}, {
301547443196SThierry Reding 		.index = 4,
301647443196SThierry Reding 		.dc = 2,
301747443196SThierry Reding 		.windows = (const unsigned int[]) { 4 },
301847443196SThierry Reding 		.num_windows = 1,
301947443196SThierry Reding 	}, {
302047443196SThierry Reding 		.index = 5,
302147443196SThierry Reding 		.dc = 2,
302247443196SThierry Reding 		.windows = (const unsigned int[]) { 5 },
302347443196SThierry Reding 		.num_windows = 1,
302447443196SThierry Reding 	},
302547443196SThierry Reding };
302647443196SThierry Reding 
302747443196SThierry Reding static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
302847443196SThierry Reding 	.supports_background_color = true,
302947443196SThierry Reding 	.supports_interlacing = true,
303047443196SThierry Reding 	.supports_cursor = true,
303147443196SThierry Reding 	.supports_block_linear = true,
30327b6f8467SThierry Reding 	.supports_sector_layout = true,
303347443196SThierry Reding 	.has_legacy_blending = false,
303447443196SThierry Reding 	.pitch_align = 64,
303547443196SThierry Reding 	.has_powergate = false,
303647443196SThierry Reding 	.coupled_pm = false,
303747443196SThierry Reding 	.has_nvdisplay = true,
303847443196SThierry Reding 	.wgrps = tegra194_dc_wgrps,
303947443196SThierry Reding 	.num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
304004d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
30410c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
304247443196SThierry Reding };
304347443196SThierry Reding 
30448620fc62SThierry Reding static const struct of_device_id tegra_dc_of_match[] = {
30458620fc62SThierry Reding 	{
304647443196SThierry Reding 		.compatible = "nvidia,tegra194-dc",
304747443196SThierry Reding 		.data = &tegra194_dc_soc_info,
304847443196SThierry Reding 	}, {
304947307954SThierry Reding 		.compatible = "nvidia,tegra186-dc",
305047307954SThierry Reding 		.data = &tegra186_dc_soc_info,
305147307954SThierry Reding 	}, {
30525b4f516fSThierry Reding 		.compatible = "nvidia,tegra210-dc",
30535b4f516fSThierry Reding 		.data = &tegra210_dc_soc_info,
30545b4f516fSThierry Reding 	}, {
30558620fc62SThierry Reding 		.compatible = "nvidia,tegra124-dc",
30568620fc62SThierry Reding 		.data = &tegra124_dc_soc_info,
30578620fc62SThierry Reding 	}, {
30589c012700SThierry Reding 		.compatible = "nvidia,tegra114-dc",
30599c012700SThierry Reding 		.data = &tegra114_dc_soc_info,
30609c012700SThierry Reding 	}, {
30618620fc62SThierry Reding 		.compatible = "nvidia,tegra30-dc",
30628620fc62SThierry Reding 		.data = &tegra30_dc_soc_info,
30638620fc62SThierry Reding 	}, {
30648620fc62SThierry Reding 		.compatible = "nvidia,tegra20-dc",
30658620fc62SThierry Reding 		.data = &tegra20_dc_soc_info,
30668620fc62SThierry Reding 	}, {
30678620fc62SThierry Reding 		/* sentinel */
30688620fc62SThierry Reding 	}
30698620fc62SThierry Reding };
3070ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_dc_of_match);
30718620fc62SThierry Reding 
307213411dddSThierry Reding static int tegra_dc_parse_dt(struct tegra_dc *dc)
307313411dddSThierry Reding {
307413411dddSThierry Reding 	struct device_node *np;
307513411dddSThierry Reding 	u32 value = 0;
307613411dddSThierry Reding 	int err;
307713411dddSThierry Reding 
307813411dddSThierry Reding 	err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value);
307913411dddSThierry Reding 	if (err < 0) {
308013411dddSThierry Reding 		dev_err(dc->dev, "missing \"nvidia,head\" property\n");
308113411dddSThierry Reding 
308213411dddSThierry Reding 		/*
308313411dddSThierry Reding 		 * If the nvidia,head property isn't present, try to find the
308413411dddSThierry Reding 		 * correct head number by looking up the position of this
308513411dddSThierry Reding 		 * display controller's node within the device tree. Assuming
308613411dddSThierry Reding 		 * that the nodes are ordered properly in the DTS file and
308713411dddSThierry Reding 		 * that the translation into a flattened device tree blob
308813411dddSThierry Reding 		 * preserves that ordering this will actually yield the right
308913411dddSThierry Reding 		 * head number.
309013411dddSThierry Reding 		 *
309113411dddSThierry Reding 		 * If those assumptions don't hold, this will still work for
309213411dddSThierry Reding 		 * cases where only a single display controller is used.
309313411dddSThierry Reding 		 */
309413411dddSThierry Reding 		for_each_matching_node(np, tegra_dc_of_match) {
3095cf6b1744SJulia Lawall 			if (np == dc->dev->of_node) {
3096cf6b1744SJulia Lawall 				of_node_put(np);
309713411dddSThierry Reding 				break;
3098cf6b1744SJulia Lawall 			}
309913411dddSThierry Reding 
310013411dddSThierry Reding 			value++;
310113411dddSThierry Reding 		}
310213411dddSThierry Reding 	}
310313411dddSThierry Reding 
310413411dddSThierry Reding 	dc->pipe = value;
310513411dddSThierry Reding 
310613411dddSThierry Reding 	return 0;
310713411dddSThierry Reding }
310813411dddSThierry Reding 
310992ce7e83SSuzuki K Poulose static int tegra_dc_match_by_pipe(struct device *dev, const void *data)
3110f68ba691SDmitry Osipenko {
3111f68ba691SDmitry Osipenko 	struct tegra_dc *dc = dev_get_drvdata(dev);
311292ce7e83SSuzuki K Poulose 	unsigned int pipe = (unsigned long)(void *)data;
3113f68ba691SDmitry Osipenko 
3114f68ba691SDmitry Osipenko 	return dc->pipe == pipe;
3115f68ba691SDmitry Osipenko }
3116f68ba691SDmitry Osipenko 
3117f68ba691SDmitry Osipenko static int tegra_dc_couple(struct tegra_dc *dc)
3118f68ba691SDmitry Osipenko {
3119f68ba691SDmitry Osipenko 	/*
3120f68ba691SDmitry Osipenko 	 * On Tegra20, DC1 requires DC0 to be taken out of reset in order to
3121f68ba691SDmitry Osipenko 	 * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND /
3122f68ba691SDmitry Osipenko 	 * POWER_CONTROL registers during CRTC enabling.
3123f68ba691SDmitry Osipenko 	 */
3124f68ba691SDmitry Osipenko 	if (dc->soc->coupled_pm && dc->pipe == 1) {
3125a31500feSThierry Reding 		struct device *companion;
3126a31500feSThierry Reding 		struct tegra_dc *parent;
3127f68ba691SDmitry Osipenko 
3128a31500feSThierry Reding 		companion = driver_find_device(dc->dev->driver, NULL, (const void *)0,
3129f68ba691SDmitry Osipenko 					       tegra_dc_match_by_pipe);
3130a31500feSThierry Reding 		if (!companion)
3131f68ba691SDmitry Osipenko 			return -EPROBE_DEFER;
3132f68ba691SDmitry Osipenko 
3133a31500feSThierry Reding 		parent = dev_get_drvdata(companion);
3134a31500feSThierry Reding 		dc->client.parent = &parent->client;
3135f68ba691SDmitry Osipenko 
3136a31500feSThierry Reding 		dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion));
3137f68ba691SDmitry Osipenko 	}
3138f68ba691SDmitry Osipenko 
3139f68ba691SDmitry Osipenko 	return 0;
3140f68ba691SDmitry Osipenko }
3141f68ba691SDmitry Osipenko 
31424ce3048cSDmitry Osipenko static int tegra_dc_init_opp_table(struct tegra_dc *dc)
31434ce3048cSDmitry Osipenko {
31444ce3048cSDmitry Osipenko 	struct tegra_core_opp_params opp_params = {};
31454ce3048cSDmitry Osipenko 	int err;
31464ce3048cSDmitry Osipenko 
31474ce3048cSDmitry Osipenko 	err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
31484ce3048cSDmitry Osipenko 	if (err && err != -ENODEV)
31494ce3048cSDmitry Osipenko 		return err;
31504ce3048cSDmitry Osipenko 
31514ce3048cSDmitry Osipenko 	if (err)
31524ce3048cSDmitry Osipenko 		dc->has_opp_table = false;
31534ce3048cSDmitry Osipenko 	else
31544ce3048cSDmitry Osipenko 		dc->has_opp_table = true;
31554ce3048cSDmitry Osipenko 
31564ce3048cSDmitry Osipenko 	return 0;
31574ce3048cSDmitry Osipenko }
31584ce3048cSDmitry Osipenko 
3159dee8268fSThierry Reding static int tegra_dc_probe(struct platform_device *pdev)
3160dee8268fSThierry Reding {
316186044e74SThierry Reding 	u64 dma_mask = dma_get_mask(pdev->dev.parent);
3162dee8268fSThierry Reding 	struct tegra_dc *dc;
3163dee8268fSThierry Reding 	int err;
3164dee8268fSThierry Reding 
316586044e74SThierry Reding 	err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
316686044e74SThierry Reding 	if (err < 0) {
316786044e74SThierry Reding 		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
316886044e74SThierry Reding 		return err;
316986044e74SThierry Reding 	}
317086044e74SThierry Reding 
3171dee8268fSThierry Reding 	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
3172dee8268fSThierry Reding 	if (!dc)
3173dee8268fSThierry Reding 		return -ENOMEM;
3174dee8268fSThierry Reding 
3175b9ff7aeaSThierry Reding 	dc->soc = of_device_get_match_data(&pdev->dev);
31768620fc62SThierry Reding 
3177dee8268fSThierry Reding 	INIT_LIST_HEAD(&dc->list);
3178dee8268fSThierry Reding 	dc->dev = &pdev->dev;
3179dee8268fSThierry Reding 
318013411dddSThierry Reding 	err = tegra_dc_parse_dt(dc);
318113411dddSThierry Reding 	if (err < 0)
318213411dddSThierry Reding 		return err;
318313411dddSThierry Reding 
3184f68ba691SDmitry Osipenko 	err = tegra_dc_couple(dc);
3185f68ba691SDmitry Osipenko 	if (err < 0)
3186f68ba691SDmitry Osipenko 		return err;
3187f68ba691SDmitry Osipenko 
3188dee8268fSThierry Reding 	dc->clk = devm_clk_get(&pdev->dev, NULL);
3189dee8268fSThierry Reding 	if (IS_ERR(dc->clk)) {
3190dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to get clock\n");
3191dee8268fSThierry Reding 		return PTR_ERR(dc->clk);
3192dee8268fSThierry Reding 	}
3193dee8268fSThierry Reding 
3194ca48080aSStephen Warren 	dc->rst = devm_reset_control_get(&pdev->dev, "dc");
3195ca48080aSStephen Warren 	if (IS_ERR(dc->rst)) {
3196ca48080aSStephen Warren 		dev_err(&pdev->dev, "failed to get reset\n");
3197ca48080aSStephen Warren 		return PTR_ERR(dc->rst);
3198ca48080aSStephen Warren 	}
3199ca48080aSStephen Warren 
3200a2f2f740SThierry Reding 	/* assert reset and disable clock */
3201a2f2f740SThierry Reding 	err = clk_prepare_enable(dc->clk);
3202a2f2f740SThierry Reding 	if (err < 0)
3203a2f2f740SThierry Reding 		return err;
3204a2f2f740SThierry Reding 
3205a2f2f740SThierry Reding 	usleep_range(2000, 4000);
3206a2f2f740SThierry Reding 
3207a2f2f740SThierry Reding 	err = reset_control_assert(dc->rst);
32087ad4384dSZhang Zekun 	if (err < 0) {
32097ad4384dSZhang Zekun 		clk_disable_unprepare(dc->clk);
3210a2f2f740SThierry Reding 		return err;
32117ad4384dSZhang Zekun 	}
3212a2f2f740SThierry Reding 
3213a2f2f740SThierry Reding 	usleep_range(2000, 4000);
3214a2f2f740SThierry Reding 
3215a2f2f740SThierry Reding 	clk_disable_unprepare(dc->clk);
321633a8eb8dSThierry Reding 
32179c012700SThierry Reding 	if (dc->soc->has_powergate) {
32189c012700SThierry Reding 		if (dc->pipe == 0)
32199c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DIS;
32209c012700SThierry Reding 		else
32219c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DISB;
32229c012700SThierry Reding 
322333a8eb8dSThierry Reding 		tegra_powergate_power_off(dc->powergate);
32249c012700SThierry Reding 	}
3225dee8268fSThierry Reding 
32264ce3048cSDmitry Osipenko 	err = tegra_dc_init_opp_table(dc);
32274ce3048cSDmitry Osipenko 	if (err < 0)
32284ce3048cSDmitry Osipenko 		return err;
32294ce3048cSDmitry Osipenko 
3230a858ac8fSDmitry Osipenko 	dc->regs = devm_platform_ioremap_resource(pdev, 0);
3231dee8268fSThierry Reding 	if (IS_ERR(dc->regs))
3232dee8268fSThierry Reding 		return PTR_ERR(dc->regs);
3233dee8268fSThierry Reding 
3234dee8268fSThierry Reding 	dc->irq = platform_get_irq(pdev, 0);
32355f1df70fSTang Bin 	if (dc->irq < 0)
3236dee8268fSThierry Reding 		return -ENXIO;
3237dee8268fSThierry Reding 
3238dee8268fSThierry Reding 	err = tegra_dc_rgb_probe(dc);
3239f07f04a5SDmitry Osipenko 	if (err < 0 && err != -ENODEV)
3240f07f04a5SDmitry Osipenko 		return dev_err_probe(&pdev->dev, err,
3241f07f04a5SDmitry Osipenko 				     "failed to probe RGB output\n");
3242dee8268fSThierry Reding 
324333a8eb8dSThierry Reding 	platform_set_drvdata(pdev, dc);
324433a8eb8dSThierry Reding 	pm_runtime_enable(&pdev->dev);
324533a8eb8dSThierry Reding 
324633a8eb8dSThierry Reding 	INIT_LIST_HEAD(&dc->client.list);
324733a8eb8dSThierry Reding 	dc->client.ops = &dc_client_ops;
324833a8eb8dSThierry Reding 	dc->client.dev = &pdev->dev;
324933a8eb8dSThierry Reding 
3250dee8268fSThierry Reding 	err = host1x_client_register(&dc->client);
3251dee8268fSThierry Reding 	if (err < 0) {
3252dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
3253dee8268fSThierry Reding 			err);
32540411ea89SDmitry Osipenko 		goto disable_pm;
3255dee8268fSThierry Reding 	}
3256dee8268fSThierry Reding 
3257dee8268fSThierry Reding 	return 0;
32580411ea89SDmitry Osipenko 
32590411ea89SDmitry Osipenko disable_pm:
32600411ea89SDmitry Osipenko 	pm_runtime_disable(&pdev->dev);
32610411ea89SDmitry Osipenko 	tegra_dc_rgb_remove(dc);
32620411ea89SDmitry Osipenko 
32630411ea89SDmitry Osipenko 	return err;
3264dee8268fSThierry Reding }
3265dee8268fSThierry Reding 
3266*30d92e0fSUwe Kleine-König static void tegra_dc_remove(struct platform_device *pdev)
3267dee8268fSThierry Reding {
3268dee8268fSThierry Reding 	struct tegra_dc *dc = platform_get_drvdata(pdev);
3269dee8268fSThierry Reding 
32701d83d1a2SUwe Kleine-König 	host1x_client_unregister(&dc->client);
3271dee8268fSThierry Reding 
327243740540SUwe Kleine-König 	tegra_dc_rgb_remove(dc);
327359d29c0eSThierry Reding 
327433a8eb8dSThierry Reding 	pm_runtime_disable(&pdev->dev);
327533a8eb8dSThierry Reding }
327633a8eb8dSThierry Reding 
3277dee8268fSThierry Reding struct platform_driver tegra_dc_driver = {
3278dee8268fSThierry Reding 	.driver = {
3279dee8268fSThierry Reding 		.name = "tegra-dc",
3280dee8268fSThierry Reding 		.of_match_table = tegra_dc_of_match,
3281dee8268fSThierry Reding 	},
3282dee8268fSThierry Reding 	.probe = tegra_dc_probe,
3283*30d92e0fSUwe Kleine-König 	.remove_new = tegra_dc_remove,
3284dee8268fSThierry Reding };
3285