xref: /openbmc/linux/drivers/gpu/drm/tegra/dc.c (revision 720cf96d8fecde29b72e1101f8a567a0ce99594f)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dee8268fSThierry Reding /*
3dee8268fSThierry Reding  * Copyright (C) 2012 Avionic Design GmbH
4dee8268fSThierry Reding  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
5dee8268fSThierry Reding  */
6dee8268fSThierry Reding 
7dee8268fSThierry Reding #include <linux/clk.h>
8dee8268fSThierry Reding #include <linux/debugfs.h>
9eb1df694SSam Ravnborg #include <linux/delay.h>
10df06b759SThierry Reding #include <linux/iommu.h>
1104d5d5dfSDmitry Osipenko #include <linux/interconnect.h>
12eb1df694SSam Ravnborg #include <linux/module.h>
13b9ff7aeaSThierry Reding #include <linux/of_device.h>
144ce3048cSDmitry Osipenko #include <linux/pm_domain.h>
154ce3048cSDmitry Osipenko #include <linux/pm_opp.h>
1633a8eb8dSThierry Reding #include <linux/pm_runtime.h>
17ca48080aSStephen Warren #include <linux/reset.h>
18dee8268fSThierry Reding 
194ce3048cSDmitry Osipenko #include <soc/tegra/common.h>
209c012700SThierry Reding #include <soc/tegra/pmc.h>
219c012700SThierry Reding 
22eb1df694SSam Ravnborg #include <drm/drm_atomic.h>
23eb1df694SSam Ravnborg #include <drm/drm_atomic_helper.h>
24eb1df694SSam Ravnborg #include <drm/drm_debugfs.h>
25eb1df694SSam Ravnborg #include <drm/drm_fourcc.h>
26*720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
27eb1df694SSam Ravnborg #include <drm/drm_plane_helper.h>
28eb1df694SSam Ravnborg #include <drm/drm_vblank.h>
29eb1df694SSam Ravnborg 
30dee8268fSThierry Reding #include "dc.h"
31dee8268fSThierry Reding #include "drm.h"
32dee8268fSThierry Reding #include "gem.h"
3347307954SThierry Reding #include "hub.h"
345acd3514SThierry Reding #include "plane.h"
35dee8268fSThierry Reding 
36b7e0b04aSMaarten Lankhorst static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
37b7e0b04aSMaarten Lankhorst 					    struct drm_crtc_state *state);
38b7e0b04aSMaarten Lankhorst 
39791ddb1eSThierry Reding static void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
40791ddb1eSThierry Reding {
41791ddb1eSThierry Reding 	stats->frames = 0;
42791ddb1eSThierry Reding 	stats->vblank = 0;
43791ddb1eSThierry Reding 	stats->underflow = 0;
44791ddb1eSThierry Reding 	stats->overflow = 0;
45791ddb1eSThierry Reding }
46791ddb1eSThierry Reding 
471087fac1SThierry Reding /* Reads the active copy of a register. */
4886df256fSThierry Reding static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
4986df256fSThierry Reding {
5086df256fSThierry Reding 	u32 value;
5186df256fSThierry Reding 
5286df256fSThierry Reding 	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
5386df256fSThierry Reding 	value = tegra_dc_readl(dc, offset);
5486df256fSThierry Reding 	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
5586df256fSThierry Reding 
5686df256fSThierry Reding 	return value;
5786df256fSThierry Reding }
5886df256fSThierry Reding 
591087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
601087fac1SThierry Reding 					      unsigned int offset)
611087fac1SThierry Reding {
621087fac1SThierry Reding 	if (offset >= 0x500 && offset <= 0x638) {
631087fac1SThierry Reding 		offset = 0x000 + (offset - 0x500);
641087fac1SThierry Reding 		return plane->offset + offset;
651087fac1SThierry Reding 	}
661087fac1SThierry Reding 
671087fac1SThierry Reding 	if (offset >= 0x700 && offset <= 0x719) {
681087fac1SThierry Reding 		offset = 0x180 + (offset - 0x700);
691087fac1SThierry Reding 		return plane->offset + offset;
701087fac1SThierry Reding 	}
711087fac1SThierry Reding 
721087fac1SThierry Reding 	if (offset >= 0x800 && offset <= 0x839) {
731087fac1SThierry Reding 		offset = 0x1c0 + (offset - 0x800);
741087fac1SThierry Reding 		return plane->offset + offset;
751087fac1SThierry Reding 	}
761087fac1SThierry Reding 
771087fac1SThierry Reding 	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
781087fac1SThierry Reding 
791087fac1SThierry Reding 	return plane->offset + offset;
801087fac1SThierry Reding }
811087fac1SThierry Reding 
821087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane,
831087fac1SThierry Reding 				    unsigned int offset)
841087fac1SThierry Reding {
851087fac1SThierry Reding 	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
861087fac1SThierry Reding }
871087fac1SThierry Reding 
881087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
891087fac1SThierry Reding 				      unsigned int offset)
901087fac1SThierry Reding {
911087fac1SThierry Reding 	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
921087fac1SThierry Reding }
931087fac1SThierry Reding 
94c57997bcSThierry Reding bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
95c57997bcSThierry Reding {
96c57997bcSThierry Reding 	struct device_node *np = dc->dev->of_node;
97c57997bcSThierry Reding 	struct of_phandle_iterator it;
98c57997bcSThierry Reding 	int err;
99c57997bcSThierry Reding 
100c57997bcSThierry Reding 	of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
101c57997bcSThierry Reding 		if (it.node == dev->of_node)
102c57997bcSThierry Reding 			return true;
103c57997bcSThierry Reding 
104c57997bcSThierry Reding 	return false;
105c57997bcSThierry Reding }
106c57997bcSThierry Reding 
10786df256fSThierry Reding /*
108d700ba7aSThierry Reding  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
109d700ba7aSThierry Reding  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
110d700ba7aSThierry Reding  * Latching happens mmediately if the display controller is in STOP mode or
111d700ba7aSThierry Reding  * on the next frame boundary otherwise.
112d700ba7aSThierry Reding  *
113d700ba7aSThierry Reding  * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The
114d700ba7aSThierry Reding  * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits
115d700ba7aSThierry Reding  * are written. When the *_ACT_REQ bits are written, the ARM copy is latched
116d700ba7aSThierry Reding  * into the ACTIVE copy, either immediately if the display controller is in
117d700ba7aSThierry Reding  * STOP mode, or at the next frame boundary otherwise.
118d700ba7aSThierry Reding  */
11962b9e063SThierry Reding void tegra_dc_commit(struct tegra_dc *dc)
120205d48edSThierry Reding {
121205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
122205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
123205d48edSThierry Reding }
124205d48edSThierry Reding 
12510288eeaSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
12610288eeaSThierry Reding 				  unsigned int bpp)
12710288eeaSThierry Reding {
12810288eeaSThierry Reding 	fixed20_12 outf = dfixed_init(out);
12910288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
13010288eeaSThierry Reding 	u32 dda_inc;
13110288eeaSThierry Reding 	int max;
13210288eeaSThierry Reding 
13310288eeaSThierry Reding 	if (v)
13410288eeaSThierry Reding 		max = 15;
13510288eeaSThierry Reding 	else {
13610288eeaSThierry Reding 		switch (bpp) {
13710288eeaSThierry Reding 		case 2:
13810288eeaSThierry Reding 			max = 8;
13910288eeaSThierry Reding 			break;
14010288eeaSThierry Reding 
14110288eeaSThierry Reding 		default:
14210288eeaSThierry Reding 			WARN_ON_ONCE(1);
143df561f66SGustavo A. R. Silva 			fallthrough;
14410288eeaSThierry Reding 		case 4:
14510288eeaSThierry Reding 			max = 4;
14610288eeaSThierry Reding 			break;
14710288eeaSThierry Reding 		}
14810288eeaSThierry Reding 	}
14910288eeaSThierry Reding 
15010288eeaSThierry Reding 	outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
15110288eeaSThierry Reding 	inf.full -= dfixed_const(1);
15210288eeaSThierry Reding 
15310288eeaSThierry Reding 	dda_inc = dfixed_div(inf, outf);
15410288eeaSThierry Reding 	dda_inc = min_t(u32, dda_inc, dfixed_const(max));
15510288eeaSThierry Reding 
15610288eeaSThierry Reding 	return dda_inc;
15710288eeaSThierry Reding }
15810288eeaSThierry Reding 
15910288eeaSThierry Reding static inline u32 compute_initial_dda(unsigned int in)
16010288eeaSThierry Reding {
16110288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
16210288eeaSThierry Reding 	return dfixed_frac(inf);
16310288eeaSThierry Reding }
16410288eeaSThierry Reding 
165ab7d3f58SThierry Reding static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
166ab7d3f58SThierry Reding {
167ebae8d07SThierry Reding 	u32 background[3] = {
168ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
169ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
170ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
171ebae8d07SThierry Reding 	};
172ebae8d07SThierry Reding 	u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
173ebae8d07SThierry Reding 			 BLEND_COLOR_KEY_NONE;
174ebae8d07SThierry Reding 	u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
175ebae8d07SThierry Reding 	struct tegra_plane_state *state;
1763dae08bcSDmitry Osipenko 	u32 blending[2];
177ebae8d07SThierry Reding 	unsigned int i;
178ebae8d07SThierry Reding 
1793dae08bcSDmitry Osipenko 	/* disable blending for non-overlapping case */
180ebae8d07SThierry Reding 	tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
181ebae8d07SThierry Reding 	tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
182ab7d3f58SThierry Reding 
1833dae08bcSDmitry Osipenko 	state = to_tegra_plane_state(plane->base.state);
1843dae08bcSDmitry Osipenko 
1853dae08bcSDmitry Osipenko 	if (state->opaque) {
1863dae08bcSDmitry Osipenko 		/*
1873dae08bcSDmitry Osipenko 		 * Since custom fix-weight blending isn't utilized and weight
1883dae08bcSDmitry Osipenko 		 * of top window is set to max, we can enforce dependent
1893dae08bcSDmitry Osipenko 		 * blending which in this case results in transparent bottom
1903dae08bcSDmitry Osipenko 		 * window if top window is opaque and if top window enables
1913dae08bcSDmitry Osipenko 		 * alpha blending, then bottom window is getting alpha value
1923dae08bcSDmitry Osipenko 		 * of 1 minus the sum of alpha components of the overlapping
1933dae08bcSDmitry Osipenko 		 * plane.
1943dae08bcSDmitry Osipenko 		 */
1953dae08bcSDmitry Osipenko 		background[0] |= BLEND_CONTROL_DEPENDENT;
1963dae08bcSDmitry Osipenko 		background[1] |= BLEND_CONTROL_DEPENDENT;
1973dae08bcSDmitry Osipenko 
1983dae08bcSDmitry Osipenko 		/*
1993dae08bcSDmitry Osipenko 		 * The region where three windows overlap is the intersection
2003dae08bcSDmitry Osipenko 		 * of the two regions where two windows overlap. It contributes
2013dae08bcSDmitry Osipenko 		 * to the area if all of the windows on top of it have an alpha
2023dae08bcSDmitry Osipenko 		 * component.
2033dae08bcSDmitry Osipenko 		 */
2043dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2053dae08bcSDmitry Osipenko 		case 0:
2063dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2073dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2083dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2093dae08bcSDmitry Osipenko 			break;
2103dae08bcSDmitry Osipenko 
2113dae08bcSDmitry Osipenko 		case 1:
2123dae08bcSDmitry Osipenko 			background[2] |= BLEND_CONTROL_DEPENDENT;
2133dae08bcSDmitry Osipenko 			break;
2143dae08bcSDmitry Osipenko 		}
2153dae08bcSDmitry Osipenko 	} else {
2163dae08bcSDmitry Osipenko 		/*
2173dae08bcSDmitry Osipenko 		 * Enable alpha blending if pixel format has an alpha
2183dae08bcSDmitry Osipenko 		 * component.
2193dae08bcSDmitry Osipenko 		 */
2203dae08bcSDmitry Osipenko 		foreground |= BLEND_CONTROL_ALPHA;
2213dae08bcSDmitry Osipenko 
2223dae08bcSDmitry Osipenko 		/*
2233dae08bcSDmitry Osipenko 		 * If any of the windows on top of this window is opaque, it
2243dae08bcSDmitry Osipenko 		 * will completely conceal this window within that area. If
2253dae08bcSDmitry Osipenko 		 * top window has an alpha component, it is blended over the
2263dae08bcSDmitry Osipenko 		 * bottom window.
2273dae08bcSDmitry Osipenko 		 */
2283dae08bcSDmitry Osipenko 		for (i = 0; i < 2; i++) {
2293dae08bcSDmitry Osipenko 			if (state->blending[i].alpha &&
2303dae08bcSDmitry Osipenko 			    state->blending[i].top)
2313dae08bcSDmitry Osipenko 				background[i] |= BLEND_CONTROL_DEPENDENT;
2323dae08bcSDmitry Osipenko 		}
2333dae08bcSDmitry Osipenko 
2343dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2353dae08bcSDmitry Osipenko 		case 0:
2363dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2373dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2383dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2393dae08bcSDmitry Osipenko 			break;
2403dae08bcSDmitry Osipenko 
2413dae08bcSDmitry Osipenko 		case 1:
2423dae08bcSDmitry Osipenko 			/*
2433dae08bcSDmitry Osipenko 			 * When both middle and topmost windows have an alpha,
2443dae08bcSDmitry Osipenko 			 * these windows a mixed together and then the result
2453dae08bcSDmitry Osipenko 			 * is blended over the bottom window.
2463dae08bcSDmitry Osipenko 			 */
2473dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2483dae08bcSDmitry Osipenko 			    state->blending[0].top)
2493dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2503dae08bcSDmitry Osipenko 
2513dae08bcSDmitry Osipenko 			if (state->blending[1].alpha &&
2523dae08bcSDmitry Osipenko 			    state->blending[1].top)
2533dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2543dae08bcSDmitry Osipenko 			break;
2553dae08bcSDmitry Osipenko 		}
2563dae08bcSDmitry Osipenko 	}
2573dae08bcSDmitry Osipenko 
2583dae08bcSDmitry Osipenko 	switch (state->base.normalized_zpos) {
259ab7d3f58SThierry Reding 	case 0:
260ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
261ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
262ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
263ab7d3f58SThierry Reding 		break;
264ab7d3f58SThierry Reding 
265ab7d3f58SThierry Reding 	case 1:
2663dae08bcSDmitry Osipenko 		/*
2673dae08bcSDmitry Osipenko 		 * If window B / C is topmost, then X / Y registers are
2683dae08bcSDmitry Osipenko 		 * matching the order of blending[...] state indices,
2693dae08bcSDmitry Osipenko 		 * otherwise a swap is required.
2703dae08bcSDmitry Osipenko 		 */
2713dae08bcSDmitry Osipenko 		if (!state->blending[0].top && state->blending[1].top) {
2723dae08bcSDmitry Osipenko 			blending[0] = foreground;
2733dae08bcSDmitry Osipenko 			blending[1] = background[1];
2743dae08bcSDmitry Osipenko 		} else {
2753dae08bcSDmitry Osipenko 			blending[0] = background[0];
2763dae08bcSDmitry Osipenko 			blending[1] = foreground;
2773dae08bcSDmitry Osipenko 		}
2783dae08bcSDmitry Osipenko 
2793dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
2803dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
281ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
282ab7d3f58SThierry Reding 		break;
283ab7d3f58SThierry Reding 
284ab7d3f58SThierry Reding 	case 2:
285ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
286ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
287ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
288ab7d3f58SThierry Reding 		break;
289ab7d3f58SThierry Reding 	}
290ab7d3f58SThierry Reding }
291ab7d3f58SThierry Reding 
292ab7d3f58SThierry Reding static void tegra_plane_setup_blending(struct tegra_plane *plane,
293ab7d3f58SThierry Reding 				       const struct tegra_dc_window *window)
294ab7d3f58SThierry Reding {
295ab7d3f58SThierry Reding 	u32 value;
296ab7d3f58SThierry Reding 
297ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
298ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
299ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
300ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT);
301ab7d3f58SThierry Reding 
302ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
303ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
304ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
305ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT);
306ab7d3f58SThierry Reding 
307ab7d3f58SThierry Reding 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos);
308ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
309ab7d3f58SThierry Reding }
310ab7d3f58SThierry Reding 
311acc6a3a9SDmitry Osipenko static bool
312acc6a3a9SDmitry Osipenko tegra_plane_use_horizontal_filtering(struct tegra_plane *plane,
313acc6a3a9SDmitry Osipenko 				     const struct tegra_dc_window *window)
314acc6a3a9SDmitry Osipenko {
315acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
316acc6a3a9SDmitry Osipenko 
317acc6a3a9SDmitry Osipenko 	if (window->src.w == window->dst.w)
318acc6a3a9SDmitry Osipenko 		return false;
319acc6a3a9SDmitry Osipenko 
320acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
321acc6a3a9SDmitry Osipenko 		return false;
322acc6a3a9SDmitry Osipenko 
323acc6a3a9SDmitry Osipenko 	return true;
324acc6a3a9SDmitry Osipenko }
325acc6a3a9SDmitry Osipenko 
326acc6a3a9SDmitry Osipenko static bool
327acc6a3a9SDmitry Osipenko tegra_plane_use_vertical_filtering(struct tegra_plane *plane,
328acc6a3a9SDmitry Osipenko 				   const struct tegra_dc_window *window)
329acc6a3a9SDmitry Osipenko {
330acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
331acc6a3a9SDmitry Osipenko 
332acc6a3a9SDmitry Osipenko 	if (window->src.h == window->dst.h)
333acc6a3a9SDmitry Osipenko 		return false;
334acc6a3a9SDmitry Osipenko 
335acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
336acc6a3a9SDmitry Osipenko 		return false;
337acc6a3a9SDmitry Osipenko 
338acc6a3a9SDmitry Osipenko 	if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter)
339acc6a3a9SDmitry Osipenko 		return false;
340acc6a3a9SDmitry Osipenko 
341acc6a3a9SDmitry Osipenko 	return true;
342acc6a3a9SDmitry Osipenko }
343acc6a3a9SDmitry Osipenko 
3441087fac1SThierry Reding static void tegra_dc_setup_window(struct tegra_plane *plane,
34510288eeaSThierry Reding 				  const struct tegra_dc_window *window)
34610288eeaSThierry Reding {
34710288eeaSThierry Reding 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
3481087fac1SThierry Reding 	struct tegra_dc *dc = plane->dc;
349a649b133SThierry Reding 	unsigned int planes;
3501087fac1SThierry Reding 	u32 value;
351a649b133SThierry Reding 	bool yuv;
35210288eeaSThierry Reding 
35310288eeaSThierry Reding 	/*
35410288eeaSThierry Reding 	 * For YUV planar modes, the number of bytes per pixel takes into
35510288eeaSThierry Reding 	 * account only the luma component and therefore is 1.
35610288eeaSThierry Reding 	 */
357a649b133SThierry Reding 	yuv = tegra_plane_format_is_yuv(window->format, &planes, NULL);
35810288eeaSThierry Reding 	if (!yuv)
35910288eeaSThierry Reding 		bpp = window->bits_per_pixel / 8;
36010288eeaSThierry Reding 	else
361a649b133SThierry Reding 		bpp = (planes > 1) ? 1 : 2;
36210288eeaSThierry Reding 
3631087fac1SThierry Reding 	tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH);
3641087fac1SThierry Reding 	tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP);
36510288eeaSThierry Reding 
36610288eeaSThierry Reding 	value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
3671087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_POSITION);
36810288eeaSThierry Reding 
36910288eeaSThierry Reding 	value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
3701087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_SIZE);
37110288eeaSThierry Reding 
37210288eeaSThierry Reding 	h_offset = window->src.x * bpp;
37310288eeaSThierry Reding 	v_offset = window->src.y;
37410288eeaSThierry Reding 	h_size = window->src.w * bpp;
37510288eeaSThierry Reding 	v_size = window->src.h;
37610288eeaSThierry Reding 
377cd740777SDmitry Osipenko 	if (window->reflect_x)
378cd740777SDmitry Osipenko 		h_offset += (window->src.w - 1) * bpp;
379cd740777SDmitry Osipenko 
380cd740777SDmitry Osipenko 	if (window->reflect_y)
381cd740777SDmitry Osipenko 		v_offset += window->src.h - 1;
382cd740777SDmitry Osipenko 
38310288eeaSThierry Reding 	value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
3841087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE);
38510288eeaSThierry Reding 
38610288eeaSThierry Reding 	/*
38710288eeaSThierry Reding 	 * For DDA computations the number of bytes per pixel for YUV planar
38810288eeaSThierry Reding 	 * modes needs to take into account all Y, U and V components.
38910288eeaSThierry Reding 	 */
390a649b133SThierry Reding 	if (yuv && planes > 1)
39110288eeaSThierry Reding 		bpp = 2;
39210288eeaSThierry Reding 
39310288eeaSThierry Reding 	h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
39410288eeaSThierry Reding 	v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
39510288eeaSThierry Reding 
39610288eeaSThierry Reding 	value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
3971087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_DDA_INC);
39810288eeaSThierry Reding 
39910288eeaSThierry Reding 	h_dda = compute_initial_dda(window->src.x);
40010288eeaSThierry Reding 	v_dda = compute_initial_dda(window->src.y);
40110288eeaSThierry Reding 
4021087fac1SThierry Reding 	tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA);
4031087fac1SThierry Reding 	tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA);
40410288eeaSThierry Reding 
4051087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE);
4061087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE);
40710288eeaSThierry Reding 
4081087fac1SThierry Reding 	tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR);
40910288eeaSThierry Reding 
410a649b133SThierry Reding 	if (yuv && planes > 1) {
4111087fac1SThierry Reding 		tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U);
412a649b133SThierry Reding 
413a649b133SThierry Reding 		if (planes > 2)
4141087fac1SThierry Reding 			tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V);
415a649b133SThierry Reding 
41610288eeaSThierry Reding 		value = window->stride[1] << 16 | window->stride[0];
4171087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE);
41810288eeaSThierry Reding 	} else {
4191087fac1SThierry Reding 		tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE);
42010288eeaSThierry Reding 	}
42110288eeaSThierry Reding 
4221087fac1SThierry Reding 	tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET);
4231087fac1SThierry Reding 	tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET);
42410288eeaSThierry Reding 
425c134f019SThierry Reding 	if (dc->soc->supports_block_linear) {
426c134f019SThierry Reding 		unsigned long height = window->tiling.value;
427c134f019SThierry Reding 
428c134f019SThierry Reding 		switch (window->tiling.mode) {
429c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
430c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_PITCH;
431c134f019SThierry Reding 			break;
432c134f019SThierry Reding 
433c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
434c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_TILED;
435c134f019SThierry Reding 			break;
436c134f019SThierry Reding 
437c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
438c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
439c134f019SThierry Reding 				DC_WINBUF_SURFACE_KIND_BLOCK;
440c134f019SThierry Reding 			break;
441c134f019SThierry Reding 		}
442c134f019SThierry Reding 
4431087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND);
44410288eeaSThierry Reding 	} else {
445c134f019SThierry Reding 		switch (window->tiling.mode) {
446c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
44710288eeaSThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
44810288eeaSThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
449c134f019SThierry Reding 			break;
450c134f019SThierry Reding 
451c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
452c134f019SThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
453c134f019SThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_TILE;
454c134f019SThierry Reding 			break;
455c134f019SThierry Reding 
456c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
4574aa3df71SThierry Reding 			/*
4584aa3df71SThierry Reding 			 * No need to handle this here because ->atomic_check
4594aa3df71SThierry Reding 			 * will already have filtered it out.
4604aa3df71SThierry Reding 			 */
4614aa3df71SThierry Reding 			break;
46210288eeaSThierry Reding 		}
46310288eeaSThierry Reding 
4641087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE);
465c134f019SThierry Reding 	}
46610288eeaSThierry Reding 
46710288eeaSThierry Reding 	value = WIN_ENABLE;
46810288eeaSThierry Reding 
46910288eeaSThierry Reding 	if (yuv) {
47010288eeaSThierry Reding 		/* setup default colorspace conversion coefficients */
4711087fac1SThierry Reding 		tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF);
4721087fac1SThierry Reding 		tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB);
4731087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR);
4741087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR);
4751087fac1SThierry Reding 		tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG);
4761087fac1SThierry Reding 		tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG);
4771087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB);
4781087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB);
47910288eeaSThierry Reding 
48010288eeaSThierry Reding 		value |= CSC_ENABLE;
48110288eeaSThierry Reding 	} else if (window->bits_per_pixel < 24) {
48210288eeaSThierry Reding 		value |= COLOR_EXPAND;
48310288eeaSThierry Reding 	}
48410288eeaSThierry Reding 
485cd740777SDmitry Osipenko 	if (window->reflect_x)
486cd740777SDmitry Osipenko 		value |= H_DIRECTION;
487cd740777SDmitry Osipenko 
488e9e476f7SDmitry Osipenko 	if (window->reflect_y)
48910288eeaSThierry Reding 		value |= V_DIRECTION;
49010288eeaSThierry Reding 
491acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_horizontal_filtering(plane, window)) {
492acc6a3a9SDmitry Osipenko 		/*
493acc6a3a9SDmitry Osipenko 		 * Enable horizontal 6-tap filter and set filtering
494acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
495acc6a3a9SDmitry Osipenko 		 */
496acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0));
497acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1));
498acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2));
499acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3));
500acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4));
501acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5));
502acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6));
503acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7));
504acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8));
505acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9));
506acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10));
507acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11));
508acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12));
509acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13));
510acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14));
511acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15));
512acc6a3a9SDmitry Osipenko 
513acc6a3a9SDmitry Osipenko 		value |= H_FILTER;
514acc6a3a9SDmitry Osipenko 	}
515acc6a3a9SDmitry Osipenko 
516acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_vertical_filtering(plane, window)) {
517acc6a3a9SDmitry Osipenko 		unsigned int i, k;
518acc6a3a9SDmitry Osipenko 
519acc6a3a9SDmitry Osipenko 		/*
520acc6a3a9SDmitry Osipenko 		 * Enable vertical 2-tap filter and set filtering
521acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
522acc6a3a9SDmitry Osipenko 		 */
523acc6a3a9SDmitry Osipenko 		for (i = 0, k = 128; i < 16; i++, k -= 8)
524acc6a3a9SDmitry Osipenko 			tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i));
525acc6a3a9SDmitry Osipenko 
526acc6a3a9SDmitry Osipenko 		value |= V_FILTER;
527acc6a3a9SDmitry Osipenko 	}
528acc6a3a9SDmitry Osipenko 
5291087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
53010288eeaSThierry Reding 
531a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending)
532ab7d3f58SThierry Reding 		tegra_plane_setup_blending_legacy(plane);
533a43d0a00SDmitry Osipenko 	else
534a43d0a00SDmitry Osipenko 		tegra_plane_setup_blending(plane, window);
535c7679306SThierry Reding }
536c7679306SThierry Reding 
537511c7023SThierry Reding static const u32 tegra20_primary_formats[] = {
538511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
539511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
540c7679306SThierry Reding 	DRM_FORMAT_RGB565,
541511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
542511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
543511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
544ebae8d07SThierry Reding 	/* non-native formats */
545ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
546ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
547ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
548ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
549511c7023SThierry Reding };
550511c7023SThierry Reding 
551e90124cbSThierry Reding static const u64 tegra20_modifiers[] = {
552e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
553e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED,
554e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
555e90124cbSThierry Reding };
556e90124cbSThierry Reding 
557511c7023SThierry Reding static const u32 tegra114_primary_formats[] = {
558511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
559511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
560511c7023SThierry Reding 	DRM_FORMAT_RGB565,
561511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
562511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
563511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
564511c7023SThierry Reding 	/* new on Tegra114 */
565511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
566511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
567511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
568511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
569511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
570511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
571511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
572511c7023SThierry Reding 	DRM_FORMAT_BGR565,
573511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
574511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
575511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
576511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
577511c7023SThierry Reding };
578511c7023SThierry Reding 
579511c7023SThierry Reding static const u32 tegra124_primary_formats[] = {
580511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
581511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
582511c7023SThierry Reding 	DRM_FORMAT_RGB565,
583511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
584511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
585511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
586511c7023SThierry Reding 	/* new on Tegra114 */
587511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
588511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
589511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
590511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
591511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
592511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
593511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
594511c7023SThierry Reding 	DRM_FORMAT_BGR565,
595511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
596511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
597511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
598511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
599511c7023SThierry Reding 	/* new on Tegra124 */
600511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
601511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
602c7679306SThierry Reding };
603c7679306SThierry Reding 
604e90124cbSThierry Reding static const u64 tegra124_modifiers[] = {
605e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
606e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
607e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
608e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
609e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
610e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
611e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
612e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
613e90124cbSThierry Reding };
614e90124cbSThierry Reding 
6154aa3df71SThierry Reding static int tegra_plane_atomic_check(struct drm_plane *plane,
6167c11b99aSMaxime Ripard 				    struct drm_atomic_state *state)
6174aa3df71SThierry Reding {
6187c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
6197c11b99aSMaxime Ripard 										 plane);
620ba5c1649SMaxime Ripard 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
621cd740777SDmitry Osipenko 	unsigned int supported_rotation = DRM_MODE_ROTATE_0 |
622cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_X |
623cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_Y;
624ba5c1649SMaxime Ripard 	unsigned int rotation = new_plane_state->rotation;
6258f604f8cSThierry Reding 	struct tegra_bo_tiling *tiling = &plane_state->tiling;
62647802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
627ba5c1649SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
628c7679306SThierry Reding 	int err;
629c7679306SThierry Reding 
63004d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
63104d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
63204d5d5dfSDmitry Osipenko 
6334aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
63404d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
63504d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
6364aa3df71SThierry Reding 		return 0;
63704d5d5dfSDmitry Osipenko 	}
6384aa3df71SThierry Reding 
639ba5c1649SMaxime Ripard 	err = tegra_plane_format(new_plane_state->fb->format->format,
6403dae08bcSDmitry Osipenko 				 &plane_state->format,
6418f604f8cSThierry Reding 				 &plane_state->swap);
6424aa3df71SThierry Reding 	if (err < 0)
6434aa3df71SThierry Reding 		return err;
6444aa3df71SThierry Reding 
645ebae8d07SThierry Reding 	/*
646ebae8d07SThierry Reding 	 * Tegra20 and Tegra30 are special cases here because they support
647ebae8d07SThierry Reding 	 * only variants of specific formats with an alpha component, but not
648ebae8d07SThierry Reding 	 * the corresponding opaque formats. However, the opaque formats can
649ebae8d07SThierry Reding 	 * be emulated by disabling alpha blending for the plane.
650ebae8d07SThierry Reding 	 */
651a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending) {
6523dae08bcSDmitry Osipenko 		err = tegra_plane_setup_legacy_state(tegra, plane_state);
653ebae8d07SThierry Reding 		if (err < 0)
654ebae8d07SThierry Reding 			return err;
655ebae8d07SThierry Reding 	}
656ebae8d07SThierry Reding 
657ba5c1649SMaxime Ripard 	err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
6588f604f8cSThierry Reding 	if (err < 0)
6598f604f8cSThierry Reding 		return err;
6608f604f8cSThierry Reding 
6618f604f8cSThierry Reding 	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
6624aa3df71SThierry Reding 	    !dc->soc->supports_block_linear) {
6634aa3df71SThierry Reding 		DRM_ERROR("hardware doesn't support block linear mode\n");
6644aa3df71SThierry Reding 		return -EINVAL;
6654aa3df71SThierry Reding 	}
6664aa3df71SThierry Reding 
667cd740777SDmitry Osipenko 	/*
668cd740777SDmitry Osipenko 	 * Older userspace used custom BO flag in order to specify the Y
669cd740777SDmitry Osipenko 	 * reflection, while modern userspace uses the generic DRM rotation
670cd740777SDmitry Osipenko 	 * property in order to achieve the same result.  The legacy BO flag
671cd740777SDmitry Osipenko 	 * duplicates the DRM rotation property when both are set.
672cd740777SDmitry Osipenko 	 */
673ba5c1649SMaxime Ripard 	if (tegra_fb_is_bottom_up(new_plane_state->fb))
674cd740777SDmitry Osipenko 		rotation |= DRM_MODE_REFLECT_Y;
675cd740777SDmitry Osipenko 
676cd740777SDmitry Osipenko 	rotation = drm_rotation_simplify(rotation, supported_rotation);
677cd740777SDmitry Osipenko 
678cd740777SDmitry Osipenko 	if (rotation & DRM_MODE_REFLECT_X)
679cd740777SDmitry Osipenko 		plane_state->reflect_x = true;
680cd740777SDmitry Osipenko 	else
681cd740777SDmitry Osipenko 		plane_state->reflect_x = false;
682995c5a50SThierry Reding 
683995c5a50SThierry Reding 	if (rotation & DRM_MODE_REFLECT_Y)
684e9e476f7SDmitry Osipenko 		plane_state->reflect_y = true;
685995c5a50SThierry Reding 	else
686e9e476f7SDmitry Osipenko 		plane_state->reflect_y = false;
687995c5a50SThierry Reding 
6884aa3df71SThierry Reding 	/*
6894aa3df71SThierry Reding 	 * Tegra doesn't support different strides for U and V planes so we
6904aa3df71SThierry Reding 	 * error out if the user tries to display a framebuffer with such a
6914aa3df71SThierry Reding 	 * configuration.
6924aa3df71SThierry Reding 	 */
693ba5c1649SMaxime Ripard 	if (new_plane_state->fb->format->num_planes > 2) {
694ba5c1649SMaxime Ripard 		if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
6954aa3df71SThierry Reding 			DRM_ERROR("unsupported UV-plane configuration\n");
6964aa3df71SThierry Reding 			return -EINVAL;
6974aa3df71SThierry Reding 		}
6984aa3df71SThierry Reding 	}
6994aa3df71SThierry Reding 
700ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
70147802b09SThierry Reding 	if (err < 0)
70247802b09SThierry Reding 		return err;
70347802b09SThierry Reding 
7044aa3df71SThierry Reding 	return 0;
7054aa3df71SThierry Reding }
7064aa3df71SThierry Reding 
707a4bfa096SThierry Reding static void tegra_plane_atomic_disable(struct drm_plane *plane,
708977697e2SMaxime Ripard 				       struct drm_atomic_state *state)
70980d3eef1SDmitry Osipenko {
710977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
711977697e2SMaxime Ripard 									   plane);
712a4bfa096SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
71380d3eef1SDmitry Osipenko 	u32 value;
71480d3eef1SDmitry Osipenko 
715a4bfa096SThierry Reding 	/* rien ne va plus */
716a4bfa096SThierry Reding 	if (!old_state || !old_state->crtc)
717a4bfa096SThierry Reding 		return;
718a4bfa096SThierry Reding 
7191087fac1SThierry Reding 	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
72080d3eef1SDmitry Osipenko 	value &= ~WIN_ENABLE;
7211087fac1SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
72280d3eef1SDmitry Osipenko }
72380d3eef1SDmitry Osipenko 
7244aa3df71SThierry Reding static void tegra_plane_atomic_update(struct drm_plane *plane,
725977697e2SMaxime Ripard 				      struct drm_atomic_state *state)
7264aa3df71SThierry Reding {
72737418bf1SMaxime Ripard 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
72837418bf1SMaxime Ripard 									   plane);
72941016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
730e05162c0SMaxime Ripard 	struct drm_framebuffer *fb = new_state->fb;
7314aa3df71SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
7324aa3df71SThierry Reding 	struct tegra_dc_window window;
7334aa3df71SThierry Reding 	unsigned int i;
7344aa3df71SThierry Reding 
7354aa3df71SThierry Reding 	/* rien ne va plus */
736e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
7374aa3df71SThierry Reding 		return;
7384aa3df71SThierry Reding 
739e05162c0SMaxime Ripard 	if (!new_state->visible)
740977697e2SMaxime Ripard 		return tegra_plane_atomic_disable(plane, state);
74180d3eef1SDmitry Osipenko 
742c7679306SThierry Reding 	memset(&window, 0, sizeof(window));
743e05162c0SMaxime Ripard 	window.src.x = new_state->src.x1 >> 16;
744e05162c0SMaxime Ripard 	window.src.y = new_state->src.y1 >> 16;
745e05162c0SMaxime Ripard 	window.src.w = drm_rect_width(&new_state->src) >> 16;
746e05162c0SMaxime Ripard 	window.src.h = drm_rect_height(&new_state->src) >> 16;
747e05162c0SMaxime Ripard 	window.dst.x = new_state->dst.x1;
748e05162c0SMaxime Ripard 	window.dst.y = new_state->dst.y1;
749e05162c0SMaxime Ripard 	window.dst.w = drm_rect_width(&new_state->dst);
750e05162c0SMaxime Ripard 	window.dst.h = drm_rect_height(&new_state->dst);
751272725c7SVille Syrjälä 	window.bits_per_pixel = fb->format->cpp[0] * 8;
75241016fe1SMaxime Ripard 	window.reflect_x = tegra_plane_state->reflect_x;
75341016fe1SMaxime Ripard 	window.reflect_y = tegra_plane_state->reflect_y;
754c7679306SThierry Reding 
7558f604f8cSThierry Reding 	/* copy from state */
756e05162c0SMaxime Ripard 	window.zpos = new_state->normalized_zpos;
75741016fe1SMaxime Ripard 	window.tiling = tegra_plane_state->tiling;
75841016fe1SMaxime Ripard 	window.format = tegra_plane_state->format;
75941016fe1SMaxime Ripard 	window.swap = tegra_plane_state->swap;
760c7679306SThierry Reding 
761bcb0b461SVille Syrjälä 	for (i = 0; i < fb->format->num_planes; i++) {
76241016fe1SMaxime Ripard 		window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i];
76308ee0178SDmitry Osipenko 
76408ee0178SDmitry Osipenko 		/*
76508ee0178SDmitry Osipenko 		 * Tegra uses a shared stride for UV planes. Framebuffers are
76608ee0178SDmitry Osipenko 		 * already checked for this in the tegra_plane_atomic_check()
76708ee0178SDmitry Osipenko 		 * function, so it's safe to ignore the V-plane pitch here.
76808ee0178SDmitry Osipenko 		 */
76908ee0178SDmitry Osipenko 		if (i < 2)
7704aa3df71SThierry Reding 			window.stride[i] = fb->pitches[i];
771c7679306SThierry Reding 	}
772c7679306SThierry Reding 
7731087fac1SThierry Reding 	tegra_dc_setup_window(p, &window);
7744aa3df71SThierry Reding }
7754aa3df71SThierry Reding 
776a4bfa096SThierry Reding static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
7772e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
7782e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
7794aa3df71SThierry Reding 	.atomic_check = tegra_plane_atomic_check,
7804aa3df71SThierry Reding 	.atomic_disable = tegra_plane_atomic_disable,
781a4bfa096SThierry Reding 	.atomic_update = tegra_plane_atomic_update,
782c7679306SThierry Reding };
783c7679306SThierry Reding 
78489f65018SThierry Reding static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm)
785c7679306SThierry Reding {
786518e6227SThierry Reding 	/*
787518e6227SThierry Reding 	 * Ideally this would use drm_crtc_mask(), but that would require the
788518e6227SThierry Reding 	 * CRTC to already be in the mode_config's list of CRTCs. However, it
789518e6227SThierry Reding 	 * will only be added to that list in the drm_crtc_init_with_planes()
790518e6227SThierry Reding 	 * (in tegra_dc_init()), which in turn requires registration of these
791518e6227SThierry Reding 	 * planes. So we have ourselves a nice little chicken and egg problem
792518e6227SThierry Reding 	 * here.
793518e6227SThierry Reding 	 *
794518e6227SThierry Reding 	 * We work around this by manually creating the mask from the number
795518e6227SThierry Reding 	 * of CRTCs that have been registered, and should therefore always be
796518e6227SThierry Reding 	 * the same as drm_crtc_index() after registration.
797518e6227SThierry Reding 	 */
79889f65018SThierry Reding 	return 1 << drm->mode_config.num_crtc;
79989f65018SThierry Reding }
80089f65018SThierry Reding 
80189f65018SThierry Reding static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
80289f65018SThierry Reding 						    struct tegra_dc *dc)
80389f65018SThierry Reding {
80489f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
80547307954SThierry Reding 	enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
806c7679306SThierry Reding 	struct tegra_plane *plane;
807c7679306SThierry Reding 	unsigned int num_formats;
808e90124cbSThierry Reding 	const u64 *modifiers;
809c7679306SThierry Reding 	const u32 *formats;
810c7679306SThierry Reding 	int err;
811c7679306SThierry Reding 
812c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
813c7679306SThierry Reding 	if (!plane)
814c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
815c7679306SThierry Reding 
8161087fac1SThierry Reding 	/* Always use window A as primary window */
8171087fac1SThierry Reding 	plane->offset = 0xa00;
818c4755fb9SThierry Reding 	plane->index = 0;
8191087fac1SThierry Reding 	plane->dc = dc;
8201087fac1SThierry Reding 
8211087fac1SThierry Reding 	num_formats = dc->soc->num_primary_formats;
8221087fac1SThierry Reding 	formats = dc->soc->primary_formats;
823e90124cbSThierry Reding 	modifiers = dc->soc->modifiers;
824c4755fb9SThierry Reding 
82504d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
82604d5d5dfSDmitry Osipenko 	if (err) {
82704d5d5dfSDmitry Osipenko 		kfree(plane);
82804d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
82904d5d5dfSDmitry Osipenko 	}
83004d5d5dfSDmitry Osipenko 
831518e6227SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
832c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
833e90124cbSThierry Reding 				       num_formats, modifiers, type, NULL);
834c7679306SThierry Reding 	if (err < 0) {
835c7679306SThierry Reding 		kfree(plane);
836c7679306SThierry Reding 		return ERR_PTR(err);
837c7679306SThierry Reding 	}
838c7679306SThierry Reding 
839a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
8403dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
841ab7d3f58SThierry Reding 
842995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
843995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
844995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
8454fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
846cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
847995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
848995c5a50SThierry Reding 	if (err < 0)
849995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
850995c5a50SThierry Reding 			err);
851995c5a50SThierry Reding 
852c7679306SThierry Reding 	return &plane->base;
853c7679306SThierry Reding }
854c7679306SThierry Reding 
855d5ec699dSThierry Reding static const u32 tegra_legacy_cursor_plane_formats[] = {
856c7679306SThierry Reding 	DRM_FORMAT_RGBA8888,
857c7679306SThierry Reding };
858c7679306SThierry Reding 
859d5ec699dSThierry Reding static const u32 tegra_cursor_plane_formats[] = {
860d5ec699dSThierry Reding 	DRM_FORMAT_ARGB8888,
861d5ec699dSThierry Reding };
862d5ec699dSThierry Reding 
8634aa3df71SThierry Reding static int tegra_cursor_atomic_check(struct drm_plane *plane,
8647c11b99aSMaxime Ripard 				     struct drm_atomic_state *state)
865c7679306SThierry Reding {
8667c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
8677c11b99aSMaxime Ripard 										 plane);
86804d5d5dfSDmitry Osipenko 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
86947802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
87047802b09SThierry Reding 	int err;
87147802b09SThierry Reding 
87204d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
87304d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
87404d5d5dfSDmitry Osipenko 
8754aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
87604d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
87704d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
8784aa3df71SThierry Reding 		return 0;
87904d5d5dfSDmitry Osipenko 	}
880c7679306SThierry Reding 
881c7679306SThierry Reding 	/* scaling not supported for cursor */
882ba5c1649SMaxime Ripard 	if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) ||
883ba5c1649SMaxime Ripard 	    (new_plane_state->src_h >> 16 != new_plane_state->crtc_h))
884c7679306SThierry Reding 		return -EINVAL;
885c7679306SThierry Reding 
886c7679306SThierry Reding 	/* only square cursors supported */
887ba5c1649SMaxime Ripard 	if (new_plane_state->src_w != new_plane_state->src_h)
888c7679306SThierry Reding 		return -EINVAL;
889c7679306SThierry Reding 
890ba5c1649SMaxime Ripard 	if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 &&
891ba5c1649SMaxime Ripard 	    new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256)
8924aa3df71SThierry Reding 		return -EINVAL;
8934aa3df71SThierry Reding 
894ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
89547802b09SThierry Reding 	if (err < 0)
89647802b09SThierry Reding 		return err;
89747802b09SThierry Reding 
8984aa3df71SThierry Reding 	return 0;
8994aa3df71SThierry Reding }
9004aa3df71SThierry Reding 
901cae7472eSThierry Reding static void __tegra_cursor_atomic_update(struct drm_plane *plane,
902cae7472eSThierry Reding 					 struct drm_plane_state *new_state)
9034aa3df71SThierry Reding {
90441016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
905e05162c0SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
906d5ec699dSThierry Reding 	struct tegra_drm *tegra = plane->dev->dev_private;
907d5ec699dSThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
908d5ec699dSThierry Reding 	u64 dma_mask = *dc->dev->dma_mask;
909d5ec699dSThierry Reding #endif
910d5ec699dSThierry Reding 	unsigned int x, y;
911d5ec699dSThierry Reding 	u32 value = 0;
9124aa3df71SThierry Reding 
9134aa3df71SThierry Reding 	/* rien ne va plus */
914e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
9154aa3df71SThierry Reding 		return;
9164aa3df71SThierry Reding 
917d5ec699dSThierry Reding 	/*
918d5ec699dSThierry Reding 	 * Legacy display supports hardware clipping of the cursor, but
919d5ec699dSThierry Reding 	 * nvdisplay relies on software to clip the cursor to the screen.
920d5ec699dSThierry Reding 	 */
921d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay)
922d5ec699dSThierry Reding 		value |= CURSOR_CLIP_DISPLAY;
923d5ec699dSThierry Reding 
924e05162c0SMaxime Ripard 	switch (new_state->crtc_w) {
925c7679306SThierry Reding 	case 32:
926c7679306SThierry Reding 		value |= CURSOR_SIZE_32x32;
927c7679306SThierry Reding 		break;
928c7679306SThierry Reding 
929c7679306SThierry Reding 	case 64:
930c7679306SThierry Reding 		value |= CURSOR_SIZE_64x64;
931c7679306SThierry Reding 		break;
932c7679306SThierry Reding 
933c7679306SThierry Reding 	case 128:
934c7679306SThierry Reding 		value |= CURSOR_SIZE_128x128;
935c7679306SThierry Reding 		break;
936c7679306SThierry Reding 
937c7679306SThierry Reding 	case 256:
938c7679306SThierry Reding 		value |= CURSOR_SIZE_256x256;
939c7679306SThierry Reding 		break;
940c7679306SThierry Reding 
941c7679306SThierry Reding 	default:
942c52e167bSThierry Reding 		WARN(1, "cursor size %ux%u not supported\n",
943e05162c0SMaxime Ripard 		     new_state->crtc_w, new_state->crtc_h);
9444aa3df71SThierry Reding 		return;
945c7679306SThierry Reding 	}
946c7679306SThierry Reding 
94741016fe1SMaxime Ripard 	value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff;
948c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
949c7679306SThierry Reding 
950c7679306SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
951d5ec699dSThierry Reding 	value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
952c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
953c7679306SThierry Reding #endif
954c7679306SThierry Reding 
955c7679306SThierry Reding 	/* enable cursor and set blend mode */
956c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
957c7679306SThierry Reding 	value |= CURSOR_ENABLE;
958c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
959c7679306SThierry Reding 
960c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
961c7679306SThierry Reding 	value &= ~CURSOR_DST_BLEND_MASK;
962c7679306SThierry Reding 	value &= ~CURSOR_SRC_BLEND_MASK;
963d5ec699dSThierry Reding 
964d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay)
965d5ec699dSThierry Reding 		value &= ~CURSOR_COMPOSITION_MODE_XOR;
966d5ec699dSThierry Reding 	else
967c7679306SThierry Reding 		value |= CURSOR_MODE_NORMAL;
968d5ec699dSThierry Reding 
969c7679306SThierry Reding 	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
970c7679306SThierry Reding 	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
971c7679306SThierry Reding 	value |= CURSOR_ALPHA;
972c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
973c7679306SThierry Reding 
974d5ec699dSThierry Reding 	/* nvdisplay relies on software for clipping */
975d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay) {
976d5ec699dSThierry Reding 		struct drm_rect src;
977d5ec699dSThierry Reding 
978d5ec699dSThierry Reding 		x = new_state->dst.x1;
979d5ec699dSThierry Reding 		y = new_state->dst.y1;
980d5ec699dSThierry Reding 
981d5ec699dSThierry Reding 		drm_rect_fp_to_int(&src, &new_state->src);
982d5ec699dSThierry Reding 
983d5ec699dSThierry Reding 		value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
984d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
985d5ec699dSThierry Reding 
986d5ec699dSThierry Reding 		value = (drm_rect_height(&src) & tegra->vmask) << 16 |
987d5ec699dSThierry Reding 			(drm_rect_width(&src) & tegra->hmask);
988d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
989d5ec699dSThierry Reding 	} else {
990d5ec699dSThierry Reding 		x = new_state->crtc_x;
991d5ec699dSThierry Reding 		y = new_state->crtc_y;
992d5ec699dSThierry Reding 	}
993d5ec699dSThierry Reding 
994c7679306SThierry Reding 	/* position the cursor */
995d5ec699dSThierry Reding 	value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
996c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
997c7679306SThierry Reding }
998c7679306SThierry Reding 
999cae7472eSThierry Reding static void tegra_cursor_atomic_update(struct drm_plane *plane,
1000cae7472eSThierry Reding 				       struct drm_atomic_state *state)
1001cae7472eSThierry Reding {
1002cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1003cae7472eSThierry Reding 
1004cae7472eSThierry Reding 	__tegra_cursor_atomic_update(plane, new_state);
1005cae7472eSThierry Reding }
1006cae7472eSThierry Reding 
10074aa3df71SThierry Reding static void tegra_cursor_atomic_disable(struct drm_plane *plane,
1008977697e2SMaxime Ripard 					struct drm_atomic_state *state)
1009c7679306SThierry Reding {
1010977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
1011977697e2SMaxime Ripard 									   plane);
10124aa3df71SThierry Reding 	struct tegra_dc *dc;
1013c7679306SThierry Reding 	u32 value;
1014c7679306SThierry Reding 
10154aa3df71SThierry Reding 	/* rien ne va plus */
10164aa3df71SThierry Reding 	if (!old_state || !old_state->crtc)
10174aa3df71SThierry Reding 		return;
10184aa3df71SThierry Reding 
10194aa3df71SThierry Reding 	dc = to_tegra_dc(old_state->crtc);
1020c7679306SThierry Reding 
1021c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
1022c7679306SThierry Reding 	value &= ~CURSOR_ENABLE;
1023c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
1024c7679306SThierry Reding }
1025c7679306SThierry Reding 
1026cae7472eSThierry Reding static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state)
1027cae7472eSThierry Reding {
1028cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1029cae7472eSThierry Reding 	struct drm_crtc_state *crtc_state;
1030cae7472eSThierry Reding 	int min_scale, max_scale;
1031cae7472eSThierry Reding 	int err;
1032cae7472eSThierry Reding 
1033cae7472eSThierry Reding 	crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
1034cae7472eSThierry Reding 	if (WARN_ON(!crtc_state))
1035cae7472eSThierry Reding 		return -EINVAL;
1036cae7472eSThierry Reding 
1037cae7472eSThierry Reding 	if (!crtc_state->active)
1038cae7472eSThierry Reding 		return -EINVAL;
1039cae7472eSThierry Reding 
1040cae7472eSThierry Reding 	if (plane->state->crtc != new_state->crtc ||
1041cae7472eSThierry Reding 	    plane->state->src_w != new_state->src_w ||
1042cae7472eSThierry Reding 	    plane->state->src_h != new_state->src_h ||
1043cae7472eSThierry Reding 	    plane->state->crtc_w != new_state->crtc_w ||
1044cae7472eSThierry Reding 	    plane->state->crtc_h != new_state->crtc_h ||
1045cae7472eSThierry Reding 	    plane->state->fb != new_state->fb ||
1046cae7472eSThierry Reding 	    plane->state->fb == NULL)
1047cae7472eSThierry Reding 		return -EINVAL;
1048cae7472eSThierry Reding 
1049cae7472eSThierry Reding 	min_scale = (1 << 16) / 8;
1050cae7472eSThierry Reding 	max_scale = (8 << 16) / 1;
1051cae7472eSThierry Reding 
1052cae7472eSThierry Reding 	err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale,
1053cae7472eSThierry Reding 						  true, true);
1054cae7472eSThierry Reding 	if (err < 0)
1055cae7472eSThierry Reding 		return err;
1056cae7472eSThierry Reding 
1057cae7472eSThierry Reding 	if (new_state->visible != plane->state->visible)
1058cae7472eSThierry Reding 		return -EINVAL;
1059cae7472eSThierry Reding 
1060cae7472eSThierry Reding 	return 0;
1061cae7472eSThierry Reding }
1062cae7472eSThierry Reding 
1063cae7472eSThierry Reding static void tegra_cursor_atomic_async_update(struct drm_plane *plane,
1064cae7472eSThierry Reding 					     struct drm_atomic_state *state)
1065cae7472eSThierry Reding {
1066cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1067cae7472eSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
1068cae7472eSThierry Reding 
1069cae7472eSThierry Reding 	plane->state->src_x = new_state->src_x;
1070cae7472eSThierry Reding 	plane->state->src_y = new_state->src_y;
1071cae7472eSThierry Reding 	plane->state->crtc_x = new_state->crtc_x;
1072cae7472eSThierry Reding 	plane->state->crtc_y = new_state->crtc_y;
1073cae7472eSThierry Reding 
1074cae7472eSThierry Reding 	if (new_state->visible) {
1075cae7472eSThierry Reding 		struct tegra_plane *p = to_tegra_plane(plane);
1076cae7472eSThierry Reding 		u32 value;
1077cae7472eSThierry Reding 
1078cae7472eSThierry Reding 		__tegra_cursor_atomic_update(plane, new_state);
1079cae7472eSThierry Reding 
1080cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE;
1081cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1082cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1083cae7472eSThierry Reding 
1084cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ;
1085cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1086cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1087cae7472eSThierry Reding 	}
1088cae7472eSThierry Reding }
1089cae7472eSThierry Reding 
10904aa3df71SThierry Reding static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
10912e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
10922e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
10934aa3df71SThierry Reding 	.atomic_check = tegra_cursor_atomic_check,
10944aa3df71SThierry Reding 	.atomic_update = tegra_cursor_atomic_update,
10954aa3df71SThierry Reding 	.atomic_disable = tegra_cursor_atomic_disable,
1096cae7472eSThierry Reding 	.atomic_async_check = tegra_cursor_atomic_async_check,
1097cae7472eSThierry Reding 	.atomic_async_update = tegra_cursor_atomic_async_update,
1098c7679306SThierry Reding };
1099c7679306SThierry Reding 
1100be4306adSDaniel Vetter static const uint64_t linear_modifiers[] = {
1101be4306adSDaniel Vetter 	DRM_FORMAT_MOD_LINEAR,
1102be4306adSDaniel Vetter 	DRM_FORMAT_MOD_INVALID
1103be4306adSDaniel Vetter };
1104be4306adSDaniel Vetter 
1105c7679306SThierry Reding static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
1106c7679306SThierry Reding 						      struct tegra_dc *dc)
1107c7679306SThierry Reding {
110889f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1109c7679306SThierry Reding 	struct tegra_plane *plane;
1110c7679306SThierry Reding 	unsigned int num_formats;
1111c7679306SThierry Reding 	const u32 *formats;
1112c7679306SThierry Reding 	int err;
1113c7679306SThierry Reding 
1114c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1115c7679306SThierry Reding 	if (!plane)
1116c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1117c7679306SThierry Reding 
111847802b09SThierry Reding 	/*
1119a1df3b24SThierry Reding 	 * This index is kind of fake. The cursor isn't a regular plane, but
1120a1df3b24SThierry Reding 	 * its update and activation request bits in DC_CMD_STATE_CONTROL do
1121a1df3b24SThierry Reding 	 * use the same programming. Setting this fake index here allows the
1122a1df3b24SThierry Reding 	 * code in tegra_add_plane_state() to do the right thing without the
1123a1df3b24SThierry Reding 	 * need to special-casing the cursor plane.
112447802b09SThierry Reding 	 */
112547802b09SThierry Reding 	plane->index = 6;
11261087fac1SThierry Reding 	plane->dc = dc;
112747802b09SThierry Reding 
1128d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay) {
1129d5ec699dSThierry Reding 		num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
1130d5ec699dSThierry Reding 		formats = tegra_legacy_cursor_plane_formats;
113104d5d5dfSDmitry Osipenko 
113204d5d5dfSDmitry Osipenko 		err = tegra_plane_interconnect_init(plane);
113304d5d5dfSDmitry Osipenko 		if (err) {
113404d5d5dfSDmitry Osipenko 			kfree(plane);
113504d5d5dfSDmitry Osipenko 			return ERR_PTR(err);
113604d5d5dfSDmitry Osipenko 		}
1137d5ec699dSThierry Reding 	} else {
1138c7679306SThierry Reding 		num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
1139c7679306SThierry Reding 		formats = tegra_cursor_plane_formats;
1140d5ec699dSThierry Reding 	}
1141c7679306SThierry Reding 
114289f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1143c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
1144be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1145e6fc3b68SBen Widawsky 				       DRM_PLANE_TYPE_CURSOR, NULL);
1146c7679306SThierry Reding 	if (err < 0) {
1147c7679306SThierry Reding 		kfree(plane);
1148c7679306SThierry Reding 		return ERR_PTR(err);
1149c7679306SThierry Reding 	}
1150c7679306SThierry Reding 
11514aa3df71SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
1152fce3a51dSThierry Reding 	drm_plane_create_zpos_immutable_property(&plane->base, 255);
11534aa3df71SThierry Reding 
1154c7679306SThierry Reding 	return &plane->base;
1155c7679306SThierry Reding }
1156c7679306SThierry Reding 
1157511c7023SThierry Reding static const u32 tegra20_overlay_formats[] = {
1158511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1159511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1160dee8268fSThierry Reding 	DRM_FORMAT_RGB565,
1161511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1162511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1163511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1164ebae8d07SThierry Reding 	/* non-native formats */
1165ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
1166ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
1167ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
1168ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
1169511c7023SThierry Reding 	/* planar formats */
1170511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1171511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1172511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1173511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1174511c7023SThierry Reding };
1175511c7023SThierry Reding 
1176511c7023SThierry Reding static const u32 tegra114_overlay_formats[] = {
1177511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1178511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1179511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1180511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1181511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1182511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1183511c7023SThierry Reding 	/* new on Tegra114 */
1184511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1185511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1186511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1187511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1188511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1189511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1190511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1191511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1192511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1193511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1194511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1195511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1196511c7023SThierry Reding 	/* planar formats */
1197511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1198511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1199511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1200511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1201a649b133SThierry Reding 	/* semi-planar formats */
1202a649b133SThierry Reding 	DRM_FORMAT_NV12,
1203a649b133SThierry Reding 	DRM_FORMAT_NV21,
1204a649b133SThierry Reding 	DRM_FORMAT_NV16,
1205a649b133SThierry Reding 	DRM_FORMAT_NV61,
1206a649b133SThierry Reding 	DRM_FORMAT_NV24,
1207a649b133SThierry Reding 	DRM_FORMAT_NV42,
1208511c7023SThierry Reding };
1209511c7023SThierry Reding 
1210511c7023SThierry Reding static const u32 tegra124_overlay_formats[] = {
1211511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1212511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1213511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1214511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1215511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1216511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1217511c7023SThierry Reding 	/* new on Tegra114 */
1218511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1219511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1220511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1221511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1222511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1223511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1224511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1225511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1226511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1227511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1228511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1229511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1230511c7023SThierry Reding 	/* new on Tegra124 */
1231511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
1232511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
1233511c7023SThierry Reding 	/* planar formats */
1234dee8268fSThierry Reding 	DRM_FORMAT_UYVY,
1235f925390eSThierry Reding 	DRM_FORMAT_YUYV,
1236cf5086d3SThierry Reding 	DRM_FORMAT_YVYU,
1237cf5086d3SThierry Reding 	DRM_FORMAT_VYUY,
1238cf5086d3SThierry Reding 	DRM_FORMAT_YUV420, /* YU12 */
1239cf5086d3SThierry Reding 	DRM_FORMAT_YUV422, /* YU16 */
1240cf5086d3SThierry Reding 	DRM_FORMAT_YUV444, /* YU24 */
1241a649b133SThierry Reding 	/* semi-planar formats */
1242a649b133SThierry Reding 	DRM_FORMAT_NV12,
1243a649b133SThierry Reding 	DRM_FORMAT_NV21,
1244a649b133SThierry Reding 	DRM_FORMAT_NV16,
1245a649b133SThierry Reding 	DRM_FORMAT_NV61,
1246a649b133SThierry Reding 	DRM_FORMAT_NV24,
1247a649b133SThierry Reding 	DRM_FORMAT_NV42,
1248dee8268fSThierry Reding };
1249dee8268fSThierry Reding 
1250c7679306SThierry Reding static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
1251c7679306SThierry Reding 						       struct tegra_dc *dc,
12529f446d83SDmitry Osipenko 						       unsigned int index,
12539f446d83SDmitry Osipenko 						       bool cursor)
1254dee8268fSThierry Reding {
125589f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1256dee8268fSThierry Reding 	struct tegra_plane *plane;
1257c7679306SThierry Reding 	unsigned int num_formats;
12589f446d83SDmitry Osipenko 	enum drm_plane_type type;
1259c7679306SThierry Reding 	const u32 *formats;
1260c7679306SThierry Reding 	int err;
1261dee8268fSThierry Reding 
1262f002abc1SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1263dee8268fSThierry Reding 	if (!plane)
1264c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1265dee8268fSThierry Reding 
12661087fac1SThierry Reding 	plane->offset = 0xa00 + 0x200 * index;
1267c7679306SThierry Reding 	plane->index = index;
12681087fac1SThierry Reding 	plane->dc = dc;
1269dee8268fSThierry Reding 
1270511c7023SThierry Reding 	num_formats = dc->soc->num_overlay_formats;
1271511c7023SThierry Reding 	formats = dc->soc->overlay_formats;
1272c7679306SThierry Reding 
127304d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
127404d5d5dfSDmitry Osipenko 	if (err) {
127504d5d5dfSDmitry Osipenko 		kfree(plane);
127604d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
127704d5d5dfSDmitry Osipenko 	}
127804d5d5dfSDmitry Osipenko 
12799f446d83SDmitry Osipenko 	if (!cursor)
12809f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_OVERLAY;
12819f446d83SDmitry Osipenko 	else
12829f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_CURSOR;
12839f446d83SDmitry Osipenko 
128489f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1285301e0ddbSThierry Reding 				       &tegra_plane_funcs, formats,
1286be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1287be4306adSDaniel Vetter 				       type, NULL);
1288f002abc1SThierry Reding 	if (err < 0) {
1289f002abc1SThierry Reding 		kfree(plane);
1290c7679306SThierry Reding 		return ERR_PTR(err);
1291dee8268fSThierry Reding 	}
1292c7679306SThierry Reding 
1293a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
12943dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
1295ab7d3f58SThierry Reding 
1296995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
1297995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
1298995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
12994fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
1300cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
1301995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
1302995c5a50SThierry Reding 	if (err < 0)
1303995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
1304995c5a50SThierry Reding 			err);
1305995c5a50SThierry Reding 
1306c7679306SThierry Reding 	return &plane->base;
1307c7679306SThierry Reding }
1308c7679306SThierry Reding 
130947307954SThierry Reding static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
131047307954SThierry Reding 						    struct tegra_dc *dc)
1311c7679306SThierry Reding {
131247307954SThierry Reding 	struct drm_plane *plane, *primary = NULL;
131347307954SThierry Reding 	unsigned int i, j;
131447307954SThierry Reding 
131547307954SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
131647307954SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
131747307954SThierry Reding 
131847307954SThierry Reding 		if (wgrp->dc == dc->pipe) {
131947307954SThierry Reding 			for (j = 0; j < wgrp->num_windows; j++) {
132047307954SThierry Reding 				unsigned int index = wgrp->windows[j];
132147307954SThierry Reding 
132247307954SThierry Reding 				plane = tegra_shared_plane_create(drm, dc,
132347307954SThierry Reding 								  wgrp->index,
132447307954SThierry Reding 								  index);
132547307954SThierry Reding 				if (IS_ERR(plane))
132647307954SThierry Reding 					return plane;
132747307954SThierry Reding 
132847307954SThierry Reding 				/*
132947307954SThierry Reding 				 * Choose the first shared plane owned by this
133047307954SThierry Reding 				 * head as the primary plane.
133147307954SThierry Reding 				 */
133247307954SThierry Reding 				if (!primary) {
133347307954SThierry Reding 					plane->type = DRM_PLANE_TYPE_PRIMARY;
133447307954SThierry Reding 					primary = plane;
133547307954SThierry Reding 				}
133647307954SThierry Reding 			}
133747307954SThierry Reding 		}
133847307954SThierry Reding 	}
133947307954SThierry Reding 
134047307954SThierry Reding 	return primary;
134147307954SThierry Reding }
134247307954SThierry Reding 
134347307954SThierry Reding static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
134447307954SThierry Reding 					     struct tegra_dc *dc)
134547307954SThierry Reding {
13468f62142eSThierry Reding 	struct drm_plane *planes[2], *primary;
13479f446d83SDmitry Osipenko 	unsigned int planes_num;
1348c7679306SThierry Reding 	unsigned int i;
13498f62142eSThierry Reding 	int err;
1350c7679306SThierry Reding 
135147307954SThierry Reding 	primary = tegra_primary_plane_create(drm, dc);
135247307954SThierry Reding 	if (IS_ERR(primary))
135347307954SThierry Reding 		return primary;
135447307954SThierry Reding 
13559f446d83SDmitry Osipenko 	if (dc->soc->supports_cursor)
13569f446d83SDmitry Osipenko 		planes_num = 2;
13579f446d83SDmitry Osipenko 	else
13589f446d83SDmitry Osipenko 		planes_num = 1;
13599f446d83SDmitry Osipenko 
13609f446d83SDmitry Osipenko 	for (i = 0; i < planes_num; i++) {
13619f446d83SDmitry Osipenko 		planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i,
13629f446d83SDmitry Osipenko 							  false);
13638f62142eSThierry Reding 		if (IS_ERR(planes[i])) {
13648f62142eSThierry Reding 			err = PTR_ERR(planes[i]);
13658f62142eSThierry Reding 
13668f62142eSThierry Reding 			while (i--)
136740dc962dSThierry Reding 				planes[i]->funcs->destroy(planes[i]);
13688f62142eSThierry Reding 
136940dc962dSThierry Reding 			primary->funcs->destroy(primary);
13708f62142eSThierry Reding 			return ERR_PTR(err);
137147307954SThierry Reding 		}
1372f002abc1SThierry Reding 	}
1373dee8268fSThierry Reding 
137447307954SThierry Reding 	return primary;
1375dee8268fSThierry Reding }
1376dee8268fSThierry Reding 
1377f002abc1SThierry Reding static void tegra_dc_destroy(struct drm_crtc *crtc)
1378f002abc1SThierry Reding {
1379f002abc1SThierry Reding 	drm_crtc_cleanup(crtc);
1380f002abc1SThierry Reding }
1381f002abc1SThierry Reding 
1382ca915b10SThierry Reding static void tegra_crtc_reset(struct drm_crtc *crtc)
1383ca915b10SThierry Reding {
1384b7e0b04aSMaarten Lankhorst 	struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
1385ca915b10SThierry Reding 
13863b59b7acSThierry Reding 	if (crtc->state)
1387b7e0b04aSMaarten Lankhorst 		tegra_crtc_atomic_destroy_state(crtc, crtc->state);
13883b59b7acSThierry Reding 
1389b7e0b04aSMaarten Lankhorst 	__drm_atomic_helper_crtc_reset(crtc, &state->base);
1390ca915b10SThierry Reding }
1391ca915b10SThierry Reding 
1392ca915b10SThierry Reding static struct drm_crtc_state *
1393ca915b10SThierry Reding tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
1394ca915b10SThierry Reding {
1395ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc->state);
1396ca915b10SThierry Reding 	struct tegra_dc_state *copy;
1397ca915b10SThierry Reding 
13983b59b7acSThierry Reding 	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
1399ca915b10SThierry Reding 	if (!copy)
1400ca915b10SThierry Reding 		return NULL;
1401ca915b10SThierry Reding 
14023b59b7acSThierry Reding 	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
14033b59b7acSThierry Reding 	copy->clk = state->clk;
14043b59b7acSThierry Reding 	copy->pclk = state->pclk;
14053b59b7acSThierry Reding 	copy->div = state->div;
14063b59b7acSThierry Reding 	copy->planes = state->planes;
1407ca915b10SThierry Reding 
1408ca915b10SThierry Reding 	return &copy->base;
1409ca915b10SThierry Reding }
1410ca915b10SThierry Reding 
1411ca915b10SThierry Reding static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
1412ca915b10SThierry Reding 					    struct drm_crtc_state *state)
1413ca915b10SThierry Reding {
1414ec2dc6a0SDaniel Vetter 	__drm_atomic_helper_crtc_destroy_state(state);
1415ca915b10SThierry Reding 	kfree(state);
1416ca915b10SThierry Reding }
1417ca915b10SThierry Reding 
1418b95800eeSThierry Reding #define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
1419b95800eeSThierry Reding 
1420b95800eeSThierry Reding static const struct debugfs_reg32 tegra_dc_regs[] = {
1421b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT),
1422b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL),
1423b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR),
1424b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT),
1425b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL),
1426b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR),
1427b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT),
1428b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL),
1429b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR),
1430b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT),
1431b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL),
1432b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR),
1433b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC),
1434b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0),
1435b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND),
1436b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE),
1437b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL),
1438b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_STATUS),
1439b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_MASK),
1440b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_ENABLE),
1441b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_TYPE),
1442b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_POLARITY),
1443b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1),
1444b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2),
1445b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3),
1446b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_ACCESS),
1447b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_CONTROL),
1448b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER),
1449b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL),
1450b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CONTROL),
1451b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM),
1452b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)),
1453b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)),
1454b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)),
1455b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)),
1456b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)),
1457b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)),
1458b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)),
1459b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)),
1460b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)),
1461b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)),
1462b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)),
1463b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)),
1464b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)),
1465b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)),
1466b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)),
1467b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)),
1468b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)),
1469b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)),
1470b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)),
1471b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)),
1472b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)),
1473b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)),
1474b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)),
1475b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
1476b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
1477b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
1478b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
1479b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
1480b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
1481b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
1482b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_CONTROL),
1483b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
1484b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
1485b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD),
1486b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_CS_DC),
1487b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A),
1488b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B),
1489b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_CTRL),
1490b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER),
1491b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED),
1492b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0),
1493b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1),
1494b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS),
1495b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY),
1496b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER),
1497b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS),
1498b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_REF_TO_SYNC),
1499b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SYNC_WIDTH),
1500b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BACK_PORCH),
1501b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_ACTIVE),
1502b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_FRONT_PORCH),
1503b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL),
1504b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A),
1505b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B),
1506b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C),
1507b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D),
1508b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL),
1509b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A),
1510b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B),
1511b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C),
1512b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D),
1513b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL),
1514b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A),
1515b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B),
1516b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C),
1517b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D),
1518b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL),
1519b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A),
1520b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B),
1521b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C),
1522b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL),
1523b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A),
1524b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B),
1525b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C),
1526b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL),
1527b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A),
1528b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL),
1529b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A),
1530b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M0_CONTROL),
1531b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M1_CONTROL),
1532b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DI_CONTROL),
1533b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_CONTROL),
1534b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_A),
1535b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_B),
1536b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_C),
1537b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_D),
1538b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL),
1539b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL),
1540b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL),
1541b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS),
1542b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS),
1543b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS),
1544b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS),
1545b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BORDER_COLOR),
1546b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER),
1547b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER),
1548b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER),
1549b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER),
1550b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND),
1551b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND),
1552b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR),
1553b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS),
1554b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION),
1555b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS),
1556b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL),
1557b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A),
1558b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B),
1559b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C),
1560b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D),
1561b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL),
1562b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST),
1563b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST),
1564b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST),
1565b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST),
1566b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL),
1567b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL),
1568b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CONTROL),
1569b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF),
1570b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(0)),
1571b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(1)),
1572b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(2)),
1573b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(3)),
1574b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(4)),
1575b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(5)),
1576b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(6)),
1577b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(7)),
1578b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(8)),
1579b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL),
1580b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT),
1581b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)),
1582b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)),
1583b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)),
1584b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)),
1585b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)),
1586b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)),
1587b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)),
1588b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)),
1589b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)),
1590b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)),
1591b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)),
1592b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)),
1593b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL),
1594b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES),
1595b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES),
1596b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI),
1597b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL),
1598b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_WIN_OPTIONS),
1599b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BYTE_SWAP),
1600b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL),
1601b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_COLOR_DEPTH),
1602b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_POSITION),
1603b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_SIZE),
1604b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE),
1605b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA),
1606b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA),
1607b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DDA_INC),
1608b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_LINE_STRIDE),
1609b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUF_STRIDE),
1610b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE),
1611b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE),
1612b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DV_CONTROL),
1613b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_NOKEY),
1614b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_1WIN),
1615b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X),
1616b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y),
1617b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY),
1618b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL),
1619b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR),
1620b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS),
1621b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U),
1622b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS),
1623b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V),
1624b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS),
1625b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET),
1626b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS),
1627b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET),
1628b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS),
1629b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS),
1630b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS),
1631b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS),
1632b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS),
1633b95800eeSThierry Reding };
1634b95800eeSThierry Reding 
1635b95800eeSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data)
1636b95800eeSThierry Reding {
1637b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1638b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1639b95800eeSThierry Reding 	unsigned int i;
1640b95800eeSThierry Reding 	int err = 0;
1641b95800eeSThierry Reding 
1642b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1643b95800eeSThierry Reding 
1644b95800eeSThierry Reding 	if (!dc->base.state->active) {
1645b95800eeSThierry Reding 		err = -EBUSY;
1646b95800eeSThierry Reding 		goto unlock;
1647b95800eeSThierry Reding 	}
1648b95800eeSThierry Reding 
1649b95800eeSThierry Reding 	for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) {
1650b95800eeSThierry Reding 		unsigned int offset = tegra_dc_regs[i].offset;
1651b95800eeSThierry Reding 
1652b95800eeSThierry Reding 		seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name,
1653b95800eeSThierry Reding 			   offset, tegra_dc_readl(dc, offset));
1654b95800eeSThierry Reding 	}
1655b95800eeSThierry Reding 
1656b95800eeSThierry Reding unlock:
1657b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1658b95800eeSThierry Reding 	return err;
1659b95800eeSThierry Reding }
1660b95800eeSThierry Reding 
1661b95800eeSThierry Reding static int tegra_dc_show_crc(struct seq_file *s, void *data)
1662b95800eeSThierry Reding {
1663b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1664b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1665b95800eeSThierry Reding 	int err = 0;
1666b95800eeSThierry Reding 	u32 value;
1667b95800eeSThierry Reding 
1668b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1669b95800eeSThierry Reding 
1670b95800eeSThierry Reding 	if (!dc->base.state->active) {
1671b95800eeSThierry Reding 		err = -EBUSY;
1672b95800eeSThierry Reding 		goto unlock;
1673b95800eeSThierry Reding 	}
1674b95800eeSThierry Reding 
1675b95800eeSThierry Reding 	value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
1676b95800eeSThierry Reding 	tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
1677b95800eeSThierry Reding 	tegra_dc_commit(dc);
1678b95800eeSThierry Reding 
1679b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1680b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1681b95800eeSThierry Reding 
1682b95800eeSThierry Reding 	value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM);
1683b95800eeSThierry Reding 	seq_printf(s, "%08x\n", value);
1684b95800eeSThierry Reding 
1685b95800eeSThierry Reding 	tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
1686b95800eeSThierry Reding 
1687b95800eeSThierry Reding unlock:
1688b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1689b95800eeSThierry Reding 	return err;
1690b95800eeSThierry Reding }
1691b95800eeSThierry Reding 
1692b95800eeSThierry Reding static int tegra_dc_show_stats(struct seq_file *s, void *data)
1693b95800eeSThierry Reding {
1694b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1695b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1696b95800eeSThierry Reding 
1697b95800eeSThierry Reding 	seq_printf(s, "frames: %lu\n", dc->stats.frames);
1698b95800eeSThierry Reding 	seq_printf(s, "vblank: %lu\n", dc->stats.vblank);
1699b95800eeSThierry Reding 	seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
1700b95800eeSThierry Reding 	seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
1701b95800eeSThierry Reding 
1702ad85b084SDmitry Osipenko 	seq_printf(s, "frames total: %lu\n", dc->stats.frames_total);
1703ad85b084SDmitry Osipenko 	seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total);
1704ad85b084SDmitry Osipenko 	seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total);
1705ad85b084SDmitry Osipenko 	seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total);
1706ad85b084SDmitry Osipenko 
1707b95800eeSThierry Reding 	return 0;
1708b95800eeSThierry Reding }
1709b95800eeSThierry Reding 
1710b95800eeSThierry Reding static struct drm_info_list debugfs_files[] = {
1711b95800eeSThierry Reding 	{ "regs", tegra_dc_show_regs, 0, NULL },
1712b95800eeSThierry Reding 	{ "crc", tegra_dc_show_crc, 0, NULL },
1713b95800eeSThierry Reding 	{ "stats", tegra_dc_show_stats, 0, NULL },
1714b95800eeSThierry Reding };
1715b95800eeSThierry Reding 
1716b95800eeSThierry Reding static int tegra_dc_late_register(struct drm_crtc *crtc)
1717b95800eeSThierry Reding {
1718b95800eeSThierry Reding 	unsigned int i, count = ARRAY_SIZE(debugfs_files);
1719b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
172039f55c61SArnd Bergmann 	struct dentry *root;
1721b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1722b95800eeSThierry Reding 
172339f55c61SArnd Bergmann #ifdef CONFIG_DEBUG_FS
172439f55c61SArnd Bergmann 	root = crtc->debugfs_entry;
172539f55c61SArnd Bergmann #else
172639f55c61SArnd Bergmann 	root = NULL;
172739f55c61SArnd Bergmann #endif
172839f55c61SArnd Bergmann 
1729b95800eeSThierry Reding 	dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
1730b95800eeSThierry Reding 				    GFP_KERNEL);
1731b95800eeSThierry Reding 	if (!dc->debugfs_files)
1732b95800eeSThierry Reding 		return -ENOMEM;
1733b95800eeSThierry Reding 
1734b95800eeSThierry Reding 	for (i = 0; i < count; i++)
1735b95800eeSThierry Reding 		dc->debugfs_files[i].data = dc;
1736b95800eeSThierry Reding 
1737ad6d94f2SWambui Karuga 	drm_debugfs_create_files(dc->debugfs_files, count, root, minor);
1738b95800eeSThierry Reding 
1739b95800eeSThierry Reding 	return 0;
1740b95800eeSThierry Reding }
1741b95800eeSThierry Reding 
1742b95800eeSThierry Reding static void tegra_dc_early_unregister(struct drm_crtc *crtc)
1743b95800eeSThierry Reding {
1744b95800eeSThierry Reding 	unsigned int count = ARRAY_SIZE(debugfs_files);
1745b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
1746b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1747b95800eeSThierry Reding 
1748b95800eeSThierry Reding 	drm_debugfs_remove_files(dc->debugfs_files, count, minor);
1749b95800eeSThierry Reding 	kfree(dc->debugfs_files);
1750b95800eeSThierry Reding 	dc->debugfs_files = NULL;
1751b95800eeSThierry Reding }
1752b95800eeSThierry Reding 
1753c49c81e2SThierry Reding static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
1754c49c81e2SThierry Reding {
1755c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1756c49c81e2SThierry Reding 
175747307954SThierry Reding 	/* XXX vblank syncpoints don't work with nvdisplay yet */
175847307954SThierry Reding 	if (dc->syncpt && !dc->soc->has_nvdisplay)
1759c49c81e2SThierry Reding 		return host1x_syncpt_read(dc->syncpt);
1760c49c81e2SThierry Reding 
1761c49c81e2SThierry Reding 	/* fallback to software emulated VBLANK counter */
17623abe2413SDhinakaran Pandiyan 	return (u32)drm_crtc_vblank_count(&dc->base);
1763c49c81e2SThierry Reding }
1764c49c81e2SThierry Reding 
1765c49c81e2SThierry Reding static int tegra_dc_enable_vblank(struct drm_crtc *crtc)
1766c49c81e2SThierry Reding {
1767c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1768363541e8SThierry Reding 	u32 value;
1769c49c81e2SThierry Reding 
1770c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1771c49c81e2SThierry Reding 	value |= VBLANK_INT;
1772c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1773c49c81e2SThierry Reding 
1774c49c81e2SThierry Reding 	return 0;
1775c49c81e2SThierry Reding }
1776c49c81e2SThierry Reding 
1777c49c81e2SThierry Reding static void tegra_dc_disable_vblank(struct drm_crtc *crtc)
1778c49c81e2SThierry Reding {
1779c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1780363541e8SThierry Reding 	u32 value;
1781c49c81e2SThierry Reding 
1782c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1783c49c81e2SThierry Reding 	value &= ~VBLANK_INT;
1784c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1785c49c81e2SThierry Reding }
1786c49c81e2SThierry Reding 
1787dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = {
17881503ca47SThierry Reding 	.page_flip = drm_atomic_helper_page_flip,
178974f48791SThierry Reding 	.set_config = drm_atomic_helper_set_config,
1790f002abc1SThierry Reding 	.destroy = tegra_dc_destroy,
1791ca915b10SThierry Reding 	.reset = tegra_crtc_reset,
1792ca915b10SThierry Reding 	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
1793ca915b10SThierry Reding 	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
1794b95800eeSThierry Reding 	.late_register = tegra_dc_late_register,
1795b95800eeSThierry Reding 	.early_unregister = tegra_dc_early_unregister,
179610437d9bSShawn Guo 	.get_vblank_counter = tegra_dc_get_vblank_counter,
179710437d9bSShawn Guo 	.enable_vblank = tegra_dc_enable_vblank,
179810437d9bSShawn Guo 	.disable_vblank = tegra_dc_disable_vblank,
1799dee8268fSThierry Reding };
1800dee8268fSThierry Reding 
1801dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc,
1802dee8268fSThierry Reding 				struct drm_display_mode *mode)
1803dee8268fSThierry Reding {
18040444c0ffSThierry Reding 	unsigned int h_ref_to_sync = 1;
18050444c0ffSThierry Reding 	unsigned int v_ref_to_sync = 1;
1806dee8268fSThierry Reding 	unsigned long value;
1807dee8268fSThierry Reding 
180847307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
1809dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
1810dee8268fSThierry Reding 
1811dee8268fSThierry Reding 		value = (v_ref_to_sync << 16) | h_ref_to_sync;
1812dee8268fSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
181347307954SThierry Reding 	}
1814dee8268fSThierry Reding 
1815dee8268fSThierry Reding 	value = ((mode->vsync_end - mode->vsync_start) << 16) |
1816dee8268fSThierry Reding 		((mode->hsync_end - mode->hsync_start) <<  0);
1817dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
1818dee8268fSThierry Reding 
1819dee8268fSThierry Reding 	value = ((mode->vtotal - mode->vsync_end) << 16) |
1820dee8268fSThierry Reding 		((mode->htotal - mode->hsync_end) <<  0);
1821dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
1822dee8268fSThierry Reding 
1823dee8268fSThierry Reding 	value = ((mode->vsync_start - mode->vdisplay) << 16) |
1824dee8268fSThierry Reding 		((mode->hsync_start - mode->hdisplay) <<  0);
1825dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
1826dee8268fSThierry Reding 
1827dee8268fSThierry Reding 	value = (mode->vdisplay << 16) | mode->hdisplay;
1828dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
1829dee8268fSThierry Reding 
1830dee8268fSThierry Reding 	return 0;
1831dee8268fSThierry Reding }
1832dee8268fSThierry Reding 
18339d910b60SThierry Reding /**
18349d910b60SThierry Reding  * tegra_dc_state_setup_clock - check clock settings and store them in atomic
18359d910b60SThierry Reding  *     state
18369d910b60SThierry Reding  * @dc: display controller
18379d910b60SThierry Reding  * @crtc_state: CRTC atomic state
18389d910b60SThierry Reding  * @clk: parent clock for display controller
18399d910b60SThierry Reding  * @pclk: pixel clock
18409d910b60SThierry Reding  * @div: shift clock divider
18419d910b60SThierry Reding  *
18429d910b60SThierry Reding  * Returns:
18439d910b60SThierry Reding  * 0 on success or a negative error-code on failure.
18449d910b60SThierry Reding  */
1845ca915b10SThierry Reding int tegra_dc_state_setup_clock(struct tegra_dc *dc,
1846ca915b10SThierry Reding 			       struct drm_crtc_state *crtc_state,
1847ca915b10SThierry Reding 			       struct clk *clk, unsigned long pclk,
1848ca915b10SThierry Reding 			       unsigned int div)
1849ca915b10SThierry Reding {
1850ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc_state);
1851ca915b10SThierry Reding 
1852d2982748SThierry Reding 	if (!clk_has_parent(dc->clk, clk))
1853d2982748SThierry Reding 		return -EINVAL;
1854d2982748SThierry Reding 
1855ca915b10SThierry Reding 	state->clk = clk;
1856ca915b10SThierry Reding 	state->pclk = pclk;
1857ca915b10SThierry Reding 	state->div = div;
1858ca915b10SThierry Reding 
1859ca915b10SThierry Reding 	return 0;
1860ca915b10SThierry Reding }
1861ca915b10SThierry Reding 
18624ce3048cSDmitry Osipenko static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
18634ce3048cSDmitry Osipenko 					  struct tegra_dc_state *state)
18644ce3048cSDmitry Osipenko {
18654ce3048cSDmitry Osipenko 	unsigned long rate, pstate;
18664ce3048cSDmitry Osipenko 	struct dev_pm_opp *opp;
18674ce3048cSDmitry Osipenko 	int err;
18684ce3048cSDmitry Osipenko 
18694ce3048cSDmitry Osipenko 	if (!dc->has_opp_table)
18704ce3048cSDmitry Osipenko 		return;
18714ce3048cSDmitry Osipenko 
18724ce3048cSDmitry Osipenko 	/* calculate actual pixel clock rate which depends on internal divider */
18734ce3048cSDmitry Osipenko 	rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
18744ce3048cSDmitry Osipenko 
18754ce3048cSDmitry Osipenko 	/* find suitable OPP for the rate */
18764ce3048cSDmitry Osipenko 	opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
18774ce3048cSDmitry Osipenko 
18784ce3048cSDmitry Osipenko 	/*
18794ce3048cSDmitry Osipenko 	 * Very high resolution modes may results in a clock rate that is
18804ce3048cSDmitry Osipenko 	 * above the characterized maximum. In this case it's okay to fall
18814ce3048cSDmitry Osipenko 	 * back to the characterized maximum.
18824ce3048cSDmitry Osipenko 	 */
18834ce3048cSDmitry Osipenko 	if (opp == ERR_PTR(-ERANGE))
18844ce3048cSDmitry Osipenko 		opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
18854ce3048cSDmitry Osipenko 
18864ce3048cSDmitry Osipenko 	if (IS_ERR(opp)) {
18874ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
18884ce3048cSDmitry Osipenko 			rate, opp);
18894ce3048cSDmitry Osipenko 		return;
18904ce3048cSDmitry Osipenko 	}
18914ce3048cSDmitry Osipenko 
18924ce3048cSDmitry Osipenko 	pstate = dev_pm_opp_get_required_pstate(opp, 0);
18934ce3048cSDmitry Osipenko 	dev_pm_opp_put(opp);
18944ce3048cSDmitry Osipenko 
18954ce3048cSDmitry Osipenko 	/*
18964ce3048cSDmitry Osipenko 	 * The minimum core voltage depends on the pixel clock rate (which
18974ce3048cSDmitry Osipenko 	 * depends on internal clock divider of the CRTC) and not on the
18984ce3048cSDmitry Osipenko 	 * rate of the display controller clock. This is why we're not using
18994ce3048cSDmitry Osipenko 	 * dev_pm_opp_set_rate() API and instead controlling the power domain
19004ce3048cSDmitry Osipenko 	 * directly.
19014ce3048cSDmitry Osipenko 	 */
19024ce3048cSDmitry Osipenko 	err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
19034ce3048cSDmitry Osipenko 	if (err)
19044ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
19054ce3048cSDmitry Osipenko 			pstate, err);
19064ce3048cSDmitry Osipenko }
19074ce3048cSDmitry Osipenko 
19080c921b6dSDmitry Osipenko static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
190976d59ed0SThierry Reding 				    struct tegra_dc_state *state)
191076d59ed0SThierry Reding {
191176d59ed0SThierry Reding 	int err;
191276d59ed0SThierry Reding 
191376d59ed0SThierry Reding 	err = clk_set_parent(dc->clk, state->clk);
191476d59ed0SThierry Reding 	if (err < 0)
191576d59ed0SThierry Reding 		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
191676d59ed0SThierry Reding 
191776d59ed0SThierry Reding 	/*
191876d59ed0SThierry Reding 	 * Outputs may not want to change the parent clock rate. This is only
191976d59ed0SThierry Reding 	 * relevant to Tegra20 where only a single display PLL is available.
192076d59ed0SThierry Reding 	 * Since that PLL would typically be used for HDMI, an internal LVDS
192176d59ed0SThierry Reding 	 * panel would need to be driven by some other clock such as PLL_P
192276d59ed0SThierry Reding 	 * which is shared with other peripherals. Changing the clock rate
192376d59ed0SThierry Reding 	 * should therefore be avoided.
192476d59ed0SThierry Reding 	 */
192576d59ed0SThierry Reding 	if (state->pclk > 0) {
192676d59ed0SThierry Reding 		err = clk_set_rate(state->clk, state->pclk);
192776d59ed0SThierry Reding 		if (err < 0)
192876d59ed0SThierry Reding 			dev_err(dc->dev,
192976d59ed0SThierry Reding 				"failed to set clock rate to %lu Hz\n",
193076d59ed0SThierry Reding 				state->pclk);
1931f8fb97c9SDmitry Osipenko 
1932f8fb97c9SDmitry Osipenko 		err = clk_set_rate(dc->clk, state->pclk);
1933f8fb97c9SDmitry Osipenko 		if (err < 0)
1934f8fb97c9SDmitry Osipenko 			dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n",
1935f8fb97c9SDmitry Osipenko 				dc->clk, state->pclk, err);
193676d59ed0SThierry Reding 	}
193776d59ed0SThierry Reding 
193876d59ed0SThierry Reding 	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
193976d59ed0SThierry Reding 		      state->div);
194076d59ed0SThierry Reding 	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
19414ce3048cSDmitry Osipenko 
19424ce3048cSDmitry Osipenko 	tegra_dc_update_voltage_state(dc, state);
194376d59ed0SThierry Reding }
194476d59ed0SThierry Reding 
1945003fc848SThierry Reding static void tegra_dc_stop(struct tegra_dc *dc)
1946003fc848SThierry Reding {
1947003fc848SThierry Reding 	u32 value;
1948003fc848SThierry Reding 
1949003fc848SThierry Reding 	/* stop the display controller */
1950003fc848SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
1951003fc848SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
1952003fc848SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
1953003fc848SThierry Reding 
1954003fc848SThierry Reding 	tegra_dc_commit(dc);
1955003fc848SThierry Reding }
1956003fc848SThierry Reding 
1957003fc848SThierry Reding static bool tegra_dc_idle(struct tegra_dc *dc)
1958003fc848SThierry Reding {
1959003fc848SThierry Reding 	u32 value;
1960003fc848SThierry Reding 
1961003fc848SThierry Reding 	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
1962003fc848SThierry Reding 
1963003fc848SThierry Reding 	return (value & DISP_CTRL_MODE_MASK) == 0;
1964003fc848SThierry Reding }
1965003fc848SThierry Reding 
1966003fc848SThierry Reding static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
1967003fc848SThierry Reding {
1968003fc848SThierry Reding 	timeout = jiffies + msecs_to_jiffies(timeout);
1969003fc848SThierry Reding 
1970003fc848SThierry Reding 	while (time_before(jiffies, timeout)) {
1971003fc848SThierry Reding 		if (tegra_dc_idle(dc))
1972003fc848SThierry Reding 			return 0;
1973003fc848SThierry Reding 
1974003fc848SThierry Reding 		usleep_range(1000, 2000);
1975003fc848SThierry Reding 	}
1976003fc848SThierry Reding 
1977003fc848SThierry Reding 	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
1978003fc848SThierry Reding 	return -ETIMEDOUT;
1979003fc848SThierry Reding }
1980003fc848SThierry Reding 
198104d5d5dfSDmitry Osipenko static void
198204d5d5dfSDmitry Osipenko tegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc,
198304d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state,
198404d5d5dfSDmitry Osipenko 				   bool prepare_bandwidth_transition)
198504d5d5dfSDmitry Osipenko {
198604d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *old_tegra_state, *new_tegra_state;
198704d5d5dfSDmitry Osipenko 	u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw;
198804d5d5dfSDmitry Osipenko 	const struct drm_plane_state *old_plane_state;
198904d5d5dfSDmitry Osipenko 	const struct drm_crtc_state *old_crtc_state;
199004d5d5dfSDmitry Osipenko 	struct tegra_dc_window window, old_window;
199104d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
199204d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
199304d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
199404d5d5dfSDmitry Osipenko 
199504d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
199604d5d5dfSDmitry Osipenko 		return;
199704d5d5dfSDmitry Osipenko 
199804d5d5dfSDmitry Osipenko 	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
199904d5d5dfSDmitry Osipenko 
200004d5d5dfSDmitry Osipenko 	if (!crtc->state->active) {
200104d5d5dfSDmitry Osipenko 		if (!old_crtc_state->active)
200204d5d5dfSDmitry Osipenko 			return;
200304d5d5dfSDmitry Osipenko 
200404d5d5dfSDmitry Osipenko 		/*
200504d5d5dfSDmitry Osipenko 		 * When CRTC is disabled on DPMS, the state of attached planes
200604d5d5dfSDmitry Osipenko 		 * is kept unchanged. Hence we need to enforce removal of the
200704d5d5dfSDmitry Osipenko 		 * bandwidths from the ICC paths.
200804d5d5dfSDmitry Osipenko 		 */
200904d5d5dfSDmitry Osipenko 		drm_atomic_crtc_for_each_plane(plane, crtc) {
201004d5d5dfSDmitry Osipenko 			tegra = to_tegra_plane(plane);
201104d5d5dfSDmitry Osipenko 
201204d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem, 0, 0);
201304d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
201404d5d5dfSDmitry Osipenko 		}
201504d5d5dfSDmitry Osipenko 
201604d5d5dfSDmitry Osipenko 		return;
201704d5d5dfSDmitry Osipenko 	}
201804d5d5dfSDmitry Osipenko 
201904d5d5dfSDmitry Osipenko 	for_each_old_plane_in_state(old_crtc_state->state, plane,
202004d5d5dfSDmitry Osipenko 				    old_plane_state, i) {
202104d5d5dfSDmitry Osipenko 		old_tegra_state = to_const_tegra_plane_state(old_plane_state);
202204d5d5dfSDmitry Osipenko 		new_tegra_state = to_const_tegra_plane_state(plane->state);
202304d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
202404d5d5dfSDmitry Osipenko 
202504d5d5dfSDmitry Osipenko 		/*
202604d5d5dfSDmitry Osipenko 		 * We're iterating over the global atomic state and it contains
202704d5d5dfSDmitry Osipenko 		 * planes from another CRTC, hence we need to filter out the
202804d5d5dfSDmitry Osipenko 		 * planes unrelated to this CRTC.
202904d5d5dfSDmitry Osipenko 		 */
203004d5d5dfSDmitry Osipenko 		if (tegra->dc != dc)
203104d5d5dfSDmitry Osipenko 			continue;
203204d5d5dfSDmitry Osipenko 
203304d5d5dfSDmitry Osipenko 		new_avg_bw = new_tegra_state->avg_memory_bandwidth;
203404d5d5dfSDmitry Osipenko 		old_avg_bw = old_tegra_state->avg_memory_bandwidth;
203504d5d5dfSDmitry Osipenko 
203604d5d5dfSDmitry Osipenko 		new_peak_bw = new_tegra_state->total_peak_memory_bandwidth;
203704d5d5dfSDmitry Osipenko 		old_peak_bw = old_tegra_state->total_peak_memory_bandwidth;
203804d5d5dfSDmitry Osipenko 
203904d5d5dfSDmitry Osipenko 		/*
204004d5d5dfSDmitry Osipenko 		 * See the comment related to !crtc->state->active above,
204104d5d5dfSDmitry Osipenko 		 * which explains why bandwidths need to be updated when
204204d5d5dfSDmitry Osipenko 		 * CRTC is turning ON.
204304d5d5dfSDmitry Osipenko 		 */
204404d5d5dfSDmitry Osipenko 		if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw &&
204504d5d5dfSDmitry Osipenko 		    old_crtc_state->active)
204604d5d5dfSDmitry Osipenko 			continue;
204704d5d5dfSDmitry Osipenko 
204804d5d5dfSDmitry Osipenko 		window.src.h = drm_rect_height(&plane->state->src) >> 16;
204904d5d5dfSDmitry Osipenko 		window.dst.h = drm_rect_height(&plane->state->dst);
205004d5d5dfSDmitry Osipenko 
205104d5d5dfSDmitry Osipenko 		old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16;
205204d5d5dfSDmitry Osipenko 		old_window.dst.h = drm_rect_height(&old_plane_state->dst);
205304d5d5dfSDmitry Osipenko 
205404d5d5dfSDmitry Osipenko 		/*
205504d5d5dfSDmitry Osipenko 		 * During the preparation phase (atomic_begin), the memory
205604d5d5dfSDmitry Osipenko 		 * freq should go high before the DC changes are committed
205704d5d5dfSDmitry Osipenko 		 * if bandwidth requirement goes up, otherwise memory freq
205804d5d5dfSDmitry Osipenko 		 * should to stay high if BW requirement goes down.  The
205904d5d5dfSDmitry Osipenko 		 * opposite applies to the completion phase (post_commit).
206004d5d5dfSDmitry Osipenko 		 */
206104d5d5dfSDmitry Osipenko 		if (prepare_bandwidth_transition) {
206204d5d5dfSDmitry Osipenko 			new_avg_bw = max(old_avg_bw, new_avg_bw);
206304d5d5dfSDmitry Osipenko 			new_peak_bw = max(old_peak_bw, new_peak_bw);
206404d5d5dfSDmitry Osipenko 
206504d5d5dfSDmitry Osipenko 			if (tegra_plane_use_vertical_filtering(tegra, &old_window))
206604d5d5dfSDmitry Osipenko 				window = old_window;
206704d5d5dfSDmitry Osipenko 		}
206804d5d5dfSDmitry Osipenko 
206904d5d5dfSDmitry Osipenko 		icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw);
207004d5d5dfSDmitry Osipenko 
207104d5d5dfSDmitry Osipenko 		if (tegra_plane_use_vertical_filtering(tegra, &window))
207204d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw);
207304d5d5dfSDmitry Osipenko 		else
207404d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
207504d5d5dfSDmitry Osipenko 	}
207604d5d5dfSDmitry Osipenko }
207704d5d5dfSDmitry Osipenko 
207864581714SLaurent Pinchart static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
2079351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
2080003fc848SThierry Reding {
2081003fc848SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2082003fc848SThierry Reding 	u32 value;
2083fd67e9c6SThierry Reding 	int err;
2084003fc848SThierry Reding 
2085003fc848SThierry Reding 	if (!tegra_dc_idle(dc)) {
2086003fc848SThierry Reding 		tegra_dc_stop(dc);
2087003fc848SThierry Reding 
2088003fc848SThierry Reding 		/*
2089003fc848SThierry Reding 		 * Ignore the return value, there isn't anything useful to do
2090003fc848SThierry Reding 		 * in case this fails.
2091003fc848SThierry Reding 		 */
2092003fc848SThierry Reding 		tegra_dc_wait_idle(dc, 100);
2093003fc848SThierry Reding 	}
2094003fc848SThierry Reding 
2095003fc848SThierry Reding 	/*
2096003fc848SThierry Reding 	 * This should really be part of the RGB encoder driver, but clearing
2097003fc848SThierry Reding 	 * these bits has the side-effect of stopping the display controller.
2098003fc848SThierry Reding 	 * When that happens no VBLANK interrupts will be raised. At the same
2099003fc848SThierry Reding 	 * time the encoder is disabled before the display controller, so the
2100003fc848SThierry Reding 	 * above code is always going to timeout waiting for the controller
2101003fc848SThierry Reding 	 * to go idle.
2102003fc848SThierry Reding 	 *
2103003fc848SThierry Reding 	 * Given the close coupling between the RGB encoder and the display
2104003fc848SThierry Reding 	 * controller doing it here is still kind of okay. None of the other
2105003fc848SThierry Reding 	 * encoder drivers require these bits to be cleared.
2106003fc848SThierry Reding 	 *
2107003fc848SThierry Reding 	 * XXX: Perhaps given that the display controller is switched off at
2108003fc848SThierry Reding 	 * this point anyway maybe clearing these bits isn't even useful for
2109003fc848SThierry Reding 	 * the RGB encoder?
2110003fc848SThierry Reding 	 */
2111003fc848SThierry Reding 	if (dc->rgb) {
2112003fc848SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2113003fc848SThierry Reding 		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2114003fc848SThierry Reding 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
2115003fc848SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
2116003fc848SThierry Reding 	}
2117003fc848SThierry Reding 
2118003fc848SThierry Reding 	tegra_dc_stats_reset(&dc->stats);
2119003fc848SThierry Reding 	drm_crtc_vblank_off(crtc);
212033a8eb8dSThierry Reding 
21219d99ab6eSThierry Reding 	spin_lock_irq(&crtc->dev->event_lock);
21229d99ab6eSThierry Reding 
21239d99ab6eSThierry Reding 	if (crtc->state->event) {
21249d99ab6eSThierry Reding 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
21259d99ab6eSThierry Reding 		crtc->state->event = NULL;
21269d99ab6eSThierry Reding 	}
21279d99ab6eSThierry Reding 
21289d99ab6eSThierry Reding 	spin_unlock_irq(&crtc->dev->event_lock);
21299d99ab6eSThierry Reding 
2130fd67e9c6SThierry Reding 	err = host1x_client_suspend(&dc->client);
2131fd67e9c6SThierry Reding 	if (err < 0)
2132fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to suspend: %d\n", err);
21334ce3048cSDmitry Osipenko 
21344ce3048cSDmitry Osipenko 	if (dc->has_opp_table) {
21354ce3048cSDmitry Osipenko 		err = dev_pm_genpd_set_performance_state(dc->dev, 0);
21364ce3048cSDmitry Osipenko 		if (err)
21374ce3048cSDmitry Osipenko 			dev_err(dc->dev,
21384ce3048cSDmitry Osipenko 				"failed to clear power domain state: %d\n", err);
21394ce3048cSDmitry Osipenko 	}
2140003fc848SThierry Reding }
2141003fc848SThierry Reding 
21420b20a0f8SLaurent Pinchart static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
2143351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
2144dee8268fSThierry Reding {
21454aa3df71SThierry Reding 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
2146351f950dSMaxime Ripard 	struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
2147dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2148dbb3f2f7SThierry Reding 	u32 value;
2149fd67e9c6SThierry Reding 	int err;
2150dee8268fSThierry Reding 
21510c921b6dSDmitry Osipenko 	/* apply PLL changes */
21520c921b6dSDmitry Osipenko 	tegra_dc_set_clock_rate(dc, crtc_state);
21530c921b6dSDmitry Osipenko 
2154fd67e9c6SThierry Reding 	err = host1x_client_resume(&dc->client);
2155fd67e9c6SThierry Reding 	if (err < 0) {
2156fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to resume: %d\n", err);
2157fd67e9c6SThierry Reding 		return;
2158fd67e9c6SThierry Reding 	}
215933a8eb8dSThierry Reding 
216033a8eb8dSThierry Reding 	/* initialize display controller */
216133a8eb8dSThierry Reding 	if (dc->syncpt) {
216247307954SThierry Reding 		u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
216347307954SThierry Reding 
216447307954SThierry Reding 		if (dc->soc->has_nvdisplay)
216547307954SThierry Reding 			enable = 1 << 31;
216647307954SThierry Reding 		else
216747307954SThierry Reding 			enable = 1 << 8;
216833a8eb8dSThierry Reding 
216933a8eb8dSThierry Reding 		value = SYNCPT_CNTRL_NO_STALL;
217033a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
217133a8eb8dSThierry Reding 
217247307954SThierry Reding 		value = enable | syncpt;
217333a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
217433a8eb8dSThierry Reding 	}
217533a8eb8dSThierry Reding 
217647307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
217747307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
217847307954SThierry Reding 			DSC_OBUF_UF_INT;
217947307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
218047307954SThierry Reding 
218147307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
218247307954SThierry Reding 			DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
218347307954SThierry Reding 			HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
218447307954SThierry Reding 			REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
218547307954SThierry Reding 			VBLANK_INT | FRAME_END_INT;
218647307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
218747307954SThierry Reding 
218847307954SThierry Reding 		value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
218947307954SThierry Reding 			FRAME_END_INT;
219047307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
219147307954SThierry Reding 
219247307954SThierry Reding 		value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
219347307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
219447307954SThierry Reding 
219547307954SThierry Reding 		tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
219647307954SThierry Reding 	} else {
219733a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
219833a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
219933a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
220033a8eb8dSThierry Reding 
220133a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
220233a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
220333a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
220433a8eb8dSThierry Reding 
220533a8eb8dSThierry Reding 		/* initialize timer */
220633a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
220733a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
220833a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
220933a8eb8dSThierry Reding 
221033a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
221133a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
221233a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
221333a8eb8dSThierry Reding 
221433a8eb8dSThierry Reding 		value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
221533a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
221633a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
221733a8eb8dSThierry Reding 
221833a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
221933a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
222033a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
222147307954SThierry Reding 	}
222233a8eb8dSThierry Reding 
22237116e9a8SThierry Reding 	if (dc->soc->supports_background_color)
22247116e9a8SThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
22257116e9a8SThierry Reding 	else
222633a8eb8dSThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
222733a8eb8dSThierry Reding 
22280c921b6dSDmitry Osipenko 	/* apply pixel clock changes */
22290c921b6dSDmitry Osipenko 	if (!dc->soc->has_nvdisplay) {
22300c921b6dSDmitry Osipenko 		value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1;
22310c921b6dSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
22320c921b6dSDmitry Osipenko 	}
223376d59ed0SThierry Reding 
2234dee8268fSThierry Reding 	/* program display mode */
2235dee8268fSThierry Reding 	tegra_dc_set_timings(dc, mode);
2236dee8268fSThierry Reding 
22378620fc62SThierry Reding 	/* interlacing isn't supported yet, so disable it */
22388620fc62SThierry Reding 	if (dc->soc->supports_interlacing) {
22398620fc62SThierry Reding 		value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
22408620fc62SThierry Reding 		value &= ~INTERLACE_ENABLE;
22418620fc62SThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
22428620fc62SThierry Reding 	}
2243666cb873SThierry Reding 
2244666cb873SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
2245666cb873SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
2246666cb873SThierry Reding 	value |= DISP_CTRL_MODE_C_DISPLAY;
2247666cb873SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
2248666cb873SThierry Reding 
224947307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
2250666cb873SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2251666cb873SThierry Reding 		value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2252666cb873SThierry Reding 			 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
2253666cb873SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
225447307954SThierry Reding 	}
225547307954SThierry Reding 
225647307954SThierry Reding 	/* enable underflow reporting and display red for missing pixels */
225747307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
225847307954SThierry Reding 		value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
225947307954SThierry Reding 		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
226047307954SThierry Reding 	}
2261666cb873SThierry Reding 
2262f7d6c6aeSDmitry Osipenko 	if (dc->rgb) {
2263f7d6c6aeSDmitry Osipenko 		/* XXX: parameterize? */
2264f7d6c6aeSDmitry Osipenko 		value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
2265f7d6c6aeSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
2266f7d6c6aeSDmitry Osipenko 	}
2267f7d6c6aeSDmitry Osipenko 
2268666cb873SThierry Reding 	tegra_dc_commit(dc);
2269dee8268fSThierry Reding 
22708ff64c17SThierry Reding 	drm_crtc_vblank_on(crtc);
2271dee8268fSThierry Reding }
2272dee8268fSThierry Reding 
2273613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
2274f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22754aa3df71SThierry Reding {
22769d99ab6eSThierry Reding 	unsigned long flags;
22771503ca47SThierry Reding 
227804d5d5dfSDmitry Osipenko 	tegra_crtc_update_memory_bandwidth(crtc, state, true);
227904d5d5dfSDmitry Osipenko 
22801503ca47SThierry Reding 	if (crtc->state->event) {
22819d99ab6eSThierry Reding 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
22821503ca47SThierry Reding 
22839d99ab6eSThierry Reding 		if (drm_crtc_vblank_get(crtc) != 0)
22849d99ab6eSThierry Reding 			drm_crtc_send_vblank_event(crtc, crtc->state->event);
22859d99ab6eSThierry Reding 		else
22869d99ab6eSThierry Reding 			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
22871503ca47SThierry Reding 
22889d99ab6eSThierry Reding 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
22899d99ab6eSThierry Reding 
22901503ca47SThierry Reding 		crtc->state->event = NULL;
22911503ca47SThierry Reding 	}
22924aa3df71SThierry Reding }
22934aa3df71SThierry Reding 
2294613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
2295f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22964aa3df71SThierry Reding {
2297253f28b6SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
2298253f28b6SMaxime Ripard 									  crtc);
2299253f28b6SMaxime Ripard 	struct tegra_dc_state *dc_state = to_dc_state(crtc_state);
230047802b09SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
230147307954SThierry Reding 	u32 value;
230247802b09SThierry Reding 
2303253f28b6SMaxime Ripard 	value = dc_state->planes << 8 | GENERAL_UPDATE;
230447307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
230547307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
230647307954SThierry Reding 
2307253f28b6SMaxime Ripard 	value = dc_state->planes | GENERAL_ACT_REQ;
230847307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
230947307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
23104aa3df71SThierry Reding }
23114aa3df71SThierry Reding 
231204d5d5dfSDmitry Osipenko static bool tegra_plane_is_cursor(const struct drm_plane_state *state)
231304d5d5dfSDmitry Osipenko {
231404d5d5dfSDmitry Osipenko 	const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc;
231504d5d5dfSDmitry Osipenko 	const struct drm_format_info *fmt = state->fb->format;
231604d5d5dfSDmitry Osipenko 	unsigned int src_w = drm_rect_width(&state->src) >> 16;
231704d5d5dfSDmitry Osipenko 	unsigned int dst_w = drm_rect_width(&state->dst);
231804d5d5dfSDmitry Osipenko 
231904d5d5dfSDmitry Osipenko 	if (state->plane->type != DRM_PLANE_TYPE_CURSOR)
232004d5d5dfSDmitry Osipenko 		return false;
232104d5d5dfSDmitry Osipenko 
232204d5d5dfSDmitry Osipenko 	if (soc->supports_cursor)
232304d5d5dfSDmitry Osipenko 		return true;
232404d5d5dfSDmitry Osipenko 
232504d5d5dfSDmitry Osipenko 	if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256)
232604d5d5dfSDmitry Osipenko 		return false;
232704d5d5dfSDmitry Osipenko 
232804d5d5dfSDmitry Osipenko 	return true;
232904d5d5dfSDmitry Osipenko }
233004d5d5dfSDmitry Osipenko 
233104d5d5dfSDmitry Osipenko static unsigned long
233204d5d5dfSDmitry Osipenko tegra_plane_overlap_mask(struct drm_crtc_state *state,
233304d5d5dfSDmitry Osipenko 			 const struct drm_plane_state *plane_state)
233404d5d5dfSDmitry Osipenko {
233504d5d5dfSDmitry Osipenko 	const struct drm_plane_state *other_state;
233604d5d5dfSDmitry Osipenko 	const struct tegra_plane *tegra;
233704d5d5dfSDmitry Osipenko 	unsigned long overlap_mask = 0;
233804d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
233904d5d5dfSDmitry Osipenko 	struct drm_rect rect;
234004d5d5dfSDmitry Osipenko 
234104d5d5dfSDmitry Osipenko 	if (!plane_state->visible || !plane_state->fb)
234204d5d5dfSDmitry Osipenko 		return 0;
234304d5d5dfSDmitry Osipenko 
234404d5d5dfSDmitry Osipenko 	/*
234504d5d5dfSDmitry Osipenko 	 * Data-prefetch FIFO will easily help to overcome temporal memory
234604d5d5dfSDmitry Osipenko 	 * pressure if other plane overlaps with the cursor plane.
234704d5d5dfSDmitry Osipenko 	 */
234804d5d5dfSDmitry Osipenko 	if (tegra_plane_is_cursor(plane_state))
234904d5d5dfSDmitry Osipenko 		return 0;
235004d5d5dfSDmitry Osipenko 
235104d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) {
235204d5d5dfSDmitry Osipenko 		rect = plane_state->dst;
235304d5d5dfSDmitry Osipenko 
235404d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(other_state->plane);
235504d5d5dfSDmitry Osipenko 
235604d5d5dfSDmitry Osipenko 		if (!other_state->visible || !other_state->fb)
235704d5d5dfSDmitry Osipenko 			continue;
235804d5d5dfSDmitry Osipenko 
235904d5d5dfSDmitry Osipenko 		/*
236004d5d5dfSDmitry Osipenko 		 * Ignore cursor plane overlaps because it's not practical to
236104d5d5dfSDmitry Osipenko 		 * assume that it contributes to the bandwidth in overlapping
236204d5d5dfSDmitry Osipenko 		 * area if window width is small.
236304d5d5dfSDmitry Osipenko 		 */
236404d5d5dfSDmitry Osipenko 		if (tegra_plane_is_cursor(other_state))
236504d5d5dfSDmitry Osipenko 			continue;
236604d5d5dfSDmitry Osipenko 
236704d5d5dfSDmitry Osipenko 		if (drm_rect_intersect(&rect, &other_state->dst))
236804d5d5dfSDmitry Osipenko 			overlap_mask |= BIT(tegra->index);
236904d5d5dfSDmitry Osipenko 	}
237004d5d5dfSDmitry Osipenko 
237104d5d5dfSDmitry Osipenko 	return overlap_mask;
237204d5d5dfSDmitry Osipenko }
237304d5d5dfSDmitry Osipenko 
237404d5d5dfSDmitry Osipenko static int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc,
237504d5d5dfSDmitry Osipenko 						 struct drm_atomic_state *state)
237604d5d5dfSDmitry Osipenko {
237704d5d5dfSDmitry Osipenko 	ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask;
237804d5d5dfSDmitry Osipenko 	u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {};
237904d5d5dfSDmitry Osipenko 	bool all_planes_overlap_simultaneously = true;
238004d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *tegra_state;
238104d5d5dfSDmitry Osipenko 	const struct drm_plane_state *plane_state;
238204d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
238304d5d5dfSDmitry Osipenko 	const struct drm_crtc_state *old_state;
238404d5d5dfSDmitry Osipenko 	struct drm_crtc_state *new_state;
238504d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
238604d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
238704d5d5dfSDmitry Osipenko 
238804d5d5dfSDmitry Osipenko 	/*
238904d5d5dfSDmitry Osipenko 	 * The nv-display uses shared planes.  The algorithm below assumes
239004d5d5dfSDmitry Osipenko 	 * maximum 3 planes per-CRTC, this assumption isn't applicable to
239104d5d5dfSDmitry Osipenko 	 * the nv-display.  Note that T124 support has additional windows,
239204d5d5dfSDmitry Osipenko 	 * but currently they aren't supported by the driver.
239304d5d5dfSDmitry Osipenko 	 */
239404d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
239504d5d5dfSDmitry Osipenko 		return 0;
239604d5d5dfSDmitry Osipenko 
239704d5d5dfSDmitry Osipenko 	new_state = drm_atomic_get_new_crtc_state(state, crtc);
239804d5d5dfSDmitry Osipenko 	old_state = drm_atomic_get_old_crtc_state(state, crtc);
239904d5d5dfSDmitry Osipenko 
240004d5d5dfSDmitry Osipenko 	/*
240104d5d5dfSDmitry Osipenko 	 * For overlapping planes pixel's data is fetched for each plane at
240204d5d5dfSDmitry Osipenko 	 * the same time, hence bandwidths are accumulated in this case.
240304d5d5dfSDmitry Osipenko 	 * This needs to be taken into account for calculating total bandwidth
240404d5d5dfSDmitry Osipenko 	 * consumed by all planes.
240504d5d5dfSDmitry Osipenko 	 *
240604d5d5dfSDmitry Osipenko 	 * Here we get the overlapping state of each plane, which is a
240704d5d5dfSDmitry Osipenko 	 * bitmask of plane indices telling with what planes there is an
240804d5d5dfSDmitry Osipenko 	 * overlap. Note that bitmask[plane] includes BIT(plane) in order
240904d5d5dfSDmitry Osipenko 	 * to make further code nicer and simpler.
241004d5d5dfSDmitry Osipenko 	 */
241104d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
241204d5d5dfSDmitry Osipenko 		tegra_state = to_const_tegra_plane_state(plane_state);
241304d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
241404d5d5dfSDmitry Osipenko 
241504d5d5dfSDmitry Osipenko 		if (WARN_ON_ONCE(tegra->index >= TEGRA_DC_LEGACY_PLANES_NUM))
241604d5d5dfSDmitry Osipenko 			return -EINVAL;
241704d5d5dfSDmitry Osipenko 
241804d5d5dfSDmitry Osipenko 		plane_peak_bw[tegra->index] = tegra_state->peak_memory_bandwidth;
241904d5d5dfSDmitry Osipenko 		mask = tegra_plane_overlap_mask(new_state, plane_state);
242004d5d5dfSDmitry Osipenko 		overlap_mask[tegra->index] = mask;
242104d5d5dfSDmitry Osipenko 
242204d5d5dfSDmitry Osipenko 		if (hweight_long(mask) != 3)
242304d5d5dfSDmitry Osipenko 			all_planes_overlap_simultaneously = false;
242404d5d5dfSDmitry Osipenko 	}
242504d5d5dfSDmitry Osipenko 
242604d5d5dfSDmitry Osipenko 	/*
242704d5d5dfSDmitry Osipenko 	 * Then we calculate maximum bandwidth of each plane state.
242804d5d5dfSDmitry Osipenko 	 * The bandwidth includes the plane BW + BW of the "simultaneously"
242904d5d5dfSDmitry Osipenko 	 * overlapping planes, where "simultaneously" means areas where DC
243004d5d5dfSDmitry Osipenko 	 * fetches from the planes simultaneously during of scan-out process.
243104d5d5dfSDmitry Osipenko 	 *
243204d5d5dfSDmitry Osipenko 	 * For example, if plane A overlaps with planes B and C, but B and C
243304d5d5dfSDmitry Osipenko 	 * don't overlap, then the peak bandwidth will be either in area where
243404d5d5dfSDmitry Osipenko 	 * A-and-B or A-and-C planes overlap.
243504d5d5dfSDmitry Osipenko 	 *
243604d5d5dfSDmitry Osipenko 	 * The plane_peak_bw[] contains peak memory bandwidth values of
243704d5d5dfSDmitry Osipenko 	 * each plane, this information is needed by interconnect provider
243804d5d5dfSDmitry Osipenko 	 * in order to set up latency allowance based on the peak BW, see
243904d5d5dfSDmitry Osipenko 	 * tegra_crtc_update_memory_bandwidth().
244004d5d5dfSDmitry Osipenko 	 */
244104d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, plane_state, new_state) {
244204d5d5dfSDmitry Osipenko 		u32 i, old_peak_bw, new_peak_bw, overlap_bw = 0;
244304d5d5dfSDmitry Osipenko 
244404d5d5dfSDmitry Osipenko 		/*
244504d5d5dfSDmitry Osipenko 		 * Note that plane's atomic check doesn't touch the
244604d5d5dfSDmitry Osipenko 		 * total_peak_memory_bandwidth of enabled plane, hence the
244704d5d5dfSDmitry Osipenko 		 * current state contains the old bandwidth state from the
244804d5d5dfSDmitry Osipenko 		 * previous CRTC commit.
244904d5d5dfSDmitry Osipenko 		 */
245004d5d5dfSDmitry Osipenko 		tegra_state = to_const_tegra_plane_state(plane_state);
245104d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
245204d5d5dfSDmitry Osipenko 
245304d5d5dfSDmitry Osipenko 		for_each_set_bit(i, &overlap_mask[tegra->index], 3) {
245404d5d5dfSDmitry Osipenko 			if (i == tegra->index)
245504d5d5dfSDmitry Osipenko 				continue;
245604d5d5dfSDmitry Osipenko 
245704d5d5dfSDmitry Osipenko 			if (all_planes_overlap_simultaneously)
245804d5d5dfSDmitry Osipenko 				overlap_bw += plane_peak_bw[i];
245904d5d5dfSDmitry Osipenko 			else
246004d5d5dfSDmitry Osipenko 				overlap_bw = max(overlap_bw, plane_peak_bw[i]);
246104d5d5dfSDmitry Osipenko 		}
246204d5d5dfSDmitry Osipenko 
246304d5d5dfSDmitry Osipenko 		new_peak_bw = plane_peak_bw[tegra->index] + overlap_bw;
246404d5d5dfSDmitry Osipenko 		old_peak_bw = tegra_state->total_peak_memory_bandwidth;
246504d5d5dfSDmitry Osipenko 
246604d5d5dfSDmitry Osipenko 		/*
246704d5d5dfSDmitry Osipenko 		 * If plane's peak bandwidth changed (for example plane isn't
246804d5d5dfSDmitry Osipenko 		 * overlapped anymore) and plane isn't in the atomic state,
246904d5d5dfSDmitry Osipenko 		 * then add plane to the state in order to have the bandwidth
247004d5d5dfSDmitry Osipenko 		 * updated.
247104d5d5dfSDmitry Osipenko 		 */
247204d5d5dfSDmitry Osipenko 		if (old_peak_bw != new_peak_bw) {
247304d5d5dfSDmitry Osipenko 			struct tegra_plane_state *new_tegra_state;
247404d5d5dfSDmitry Osipenko 			struct drm_plane_state *new_plane_state;
247504d5d5dfSDmitry Osipenko 
247604d5d5dfSDmitry Osipenko 			new_plane_state = drm_atomic_get_plane_state(state, plane);
247704d5d5dfSDmitry Osipenko 			if (IS_ERR(new_plane_state))
247804d5d5dfSDmitry Osipenko 				return PTR_ERR(new_plane_state);
247904d5d5dfSDmitry Osipenko 
248004d5d5dfSDmitry Osipenko 			new_tegra_state = to_tegra_plane_state(new_plane_state);
248104d5d5dfSDmitry Osipenko 			new_tegra_state->total_peak_memory_bandwidth = new_peak_bw;
248204d5d5dfSDmitry Osipenko 		}
248304d5d5dfSDmitry Osipenko 	}
248404d5d5dfSDmitry Osipenko 
248504d5d5dfSDmitry Osipenko 	return 0;
248604d5d5dfSDmitry Osipenko }
248704d5d5dfSDmitry Osipenko 
248804d5d5dfSDmitry Osipenko static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
248904d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state)
249004d5d5dfSDmitry Osipenko {
249104d5d5dfSDmitry Osipenko 	int err;
249204d5d5dfSDmitry Osipenko 
249304d5d5dfSDmitry Osipenko 	err = tegra_crtc_calculate_memory_bandwidth(crtc, state);
249404d5d5dfSDmitry Osipenko 	if (err)
249504d5d5dfSDmitry Osipenko 		return err;
249604d5d5dfSDmitry Osipenko 
249704d5d5dfSDmitry Osipenko 	return 0;
249804d5d5dfSDmitry Osipenko }
249904d5d5dfSDmitry Osipenko 
250004d5d5dfSDmitry Osipenko void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc,
250104d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state)
250204d5d5dfSDmitry Osipenko {
250304d5d5dfSDmitry Osipenko 	/*
250404d5d5dfSDmitry Osipenko 	 * Display bandwidth is allowed to go down only once hardware state
250504d5d5dfSDmitry Osipenko 	 * is known to be armed, i.e. state was committed and VBLANK event
250604d5d5dfSDmitry Osipenko 	 * received.
250704d5d5dfSDmitry Osipenko 	 */
250804d5d5dfSDmitry Osipenko 	tegra_crtc_update_memory_bandwidth(crtc, state, false);
250904d5d5dfSDmitry Osipenko }
251004d5d5dfSDmitry Osipenko 
2511dee8268fSThierry Reding static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
251204d5d5dfSDmitry Osipenko 	.atomic_check = tegra_crtc_atomic_check,
25134aa3df71SThierry Reding 	.atomic_begin = tegra_crtc_atomic_begin,
25144aa3df71SThierry Reding 	.atomic_flush = tegra_crtc_atomic_flush,
25150b20a0f8SLaurent Pinchart 	.atomic_enable = tegra_crtc_atomic_enable,
251664581714SLaurent Pinchart 	.atomic_disable = tegra_crtc_atomic_disable,
2517dee8268fSThierry Reding };
2518dee8268fSThierry Reding 
2519dee8268fSThierry Reding static irqreturn_t tegra_dc_irq(int irq, void *data)
2520dee8268fSThierry Reding {
2521dee8268fSThierry Reding 	struct tegra_dc *dc = data;
2522dee8268fSThierry Reding 	unsigned long status;
2523dee8268fSThierry Reding 
2524dee8268fSThierry Reding 	status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
2525dee8268fSThierry Reding 	tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
2526dee8268fSThierry Reding 
2527dee8268fSThierry Reding 	if (status & FRAME_END_INT) {
2528dee8268fSThierry Reding 		/*
2529dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): frame end\n", __func__);
2530dee8268fSThierry Reding 		*/
2531ad85b084SDmitry Osipenko 		dc->stats.frames_total++;
2532791ddb1eSThierry Reding 		dc->stats.frames++;
2533dee8268fSThierry Reding 	}
2534dee8268fSThierry Reding 
2535dee8268fSThierry Reding 	if (status & VBLANK_INT) {
2536dee8268fSThierry Reding 		/*
2537dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
2538dee8268fSThierry Reding 		*/
2539ed7dae58SThierry Reding 		drm_crtc_handle_vblank(&dc->base);
2540ad85b084SDmitry Osipenko 		dc->stats.vblank_total++;
2541791ddb1eSThierry Reding 		dc->stats.vblank++;
2542dee8268fSThierry Reding 	}
2543dee8268fSThierry Reding 
2544dee8268fSThierry Reding 	if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
2545dee8268fSThierry Reding 		/*
2546dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): underflow\n", __func__);
2547dee8268fSThierry Reding 		*/
2548ad85b084SDmitry Osipenko 		dc->stats.underflow_total++;
2549791ddb1eSThierry Reding 		dc->stats.underflow++;
2550791ddb1eSThierry Reding 	}
2551791ddb1eSThierry Reding 
2552791ddb1eSThierry Reding 	if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) {
2553791ddb1eSThierry Reding 		/*
2554791ddb1eSThierry Reding 		dev_dbg(dc->dev, "%s(): overflow\n", __func__);
2555791ddb1eSThierry Reding 		*/
2556ad85b084SDmitry Osipenko 		dc->stats.overflow_total++;
2557791ddb1eSThierry Reding 		dc->stats.overflow++;
2558dee8268fSThierry Reding 	}
2559dee8268fSThierry Reding 
256047307954SThierry Reding 	if (status & HEAD_UF_INT) {
256147307954SThierry Reding 		dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
2562ad85b084SDmitry Osipenko 		dc->stats.underflow_total++;
256347307954SThierry Reding 		dc->stats.underflow++;
256447307954SThierry Reding 	}
256547307954SThierry Reding 
2566dee8268fSThierry Reding 	return IRQ_HANDLED;
2567dee8268fSThierry Reding }
2568dee8268fSThierry Reding 
2569e75d0477SThierry Reding static bool tegra_dc_has_window_groups(struct tegra_dc *dc)
2570e75d0477SThierry Reding {
2571e75d0477SThierry Reding 	unsigned int i;
2572e75d0477SThierry Reding 
2573e75d0477SThierry Reding 	if (!dc->soc->wgrps)
2574e75d0477SThierry Reding 		return true;
2575e75d0477SThierry Reding 
2576e75d0477SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
2577e75d0477SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
2578e75d0477SThierry Reding 
2579e75d0477SThierry Reding 		if (wgrp->dc == dc->pipe && wgrp->num_windows > 0)
2580e75d0477SThierry Reding 			return true;
2581e75d0477SThierry Reding 	}
2582e75d0477SThierry Reding 
2583e75d0477SThierry Reding 	return false;
2584e75d0477SThierry Reding }
2585e75d0477SThierry Reding 
258605d1adfeSThierry Reding static int tegra_dc_early_init(struct host1x_client *client)
258705d1adfeSThierry Reding {
258805d1adfeSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
258905d1adfeSThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
259005d1adfeSThierry Reding 
259105d1adfeSThierry Reding 	tegra->num_crtcs++;
259205d1adfeSThierry Reding 
259305d1adfeSThierry Reding 	return 0;
259405d1adfeSThierry Reding }
259505d1adfeSThierry Reding 
2596dee8268fSThierry Reding static int tegra_dc_init(struct host1x_client *client)
2597dee8268fSThierry Reding {
2598608f43adSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
25992bcdcbfaSThierry Reding 	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
2600dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2601d1f3e1e0SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
2602c7679306SThierry Reding 	struct drm_plane *primary = NULL;
2603c7679306SThierry Reding 	struct drm_plane *cursor = NULL;
2604dee8268fSThierry Reding 	int err;
2605dee8268fSThierry Reding 
2606759d706fSThierry Reding 	/*
2607f5ba33fbSMikko Perttunen 	 * DC has been reset by now, so VBLANK syncpoint can be released
2608f5ba33fbSMikko Perttunen 	 * for general use.
2609f5ba33fbSMikko Perttunen 	 */
2610f5ba33fbSMikko Perttunen 	host1x_syncpt_release_vblank_reservation(client, 26 + dc->pipe);
2611f5ba33fbSMikko Perttunen 
2612f5ba33fbSMikko Perttunen 	/*
2613759d706fSThierry Reding 	 * XXX do not register DCs with no window groups because we cannot
2614759d706fSThierry Reding 	 * assign a primary plane to them, which in turn will cause KMS to
2615759d706fSThierry Reding 	 * crash.
2616759d706fSThierry Reding 	 */
2617e75d0477SThierry Reding 	if (!tegra_dc_has_window_groups(dc))
2618759d706fSThierry Reding 		return 0;
2619759d706fSThierry Reding 
2620fd67e9c6SThierry Reding 	/*
2621fd67e9c6SThierry Reding 	 * Set the display hub as the host1x client parent for the display
2622fd67e9c6SThierry Reding 	 * controller. This is needed for the runtime reference counting that
2623fd67e9c6SThierry Reding 	 * ensures the display hub is always powered when any of the display
2624fd67e9c6SThierry Reding 	 * controllers are.
2625fd67e9c6SThierry Reding 	 */
2626fd67e9c6SThierry Reding 	if (dc->soc->has_nvdisplay)
2627fd67e9c6SThierry Reding 		client->parent = &tegra->hub->client;
2628fd67e9c6SThierry Reding 
2629617dd7ccSThierry Reding 	dc->syncpt = host1x_syncpt_request(client, flags);
26302bcdcbfaSThierry Reding 	if (!dc->syncpt)
26312bcdcbfaSThierry Reding 		dev_warn(dc->dev, "failed to allocate syncpoint\n");
26322bcdcbfaSThierry Reding 
26337edd7961SThierry Reding 	err = host1x_client_iommu_attach(client);
2634a8817489SThierry Reding 	if (err < 0 && err != -ENODEV) {
26350c407de5SThierry Reding 		dev_err(client->dev, "failed to attach to domain: %d\n", err);
2636df06b759SThierry Reding 		return err;
2637df06b759SThierry Reding 	}
2638df06b759SThierry Reding 
263947307954SThierry Reding 	if (dc->soc->wgrps)
264047307954SThierry Reding 		primary = tegra_dc_add_shared_planes(drm, dc);
264147307954SThierry Reding 	else
264247307954SThierry Reding 		primary = tegra_dc_add_planes(drm, dc);
264347307954SThierry Reding 
2644c7679306SThierry Reding 	if (IS_ERR(primary)) {
2645c7679306SThierry Reding 		err = PTR_ERR(primary);
2646c7679306SThierry Reding 		goto cleanup;
2647c7679306SThierry Reding 	}
2648c7679306SThierry Reding 
2649c7679306SThierry Reding 	if (dc->soc->supports_cursor) {
2650c7679306SThierry Reding 		cursor = tegra_dc_cursor_plane_create(drm, dc);
2651c7679306SThierry Reding 		if (IS_ERR(cursor)) {
2652c7679306SThierry Reding 			err = PTR_ERR(cursor);
2653c7679306SThierry Reding 			goto cleanup;
2654c7679306SThierry Reding 		}
26559f446d83SDmitry Osipenko 	} else {
26569f446d83SDmitry Osipenko 		/* dedicate one overlay to mouse cursor */
26579f446d83SDmitry Osipenko 		cursor = tegra_dc_overlay_plane_create(drm, dc, 2, true);
26589f446d83SDmitry Osipenko 		if (IS_ERR(cursor)) {
26599f446d83SDmitry Osipenko 			err = PTR_ERR(cursor);
26609f446d83SDmitry Osipenko 			goto cleanup;
26619f446d83SDmitry Osipenko 		}
2662c7679306SThierry Reding 	}
2663c7679306SThierry Reding 
2664c7679306SThierry Reding 	err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
2665f9882876SVille Syrjälä 					&tegra_crtc_funcs, NULL);
2666c7679306SThierry Reding 	if (err < 0)
2667c7679306SThierry Reding 		goto cleanup;
2668c7679306SThierry Reding 
2669dee8268fSThierry Reding 	drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
2670dee8268fSThierry Reding 
2671d1f3e1e0SThierry Reding 	/*
2672d1f3e1e0SThierry Reding 	 * Keep track of the minimum pitch alignment across all display
2673d1f3e1e0SThierry Reding 	 * controllers.
2674d1f3e1e0SThierry Reding 	 */
2675d1f3e1e0SThierry Reding 	if (dc->soc->pitch_align > tegra->pitch_align)
2676d1f3e1e0SThierry Reding 		tegra->pitch_align = dc->soc->pitch_align;
2677d1f3e1e0SThierry Reding 
2678042c0bd7SThierry Reding 	/* track maximum resolution */
2679042c0bd7SThierry Reding 	if (dc->soc->has_nvdisplay)
2680042c0bd7SThierry Reding 		drm->mode_config.max_width = drm->mode_config.max_height = 16384;
2681042c0bd7SThierry Reding 	else
2682042c0bd7SThierry Reding 		drm->mode_config.max_width = drm->mode_config.max_height = 4096;
2683042c0bd7SThierry Reding 
26849910f5c4SThierry Reding 	err = tegra_dc_rgb_init(drm, dc);
2685dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
2686dee8268fSThierry Reding 		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
2687c7679306SThierry Reding 		goto cleanup;
2688dee8268fSThierry Reding 	}
2689dee8268fSThierry Reding 
2690dee8268fSThierry Reding 	err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
2691dee8268fSThierry Reding 			       dev_name(dc->dev), dc);
2692dee8268fSThierry Reding 	if (err < 0) {
2693dee8268fSThierry Reding 		dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
2694dee8268fSThierry Reding 			err);
2695c7679306SThierry Reding 		goto cleanup;
2696dee8268fSThierry Reding 	}
2697dee8268fSThierry Reding 
269847b15779SThierry Reding 	/*
269947b15779SThierry Reding 	 * Inherit the DMA parameters (such as maximum segment size) from the
2700608f43adSThierry Reding 	 * parent host1x device.
270147b15779SThierry Reding 	 */
2702608f43adSThierry Reding 	client->dev->dma_parms = client->host->dma_parms;
270347b15779SThierry Reding 
2704dee8268fSThierry Reding 	return 0;
2705c7679306SThierry Reding 
2706c7679306SThierry Reding cleanup:
270747307954SThierry Reding 	if (!IS_ERR_OR_NULL(cursor))
2708c7679306SThierry Reding 		drm_plane_cleanup(cursor);
2709c7679306SThierry Reding 
271047307954SThierry Reding 	if (!IS_ERR(primary))
2711c7679306SThierry Reding 		drm_plane_cleanup(primary);
2712c7679306SThierry Reding 
2713aacdf198SThierry Reding 	host1x_client_iommu_detach(client);
27142aed4f5aSMikko Perttunen 	host1x_syncpt_put(dc->syncpt);
2715fd5ec0dcSThierry Reding 
2716c7679306SThierry Reding 	return err;
2717dee8268fSThierry Reding }
2718dee8268fSThierry Reding 
2719dee8268fSThierry Reding static int tegra_dc_exit(struct host1x_client *client)
2720dee8268fSThierry Reding {
2721dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2722dee8268fSThierry Reding 	int err;
2723dee8268fSThierry Reding 
2724e75d0477SThierry Reding 	if (!tegra_dc_has_window_groups(dc))
2725e75d0477SThierry Reding 		return 0;
2726e75d0477SThierry Reding 
272747b15779SThierry Reding 	/* avoid a dangling pointer just in case this disappears */
272847b15779SThierry Reding 	client->dev->dma_parms = NULL;
272947b15779SThierry Reding 
2730dee8268fSThierry Reding 	devm_free_irq(dc->dev, dc->irq, dc);
2731dee8268fSThierry Reding 
2732dee8268fSThierry Reding 	err = tegra_dc_rgb_exit(dc);
2733dee8268fSThierry Reding 	if (err) {
2734dee8268fSThierry Reding 		dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err);
2735dee8268fSThierry Reding 		return err;
2736dee8268fSThierry Reding 	}
2737dee8268fSThierry Reding 
2738aacdf198SThierry Reding 	host1x_client_iommu_detach(client);
27392aed4f5aSMikko Perttunen 	host1x_syncpt_put(dc->syncpt);
27402bcdcbfaSThierry Reding 
2741dee8268fSThierry Reding 	return 0;
2742dee8268fSThierry Reding }
2743dee8268fSThierry Reding 
274405d1adfeSThierry Reding static int tegra_dc_late_exit(struct host1x_client *client)
274505d1adfeSThierry Reding {
274605d1adfeSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
274705d1adfeSThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
274805d1adfeSThierry Reding 
274905d1adfeSThierry Reding 	tegra->num_crtcs--;
2750dee8268fSThierry Reding 
2751dee8268fSThierry Reding 	return 0;
2752dee8268fSThierry Reding }
2753dee8268fSThierry Reding 
2754fd67e9c6SThierry Reding static int tegra_dc_runtime_suspend(struct host1x_client *client)
2755fd67e9c6SThierry Reding {
2756fd67e9c6SThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2757fd67e9c6SThierry Reding 	struct device *dev = client->dev;
2758fd67e9c6SThierry Reding 	int err;
2759fd67e9c6SThierry Reding 
2760fd67e9c6SThierry Reding 	err = reset_control_assert(dc->rst);
2761fd67e9c6SThierry Reding 	if (err < 0) {
2762fd67e9c6SThierry Reding 		dev_err(dev, "failed to assert reset: %d\n", err);
2763fd67e9c6SThierry Reding 		return err;
2764fd67e9c6SThierry Reding 	}
2765fd67e9c6SThierry Reding 
2766fd67e9c6SThierry Reding 	if (dc->soc->has_powergate)
2767fd67e9c6SThierry Reding 		tegra_powergate_power_off(dc->powergate);
2768fd67e9c6SThierry Reding 
2769fd67e9c6SThierry Reding 	clk_disable_unprepare(dc->clk);
2770fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
2771fd67e9c6SThierry Reding 
2772fd67e9c6SThierry Reding 	return 0;
2773fd67e9c6SThierry Reding }
2774fd67e9c6SThierry Reding 
2775fd67e9c6SThierry Reding static int tegra_dc_runtime_resume(struct host1x_client *client)
2776fd67e9c6SThierry Reding {
2777fd67e9c6SThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
2778fd67e9c6SThierry Reding 	struct device *dev = client->dev;
2779fd67e9c6SThierry Reding 	int err;
2780fd67e9c6SThierry Reding 
2781dcdfe271SQinglang Miao 	err = pm_runtime_resume_and_get(dev);
2782fd67e9c6SThierry Reding 	if (err < 0) {
2783fd67e9c6SThierry Reding 		dev_err(dev, "failed to get runtime PM: %d\n", err);
2784fd67e9c6SThierry Reding 		return err;
2785fd67e9c6SThierry Reding 	}
2786fd67e9c6SThierry Reding 
2787fd67e9c6SThierry Reding 	if (dc->soc->has_powergate) {
2788fd67e9c6SThierry Reding 		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
2789fd67e9c6SThierry Reding 							dc->rst);
2790fd67e9c6SThierry Reding 		if (err < 0) {
2791fd67e9c6SThierry Reding 			dev_err(dev, "failed to power partition: %d\n", err);
2792fd67e9c6SThierry Reding 			goto put_rpm;
2793fd67e9c6SThierry Reding 		}
2794fd67e9c6SThierry Reding 	} else {
2795fd67e9c6SThierry Reding 		err = clk_prepare_enable(dc->clk);
2796fd67e9c6SThierry Reding 		if (err < 0) {
2797fd67e9c6SThierry Reding 			dev_err(dev, "failed to enable clock: %d\n", err);
2798fd67e9c6SThierry Reding 			goto put_rpm;
2799fd67e9c6SThierry Reding 		}
2800fd67e9c6SThierry Reding 
2801fd67e9c6SThierry Reding 		err = reset_control_deassert(dc->rst);
2802fd67e9c6SThierry Reding 		if (err < 0) {
2803fd67e9c6SThierry Reding 			dev_err(dev, "failed to deassert reset: %d\n", err);
2804fd67e9c6SThierry Reding 			goto disable_clk;
2805fd67e9c6SThierry Reding 		}
2806fd67e9c6SThierry Reding 	}
2807fd67e9c6SThierry Reding 
2808fd67e9c6SThierry Reding 	return 0;
2809fd67e9c6SThierry Reding 
2810fd67e9c6SThierry Reding disable_clk:
2811fd67e9c6SThierry Reding 	clk_disable_unprepare(dc->clk);
2812fd67e9c6SThierry Reding put_rpm:
2813fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
2814fd67e9c6SThierry Reding 	return err;
2815fd67e9c6SThierry Reding }
2816fd67e9c6SThierry Reding 
2817dee8268fSThierry Reding static const struct host1x_client_ops dc_client_ops = {
281805d1adfeSThierry Reding 	.early_init = tegra_dc_early_init,
2819dee8268fSThierry Reding 	.init = tegra_dc_init,
2820dee8268fSThierry Reding 	.exit = tegra_dc_exit,
282105d1adfeSThierry Reding 	.late_exit = tegra_dc_late_exit,
2822fd67e9c6SThierry Reding 	.suspend = tegra_dc_runtime_suspend,
2823fd67e9c6SThierry Reding 	.resume = tegra_dc_runtime_resume,
2824dee8268fSThierry Reding };
2825dee8268fSThierry Reding 
28268620fc62SThierry Reding static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
28277116e9a8SThierry Reding 	.supports_background_color = false,
28288620fc62SThierry Reding 	.supports_interlacing = false,
2829e687651bSThierry Reding 	.supports_cursor = false,
2830c134f019SThierry Reding 	.supports_block_linear = false,
28317b6f8467SThierry Reding 	.supports_sector_layout = false,
2832a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
2833d1f3e1e0SThierry Reding 	.pitch_align = 8,
28349c012700SThierry Reding 	.has_powergate = false,
2835f68ba691SDmitry Osipenko 	.coupled_pm = true,
283647307954SThierry Reding 	.has_nvdisplay = false,
2837511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
2838511c7023SThierry Reding 	.primary_formats = tegra20_primary_formats,
2839511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
2840511c7023SThierry Reding 	.overlay_formats = tegra20_overlay_formats,
2841e90124cbSThierry Reding 	.modifiers = tegra20_modifiers,
2842acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = true,
284304d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = true,
2844acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = true,
284504d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
28460c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
28478620fc62SThierry Reding };
28488620fc62SThierry Reding 
28498620fc62SThierry Reding static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
28507116e9a8SThierry Reding 	.supports_background_color = false,
28518620fc62SThierry Reding 	.supports_interlacing = false,
2852e687651bSThierry Reding 	.supports_cursor = false,
2853c134f019SThierry Reding 	.supports_block_linear = false,
28547b6f8467SThierry Reding 	.supports_sector_layout = false,
2855a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
2856d1f3e1e0SThierry Reding 	.pitch_align = 8,
28579c012700SThierry Reding 	.has_powergate = false,
2858f68ba691SDmitry Osipenko 	.coupled_pm = false,
285947307954SThierry Reding 	.has_nvdisplay = false,
2860511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
2861511c7023SThierry Reding 	.primary_formats = tegra20_primary_formats,
2862511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
2863511c7023SThierry Reding 	.overlay_formats = tegra20_overlay_formats,
2864e90124cbSThierry Reding 	.modifiers = tegra20_modifiers,
2865acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
286604d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = true,
2867acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
286804d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = true,
28690c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
2870d1f3e1e0SThierry Reding };
2871d1f3e1e0SThierry Reding 
2872d1f3e1e0SThierry Reding static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
28737116e9a8SThierry Reding 	.supports_background_color = false,
2874d1f3e1e0SThierry Reding 	.supports_interlacing = false,
2875d1f3e1e0SThierry Reding 	.supports_cursor = false,
2876d1f3e1e0SThierry Reding 	.supports_block_linear = false,
28777b6f8467SThierry Reding 	.supports_sector_layout = false,
2878a43d0a00SDmitry Osipenko 	.has_legacy_blending = true,
2879d1f3e1e0SThierry Reding 	.pitch_align = 64,
28809c012700SThierry Reding 	.has_powergate = true,
2881f68ba691SDmitry Osipenko 	.coupled_pm = false,
288247307954SThierry Reding 	.has_nvdisplay = false,
2883511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
2884511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
2885511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
2886511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
2887e90124cbSThierry Reding 	.modifiers = tegra20_modifiers,
2888acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
288904d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = false,
2890acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
289104d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = true,
28920c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
28938620fc62SThierry Reding };
28948620fc62SThierry Reding 
28958620fc62SThierry Reding static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
28967116e9a8SThierry Reding 	.supports_background_color = true,
28978620fc62SThierry Reding 	.supports_interlacing = true,
2898e687651bSThierry Reding 	.supports_cursor = true,
2899c134f019SThierry Reding 	.supports_block_linear = true,
29007b6f8467SThierry Reding 	.supports_sector_layout = false,
2901a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
2902d1f3e1e0SThierry Reding 	.pitch_align = 64,
29039c012700SThierry Reding 	.has_powergate = true,
2904f68ba691SDmitry Osipenko 	.coupled_pm = false,
290547307954SThierry Reding 	.has_nvdisplay = false,
2906511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra124_primary_formats),
29079a02d3afSStefan Agner 	.primary_formats = tegra124_primary_formats,
2908511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
29099a02d3afSStefan Agner 	.overlay_formats = tegra124_overlay_formats,
2910e90124cbSThierry Reding 	.modifiers = tegra124_modifiers,
2911acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
291204d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = false,
2913acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
291404d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
29150c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
29168620fc62SThierry Reding };
29178620fc62SThierry Reding 
29185b4f516fSThierry Reding static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
29197116e9a8SThierry Reding 	.supports_background_color = true,
29205b4f516fSThierry Reding 	.supports_interlacing = true,
29215b4f516fSThierry Reding 	.supports_cursor = true,
29225b4f516fSThierry Reding 	.supports_block_linear = true,
29237b6f8467SThierry Reding 	.supports_sector_layout = false,
2924a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
29255b4f516fSThierry Reding 	.pitch_align = 64,
29265b4f516fSThierry Reding 	.has_powergate = true,
2927f68ba691SDmitry Osipenko 	.coupled_pm = false,
292847307954SThierry Reding 	.has_nvdisplay = false,
2929511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
2930511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
2931511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
2932511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
2933e90124cbSThierry Reding 	.modifiers = tegra124_modifiers,
2934acc6a3a9SDmitry Osipenko 	.has_win_a_without_filters = false,
293504d5d5dfSDmitry Osipenko 	.has_win_b_vfilter_mem_client = false,
2936acc6a3a9SDmitry Osipenko 	.has_win_c_without_vert_filter = false,
293704d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
29380c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = true,
293947307954SThierry Reding };
294047307954SThierry Reding 
294147307954SThierry Reding static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
294247307954SThierry Reding 	{
294347307954SThierry Reding 		.index = 0,
294447307954SThierry Reding 		.dc = 0,
294547307954SThierry Reding 		.windows = (const unsigned int[]) { 0 },
294647307954SThierry Reding 		.num_windows = 1,
294747307954SThierry Reding 	}, {
294847307954SThierry Reding 		.index = 1,
294947307954SThierry Reding 		.dc = 1,
295047307954SThierry Reding 		.windows = (const unsigned int[]) { 1 },
295147307954SThierry Reding 		.num_windows = 1,
295247307954SThierry Reding 	}, {
295347307954SThierry Reding 		.index = 2,
295447307954SThierry Reding 		.dc = 1,
295547307954SThierry Reding 		.windows = (const unsigned int[]) { 2 },
295647307954SThierry Reding 		.num_windows = 1,
295747307954SThierry Reding 	}, {
295847307954SThierry Reding 		.index = 3,
295947307954SThierry Reding 		.dc = 2,
296047307954SThierry Reding 		.windows = (const unsigned int[]) { 3 },
296147307954SThierry Reding 		.num_windows = 1,
296247307954SThierry Reding 	}, {
296347307954SThierry Reding 		.index = 4,
296447307954SThierry Reding 		.dc = 2,
296547307954SThierry Reding 		.windows = (const unsigned int[]) { 4 },
296647307954SThierry Reding 		.num_windows = 1,
296747307954SThierry Reding 	}, {
296847307954SThierry Reding 		.index = 5,
296947307954SThierry Reding 		.dc = 2,
297047307954SThierry Reding 		.windows = (const unsigned int[]) { 5 },
297147307954SThierry Reding 		.num_windows = 1,
297247307954SThierry Reding 	},
297347307954SThierry Reding };
297447307954SThierry Reding 
297547307954SThierry Reding static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
297647307954SThierry Reding 	.supports_background_color = true,
297747307954SThierry Reding 	.supports_interlacing = true,
297847307954SThierry Reding 	.supports_cursor = true,
297947307954SThierry Reding 	.supports_block_linear = true,
29807b6f8467SThierry Reding 	.supports_sector_layout = false,
2981a43d0a00SDmitry Osipenko 	.has_legacy_blending = false,
298247307954SThierry Reding 	.pitch_align = 64,
298347307954SThierry Reding 	.has_powergate = false,
2984f68ba691SDmitry Osipenko 	.coupled_pm = false,
298547307954SThierry Reding 	.has_nvdisplay = true,
298647307954SThierry Reding 	.wgrps = tegra186_dc_wgrps,
298747307954SThierry Reding 	.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
298804d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
29890c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
29905b4f516fSThierry Reding };
29915b4f516fSThierry Reding 
299247443196SThierry Reding static const struct tegra_windowgroup_soc tegra194_dc_wgrps[] = {
299347443196SThierry Reding 	{
299447443196SThierry Reding 		.index = 0,
299547443196SThierry Reding 		.dc = 0,
299647443196SThierry Reding 		.windows = (const unsigned int[]) { 0 },
299747443196SThierry Reding 		.num_windows = 1,
299847443196SThierry Reding 	}, {
299947443196SThierry Reding 		.index = 1,
300047443196SThierry Reding 		.dc = 1,
300147443196SThierry Reding 		.windows = (const unsigned int[]) { 1 },
300247443196SThierry Reding 		.num_windows = 1,
300347443196SThierry Reding 	}, {
300447443196SThierry Reding 		.index = 2,
300547443196SThierry Reding 		.dc = 1,
300647443196SThierry Reding 		.windows = (const unsigned int[]) { 2 },
300747443196SThierry Reding 		.num_windows = 1,
300847443196SThierry Reding 	}, {
300947443196SThierry Reding 		.index = 3,
301047443196SThierry Reding 		.dc = 2,
301147443196SThierry Reding 		.windows = (const unsigned int[]) { 3 },
301247443196SThierry Reding 		.num_windows = 1,
301347443196SThierry Reding 	}, {
301447443196SThierry Reding 		.index = 4,
301547443196SThierry Reding 		.dc = 2,
301647443196SThierry Reding 		.windows = (const unsigned int[]) { 4 },
301747443196SThierry Reding 		.num_windows = 1,
301847443196SThierry Reding 	}, {
301947443196SThierry Reding 		.index = 5,
302047443196SThierry Reding 		.dc = 2,
302147443196SThierry Reding 		.windows = (const unsigned int[]) { 5 },
302247443196SThierry Reding 		.num_windows = 1,
302347443196SThierry Reding 	},
302447443196SThierry Reding };
302547443196SThierry Reding 
302647443196SThierry Reding static const struct tegra_dc_soc_info tegra194_dc_soc_info = {
302747443196SThierry Reding 	.supports_background_color = true,
302847443196SThierry Reding 	.supports_interlacing = true,
302947443196SThierry Reding 	.supports_cursor = true,
303047443196SThierry Reding 	.supports_block_linear = true,
30317b6f8467SThierry Reding 	.supports_sector_layout = true,
303247443196SThierry Reding 	.has_legacy_blending = false,
303347443196SThierry Reding 	.pitch_align = 64,
303447443196SThierry Reding 	.has_powergate = false,
303547443196SThierry Reding 	.coupled_pm = false,
303647443196SThierry Reding 	.has_nvdisplay = true,
303747443196SThierry Reding 	.wgrps = tegra194_dc_wgrps,
303847443196SThierry Reding 	.num_wgrps = ARRAY_SIZE(tegra194_dc_wgrps),
303904d5d5dfSDmitry Osipenko 	.plane_tiled_memory_bandwidth_x2 = false,
30400c921b6dSDmitry Osipenko 	.has_pll_d2_out0 = false,
304147443196SThierry Reding };
304247443196SThierry Reding 
30438620fc62SThierry Reding static const struct of_device_id tegra_dc_of_match[] = {
30448620fc62SThierry Reding 	{
304547443196SThierry Reding 		.compatible = "nvidia,tegra194-dc",
304647443196SThierry Reding 		.data = &tegra194_dc_soc_info,
304747443196SThierry Reding 	}, {
304847307954SThierry Reding 		.compatible = "nvidia,tegra186-dc",
304947307954SThierry Reding 		.data = &tegra186_dc_soc_info,
305047307954SThierry Reding 	}, {
30515b4f516fSThierry Reding 		.compatible = "nvidia,tegra210-dc",
30525b4f516fSThierry Reding 		.data = &tegra210_dc_soc_info,
30535b4f516fSThierry Reding 	}, {
30548620fc62SThierry Reding 		.compatible = "nvidia,tegra124-dc",
30558620fc62SThierry Reding 		.data = &tegra124_dc_soc_info,
30568620fc62SThierry Reding 	}, {
30579c012700SThierry Reding 		.compatible = "nvidia,tegra114-dc",
30589c012700SThierry Reding 		.data = &tegra114_dc_soc_info,
30599c012700SThierry Reding 	}, {
30608620fc62SThierry Reding 		.compatible = "nvidia,tegra30-dc",
30618620fc62SThierry Reding 		.data = &tegra30_dc_soc_info,
30628620fc62SThierry Reding 	}, {
30638620fc62SThierry Reding 		.compatible = "nvidia,tegra20-dc",
30648620fc62SThierry Reding 		.data = &tegra20_dc_soc_info,
30658620fc62SThierry Reding 	}, {
30668620fc62SThierry Reding 		/* sentinel */
30678620fc62SThierry Reding 	}
30688620fc62SThierry Reding };
3069ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_dc_of_match);
30708620fc62SThierry Reding 
307113411dddSThierry Reding static int tegra_dc_parse_dt(struct tegra_dc *dc)
307213411dddSThierry Reding {
307313411dddSThierry Reding 	struct device_node *np;
307413411dddSThierry Reding 	u32 value = 0;
307513411dddSThierry Reding 	int err;
307613411dddSThierry Reding 
307713411dddSThierry Reding 	err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value);
307813411dddSThierry Reding 	if (err < 0) {
307913411dddSThierry Reding 		dev_err(dc->dev, "missing \"nvidia,head\" property\n");
308013411dddSThierry Reding 
308113411dddSThierry Reding 		/*
308213411dddSThierry Reding 		 * If the nvidia,head property isn't present, try to find the
308313411dddSThierry Reding 		 * correct head number by looking up the position of this
308413411dddSThierry Reding 		 * display controller's node within the device tree. Assuming
308513411dddSThierry Reding 		 * that the nodes are ordered properly in the DTS file and
308613411dddSThierry Reding 		 * that the translation into a flattened device tree blob
308713411dddSThierry Reding 		 * preserves that ordering this will actually yield the right
308813411dddSThierry Reding 		 * head number.
308913411dddSThierry Reding 		 *
309013411dddSThierry Reding 		 * If those assumptions don't hold, this will still work for
309113411dddSThierry Reding 		 * cases where only a single display controller is used.
309213411dddSThierry Reding 		 */
309313411dddSThierry Reding 		for_each_matching_node(np, tegra_dc_of_match) {
3094cf6b1744SJulia Lawall 			if (np == dc->dev->of_node) {
3095cf6b1744SJulia Lawall 				of_node_put(np);
309613411dddSThierry Reding 				break;
3097cf6b1744SJulia Lawall 			}
309813411dddSThierry Reding 
309913411dddSThierry Reding 			value++;
310013411dddSThierry Reding 		}
310113411dddSThierry Reding 	}
310213411dddSThierry Reding 
310313411dddSThierry Reding 	dc->pipe = value;
310413411dddSThierry Reding 
310513411dddSThierry Reding 	return 0;
310613411dddSThierry Reding }
310713411dddSThierry Reding 
310892ce7e83SSuzuki K Poulose static int tegra_dc_match_by_pipe(struct device *dev, const void *data)
3109f68ba691SDmitry Osipenko {
3110f68ba691SDmitry Osipenko 	struct tegra_dc *dc = dev_get_drvdata(dev);
311192ce7e83SSuzuki K Poulose 	unsigned int pipe = (unsigned long)(void *)data;
3112f68ba691SDmitry Osipenko 
3113f68ba691SDmitry Osipenko 	return dc->pipe == pipe;
3114f68ba691SDmitry Osipenko }
3115f68ba691SDmitry Osipenko 
3116f68ba691SDmitry Osipenko static int tegra_dc_couple(struct tegra_dc *dc)
3117f68ba691SDmitry Osipenko {
3118f68ba691SDmitry Osipenko 	/*
3119f68ba691SDmitry Osipenko 	 * On Tegra20, DC1 requires DC0 to be taken out of reset in order to
3120f68ba691SDmitry Osipenko 	 * be enabled, otherwise CPU hangs on writing to CMD_DISPLAY_COMMAND /
3121f68ba691SDmitry Osipenko 	 * POWER_CONTROL registers during CRTC enabling.
3122f68ba691SDmitry Osipenko 	 */
3123f68ba691SDmitry Osipenko 	if (dc->soc->coupled_pm && dc->pipe == 1) {
3124a31500feSThierry Reding 		struct device *companion;
3125a31500feSThierry Reding 		struct tegra_dc *parent;
3126f68ba691SDmitry Osipenko 
3127a31500feSThierry Reding 		companion = driver_find_device(dc->dev->driver, NULL, (const void *)0,
3128f68ba691SDmitry Osipenko 					       tegra_dc_match_by_pipe);
3129a31500feSThierry Reding 		if (!companion)
3130f68ba691SDmitry Osipenko 			return -EPROBE_DEFER;
3131f68ba691SDmitry Osipenko 
3132a31500feSThierry Reding 		parent = dev_get_drvdata(companion);
3133a31500feSThierry Reding 		dc->client.parent = &parent->client;
3134f68ba691SDmitry Osipenko 
3135a31500feSThierry Reding 		dev_dbg(dc->dev, "coupled to %s\n", dev_name(companion));
3136f68ba691SDmitry Osipenko 	}
3137f68ba691SDmitry Osipenko 
3138f68ba691SDmitry Osipenko 	return 0;
3139f68ba691SDmitry Osipenko }
3140f68ba691SDmitry Osipenko 
31414ce3048cSDmitry Osipenko static int tegra_dc_init_opp_table(struct tegra_dc *dc)
31424ce3048cSDmitry Osipenko {
31434ce3048cSDmitry Osipenko 	struct tegra_core_opp_params opp_params = {};
31444ce3048cSDmitry Osipenko 	int err;
31454ce3048cSDmitry Osipenko 
31464ce3048cSDmitry Osipenko 	err = devm_tegra_core_dev_init_opp_table(dc->dev, &opp_params);
31474ce3048cSDmitry Osipenko 	if (err && err != -ENODEV)
31484ce3048cSDmitry Osipenko 		return err;
31494ce3048cSDmitry Osipenko 
31504ce3048cSDmitry Osipenko 	if (err)
31514ce3048cSDmitry Osipenko 		dc->has_opp_table = false;
31524ce3048cSDmitry Osipenko 	else
31534ce3048cSDmitry Osipenko 		dc->has_opp_table = true;
31544ce3048cSDmitry Osipenko 
31554ce3048cSDmitry Osipenko 	return 0;
31564ce3048cSDmitry Osipenko }
31574ce3048cSDmitry Osipenko 
3158dee8268fSThierry Reding static int tegra_dc_probe(struct platform_device *pdev)
3159dee8268fSThierry Reding {
316086044e74SThierry Reding 	u64 dma_mask = dma_get_mask(pdev->dev.parent);
3161dee8268fSThierry Reding 	struct tegra_dc *dc;
3162dee8268fSThierry Reding 	int err;
3163dee8268fSThierry Reding 
316486044e74SThierry Reding 	err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
316586044e74SThierry Reding 	if (err < 0) {
316686044e74SThierry Reding 		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
316786044e74SThierry Reding 		return err;
316886044e74SThierry Reding 	}
316986044e74SThierry Reding 
3170dee8268fSThierry Reding 	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
3171dee8268fSThierry Reding 	if (!dc)
3172dee8268fSThierry Reding 		return -ENOMEM;
3173dee8268fSThierry Reding 
3174b9ff7aeaSThierry Reding 	dc->soc = of_device_get_match_data(&pdev->dev);
31758620fc62SThierry Reding 
3176dee8268fSThierry Reding 	INIT_LIST_HEAD(&dc->list);
3177dee8268fSThierry Reding 	dc->dev = &pdev->dev;
3178dee8268fSThierry Reding 
317913411dddSThierry Reding 	err = tegra_dc_parse_dt(dc);
318013411dddSThierry Reding 	if (err < 0)
318113411dddSThierry Reding 		return err;
318213411dddSThierry Reding 
3183f68ba691SDmitry Osipenko 	err = tegra_dc_couple(dc);
3184f68ba691SDmitry Osipenko 	if (err < 0)
3185f68ba691SDmitry Osipenko 		return err;
3186f68ba691SDmitry Osipenko 
3187dee8268fSThierry Reding 	dc->clk = devm_clk_get(&pdev->dev, NULL);
3188dee8268fSThierry Reding 	if (IS_ERR(dc->clk)) {
3189dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to get clock\n");
3190dee8268fSThierry Reding 		return PTR_ERR(dc->clk);
3191dee8268fSThierry Reding 	}
3192dee8268fSThierry Reding 
3193ca48080aSStephen Warren 	dc->rst = devm_reset_control_get(&pdev->dev, "dc");
3194ca48080aSStephen Warren 	if (IS_ERR(dc->rst)) {
3195ca48080aSStephen Warren 		dev_err(&pdev->dev, "failed to get reset\n");
3196ca48080aSStephen Warren 		return PTR_ERR(dc->rst);
3197ca48080aSStephen Warren 	}
3198ca48080aSStephen Warren 
3199a2f2f740SThierry Reding 	/* assert reset and disable clock */
3200a2f2f740SThierry Reding 	err = clk_prepare_enable(dc->clk);
3201a2f2f740SThierry Reding 	if (err < 0)
3202a2f2f740SThierry Reding 		return err;
3203a2f2f740SThierry Reding 
3204a2f2f740SThierry Reding 	usleep_range(2000, 4000);
3205a2f2f740SThierry Reding 
3206a2f2f740SThierry Reding 	err = reset_control_assert(dc->rst);
3207a2f2f740SThierry Reding 	if (err < 0)
3208a2f2f740SThierry Reding 		return err;
3209a2f2f740SThierry Reding 
3210a2f2f740SThierry Reding 	usleep_range(2000, 4000);
3211a2f2f740SThierry Reding 
3212a2f2f740SThierry Reding 	clk_disable_unprepare(dc->clk);
321333a8eb8dSThierry Reding 
32149c012700SThierry Reding 	if (dc->soc->has_powergate) {
32159c012700SThierry Reding 		if (dc->pipe == 0)
32169c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DIS;
32179c012700SThierry Reding 		else
32189c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DISB;
32199c012700SThierry Reding 
322033a8eb8dSThierry Reding 		tegra_powergate_power_off(dc->powergate);
32219c012700SThierry Reding 	}
3222dee8268fSThierry Reding 
32234ce3048cSDmitry Osipenko 	err = tegra_dc_init_opp_table(dc);
32244ce3048cSDmitry Osipenko 	if (err < 0)
32254ce3048cSDmitry Osipenko 		return err;
32264ce3048cSDmitry Osipenko 
3227a858ac8fSDmitry Osipenko 	dc->regs = devm_platform_ioremap_resource(pdev, 0);
3228dee8268fSThierry Reding 	if (IS_ERR(dc->regs))
3229dee8268fSThierry Reding 		return PTR_ERR(dc->regs);
3230dee8268fSThierry Reding 
3231dee8268fSThierry Reding 	dc->irq = platform_get_irq(pdev, 0);
32325f1df70fSTang Bin 	if (dc->irq < 0)
3233dee8268fSThierry Reding 		return -ENXIO;
3234dee8268fSThierry Reding 
3235dee8268fSThierry Reding 	err = tegra_dc_rgb_probe(dc);
3236f07f04a5SDmitry Osipenko 	if (err < 0 && err != -ENODEV)
3237f07f04a5SDmitry Osipenko 		return dev_err_probe(&pdev->dev, err,
3238f07f04a5SDmitry Osipenko 				     "failed to probe RGB output\n");
3239dee8268fSThierry Reding 
324033a8eb8dSThierry Reding 	platform_set_drvdata(pdev, dc);
324133a8eb8dSThierry Reding 	pm_runtime_enable(&pdev->dev);
324233a8eb8dSThierry Reding 
324333a8eb8dSThierry Reding 	INIT_LIST_HEAD(&dc->client.list);
324433a8eb8dSThierry Reding 	dc->client.ops = &dc_client_ops;
324533a8eb8dSThierry Reding 	dc->client.dev = &pdev->dev;
324633a8eb8dSThierry Reding 
3247dee8268fSThierry Reding 	err = host1x_client_register(&dc->client);
3248dee8268fSThierry Reding 	if (err < 0) {
3249dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
3250dee8268fSThierry Reding 			err);
32510411ea89SDmitry Osipenko 		goto disable_pm;
3252dee8268fSThierry Reding 	}
3253dee8268fSThierry Reding 
3254dee8268fSThierry Reding 	return 0;
32550411ea89SDmitry Osipenko 
32560411ea89SDmitry Osipenko disable_pm:
32570411ea89SDmitry Osipenko 	pm_runtime_disable(&pdev->dev);
32580411ea89SDmitry Osipenko 	tegra_dc_rgb_remove(dc);
32590411ea89SDmitry Osipenko 
32600411ea89SDmitry Osipenko 	return err;
3261dee8268fSThierry Reding }
3262dee8268fSThierry Reding 
3263dee8268fSThierry Reding static int tegra_dc_remove(struct platform_device *pdev)
3264dee8268fSThierry Reding {
3265dee8268fSThierry Reding 	struct tegra_dc *dc = platform_get_drvdata(pdev);
3266dee8268fSThierry Reding 	int err;
3267dee8268fSThierry Reding 
3268dee8268fSThierry Reding 	err = host1x_client_unregister(&dc->client);
3269dee8268fSThierry Reding 	if (err < 0) {
3270dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
3271dee8268fSThierry Reding 			err);
3272dee8268fSThierry Reding 		return err;
3273dee8268fSThierry Reding 	}
3274dee8268fSThierry Reding 
327559d29c0eSThierry Reding 	err = tegra_dc_rgb_remove(dc);
327659d29c0eSThierry Reding 	if (err < 0) {
327759d29c0eSThierry Reding 		dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
327859d29c0eSThierry Reding 		return err;
327959d29c0eSThierry Reding 	}
328059d29c0eSThierry Reding 
328133a8eb8dSThierry Reding 	pm_runtime_disable(&pdev->dev);
328233a8eb8dSThierry Reding 
328333a8eb8dSThierry Reding 	return 0;
328433a8eb8dSThierry Reding }
328533a8eb8dSThierry Reding 
3286dee8268fSThierry Reding struct platform_driver tegra_dc_driver = {
3287dee8268fSThierry Reding 	.driver = {
3288dee8268fSThierry Reding 		.name = "tegra-dc",
3289dee8268fSThierry Reding 		.of_match_table = tegra_dc_of_match,
3290dee8268fSThierry Reding 	},
3291dee8268fSThierry Reding 	.probe = tegra_dc_probe,
3292dee8268fSThierry Reding 	.remove = tegra_dc_remove,
3293dee8268fSThierry Reding };
3294