xref: /openbmc/linux/drivers/gpu/drm/tegra/dc.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2dee8268fSThierry Reding /*
3dee8268fSThierry Reding  * Copyright (C) 2012 Avionic Design GmbH
4dee8268fSThierry Reding  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
5dee8268fSThierry Reding  */
6dee8268fSThierry Reding 
7dee8268fSThierry Reding #include <linux/clk.h>
8dee8268fSThierry Reding #include <linux/debugfs.h>
9eb1df694SSam Ravnborg #include <linux/delay.h>
107ac1a36aSRobin Murphy #include <linux/dma-mapping.h>
11df06b759SThierry Reding #include <linux/iommu.h>
1204d5d5dfSDmitry Osipenko #include <linux/interconnect.h>
13eb1df694SSam Ravnborg #include <linux/module.h>
14*722d4f06SRob Herring #include <linux/of.h>
15*722d4f06SRob Herring #include <linux/platform_device.h>
164ce3048cSDmitry Osipenko #include <linux/pm_domain.h>
174ce3048cSDmitry Osipenko #include <linux/pm_opp.h>
1833a8eb8dSThierry Reding #include <linux/pm_runtime.h>
19ca48080aSStephen Warren #include <linux/reset.h>
20dee8268fSThierry Reding 
214ce3048cSDmitry Osipenko #include <soc/tegra/common.h>
229c012700SThierry Reding #include <soc/tegra/pmc.h>
239c012700SThierry Reding 
24eb1df694SSam Ravnborg #include <drm/drm_atomic.h>
25eb1df694SSam Ravnborg #include <drm/drm_atomic_helper.h>
2690bb087fSVille Syrjälä #include <drm/drm_blend.h>
27eb1df694SSam Ravnborg #include <drm/drm_debugfs.h>
28eb1df694SSam Ravnborg #include <drm/drm_fourcc.h>
29720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
30eb1df694SSam Ravnborg #include <drm/drm_vblank.h>
31eb1df694SSam Ravnborg 
32dee8268fSThierry Reding #include "dc.h"
33dee8268fSThierry Reding #include "drm.h"
34dee8268fSThierry Reding #include "gem.h"
3547307954SThierry Reding #include "hub.h"
365acd3514SThierry Reding #include "plane.h"
37dee8268fSThierry Reding 
38b7e0b04aSMaarten Lankhorst static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
39b7e0b04aSMaarten Lankhorst 					    struct drm_crtc_state *state);
40b7e0b04aSMaarten Lankhorst 
tegra_dc_stats_reset(struct tegra_dc_stats * stats)41791ddb1eSThierry Reding static void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
42791ddb1eSThierry Reding {
43791ddb1eSThierry Reding 	stats->frames = 0;
44791ddb1eSThierry Reding 	stats->vblank = 0;
45791ddb1eSThierry Reding 	stats->underflow = 0;
46791ddb1eSThierry Reding 	stats->overflow = 0;
47791ddb1eSThierry Reding }
48791ddb1eSThierry Reding 
491087fac1SThierry Reding /* Reads the active copy of a register. */
tegra_dc_readl_active(struct tegra_dc * dc,unsigned long offset)5086df256fSThierry Reding static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
5186df256fSThierry Reding {
5286df256fSThierry Reding 	u32 value;
5386df256fSThierry Reding 
5486df256fSThierry Reding 	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
5586df256fSThierry Reding 	value = tegra_dc_readl(dc, offset);
5686df256fSThierry Reding 	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
5786df256fSThierry Reding 
5886df256fSThierry Reding 	return value;
5986df256fSThierry Reding }
6086df256fSThierry Reding 
tegra_plane_offset(struct tegra_plane * plane,unsigned int offset)611087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
621087fac1SThierry Reding 					      unsigned int offset)
631087fac1SThierry Reding {
641087fac1SThierry Reding 	if (offset >= 0x500 && offset <= 0x638) {
651087fac1SThierry Reding 		offset = 0x000 + (offset - 0x500);
661087fac1SThierry Reding 		return plane->offset + offset;
671087fac1SThierry Reding 	}
681087fac1SThierry Reding 
691087fac1SThierry Reding 	if (offset >= 0x700 && offset <= 0x719) {
701087fac1SThierry Reding 		offset = 0x180 + (offset - 0x700);
711087fac1SThierry Reding 		return plane->offset + offset;
721087fac1SThierry Reding 	}
731087fac1SThierry Reding 
741087fac1SThierry Reding 	if (offset >= 0x800 && offset <= 0x839) {
751087fac1SThierry Reding 		offset = 0x1c0 + (offset - 0x800);
761087fac1SThierry Reding 		return plane->offset + offset;
771087fac1SThierry Reding 	}
781087fac1SThierry Reding 
791087fac1SThierry Reding 	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
801087fac1SThierry Reding 
811087fac1SThierry Reding 	return plane->offset + offset;
821087fac1SThierry Reding }
831087fac1SThierry Reding 
tegra_plane_readl(struct tegra_plane * plane,unsigned int offset)841087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane,
851087fac1SThierry Reding 				    unsigned int offset)
861087fac1SThierry Reding {
871087fac1SThierry Reding 	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
881087fac1SThierry Reding }
891087fac1SThierry Reding 
tegra_plane_writel(struct tegra_plane * plane,u32 value,unsigned int offset)901087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
911087fac1SThierry Reding 				      unsigned int offset)
921087fac1SThierry Reding {
931087fac1SThierry Reding 	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
941087fac1SThierry Reding }
951087fac1SThierry Reding 
tegra_dc_has_output(struct tegra_dc * dc,struct device * dev)96c57997bcSThierry Reding bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
97c57997bcSThierry Reding {
98c57997bcSThierry Reding 	struct device_node *np = dc->dev->of_node;
99c57997bcSThierry Reding 	struct of_phandle_iterator it;
100c57997bcSThierry Reding 	int err;
101c57997bcSThierry Reding 
102c57997bcSThierry Reding 	of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
103c57997bcSThierry Reding 		if (it.node == dev->of_node)
104c57997bcSThierry Reding 			return true;
105c57997bcSThierry Reding 
106c57997bcSThierry Reding 	return false;
107c57997bcSThierry Reding }
108c57997bcSThierry Reding 
10986df256fSThierry Reding /*
110d700ba7aSThierry Reding  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
111d700ba7aSThierry Reding  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
112d700ba7aSThierry Reding  * Latching happens mmediately if the display controller is in STOP mode or
113d700ba7aSThierry Reding  * on the next frame boundary otherwise.
114d700ba7aSThierry Reding  *
115d700ba7aSThierry Reding  * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The
116d700ba7aSThierry Reding  * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits
117d700ba7aSThierry Reding  * are written. When the *_ACT_REQ bits are written, the ARM copy is latched
118d700ba7aSThierry Reding  * into the ACTIVE copy, either immediately if the display controller is in
119d700ba7aSThierry Reding  * STOP mode, or at the next frame boundary otherwise.
120d700ba7aSThierry Reding  */
tegra_dc_commit(struct tegra_dc * dc)12162b9e063SThierry Reding void tegra_dc_commit(struct tegra_dc *dc)
122205d48edSThierry Reding {
123205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
124205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
125205d48edSThierry Reding }
126205d48edSThierry Reding 
compute_dda_inc(unsigned int in,unsigned int out,bool v,unsigned int bpp)12710288eeaSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
12810288eeaSThierry Reding 				  unsigned int bpp)
12910288eeaSThierry Reding {
13010288eeaSThierry Reding 	fixed20_12 outf = dfixed_init(out);
13110288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
13210288eeaSThierry Reding 	u32 dda_inc;
13310288eeaSThierry Reding 	int max;
13410288eeaSThierry Reding 
13510288eeaSThierry Reding 	if (v)
13610288eeaSThierry Reding 		max = 15;
13710288eeaSThierry Reding 	else {
13810288eeaSThierry Reding 		switch (bpp) {
13910288eeaSThierry Reding 		case 2:
14010288eeaSThierry Reding 			max = 8;
14110288eeaSThierry Reding 			break;
14210288eeaSThierry Reding 
14310288eeaSThierry Reding 		default:
14410288eeaSThierry Reding 			WARN_ON_ONCE(1);
145df561f66SGustavo A. R. Silva 			fallthrough;
14610288eeaSThierry Reding 		case 4:
14710288eeaSThierry Reding 			max = 4;
14810288eeaSThierry Reding 			break;
14910288eeaSThierry Reding 		}
15010288eeaSThierry Reding 	}
15110288eeaSThierry Reding 
15210288eeaSThierry Reding 	outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
15310288eeaSThierry Reding 	inf.full -= dfixed_const(1);
15410288eeaSThierry Reding 
15510288eeaSThierry Reding 	dda_inc = dfixed_div(inf, outf);
15610288eeaSThierry Reding 	dda_inc = min_t(u32, dda_inc, dfixed_const(max));
15710288eeaSThierry Reding 
15810288eeaSThierry Reding 	return dda_inc;
15910288eeaSThierry Reding }
16010288eeaSThierry Reding 
compute_initial_dda(unsigned int in)16110288eeaSThierry Reding static inline u32 compute_initial_dda(unsigned int in)
16210288eeaSThierry Reding {
16310288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
16410288eeaSThierry Reding 	return dfixed_frac(inf);
16510288eeaSThierry Reding }
16610288eeaSThierry Reding 
tegra_plane_setup_blending_legacy(struct tegra_plane * plane)167ab7d3f58SThierry Reding static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
168ab7d3f58SThierry Reding {
169ebae8d07SThierry Reding 	u32 background[3] = {
170ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
171ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
172ebae8d07SThierry Reding 		BLEND_WEIGHT1(0) | BLEND_WEIGHT0(0) | BLEND_COLOR_KEY_NONE,
173ebae8d07SThierry Reding 	};
174ebae8d07SThierry Reding 	u32 foreground = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255) |
175ebae8d07SThierry Reding 			 BLEND_COLOR_KEY_NONE;
176ebae8d07SThierry Reding 	u32 blendnokey = BLEND_WEIGHT1(255) | BLEND_WEIGHT0(255);
177ebae8d07SThierry Reding 	struct tegra_plane_state *state;
1783dae08bcSDmitry Osipenko 	u32 blending[2];
179ebae8d07SThierry Reding 	unsigned int i;
180ebae8d07SThierry Reding 
1813dae08bcSDmitry Osipenko 	/* disable blending for non-overlapping case */
182ebae8d07SThierry Reding 	tegra_plane_writel(plane, blendnokey, DC_WIN_BLEND_NOKEY);
183ebae8d07SThierry Reding 	tegra_plane_writel(plane, foreground, DC_WIN_BLEND_1WIN);
184ab7d3f58SThierry Reding 
1853dae08bcSDmitry Osipenko 	state = to_tegra_plane_state(plane->base.state);
1863dae08bcSDmitry Osipenko 
1873dae08bcSDmitry Osipenko 	if (state->opaque) {
1883dae08bcSDmitry Osipenko 		/*
1893dae08bcSDmitry Osipenko 		 * Since custom fix-weight blending isn't utilized and weight
1903dae08bcSDmitry Osipenko 		 * of top window is set to max, we can enforce dependent
1913dae08bcSDmitry Osipenko 		 * blending which in this case results in transparent bottom
1923dae08bcSDmitry Osipenko 		 * window if top window is opaque and if top window enables
1933dae08bcSDmitry Osipenko 		 * alpha blending, then bottom window is getting alpha value
1943dae08bcSDmitry Osipenko 		 * of 1 minus the sum of alpha components of the overlapping
1953dae08bcSDmitry Osipenko 		 * plane.
1963dae08bcSDmitry Osipenko 		 */
1973dae08bcSDmitry Osipenko 		background[0] |= BLEND_CONTROL_DEPENDENT;
1983dae08bcSDmitry Osipenko 		background[1] |= BLEND_CONTROL_DEPENDENT;
1993dae08bcSDmitry Osipenko 
2003dae08bcSDmitry Osipenko 		/*
2013dae08bcSDmitry Osipenko 		 * The region where three windows overlap is the intersection
2023dae08bcSDmitry Osipenko 		 * of the two regions where two windows overlap. It contributes
2033dae08bcSDmitry Osipenko 		 * to the area if all of the windows on top of it have an alpha
2043dae08bcSDmitry Osipenko 		 * component.
2053dae08bcSDmitry Osipenko 		 */
2063dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2073dae08bcSDmitry Osipenko 		case 0:
2083dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2093dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2103dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2113dae08bcSDmitry Osipenko 			break;
2123dae08bcSDmitry Osipenko 
2133dae08bcSDmitry Osipenko 		case 1:
2143dae08bcSDmitry Osipenko 			background[2] |= BLEND_CONTROL_DEPENDENT;
2153dae08bcSDmitry Osipenko 			break;
2163dae08bcSDmitry Osipenko 		}
2173dae08bcSDmitry Osipenko 	} else {
2183dae08bcSDmitry Osipenko 		/*
2193dae08bcSDmitry Osipenko 		 * Enable alpha blending if pixel format has an alpha
2203dae08bcSDmitry Osipenko 		 * component.
2213dae08bcSDmitry Osipenko 		 */
2223dae08bcSDmitry Osipenko 		foreground |= BLEND_CONTROL_ALPHA;
2233dae08bcSDmitry Osipenko 
2243dae08bcSDmitry Osipenko 		/*
2253dae08bcSDmitry Osipenko 		 * If any of the windows on top of this window is opaque, it
2263dae08bcSDmitry Osipenko 		 * will completely conceal this window within that area. If
2273dae08bcSDmitry Osipenko 		 * top window has an alpha component, it is blended over the
2283dae08bcSDmitry Osipenko 		 * bottom window.
2293dae08bcSDmitry Osipenko 		 */
2303dae08bcSDmitry Osipenko 		for (i = 0; i < 2; i++) {
2313dae08bcSDmitry Osipenko 			if (state->blending[i].alpha &&
2323dae08bcSDmitry Osipenko 			    state->blending[i].top)
2333dae08bcSDmitry Osipenko 				background[i] |= BLEND_CONTROL_DEPENDENT;
2343dae08bcSDmitry Osipenko 		}
2353dae08bcSDmitry Osipenko 
2363dae08bcSDmitry Osipenko 		switch (state->base.normalized_zpos) {
2373dae08bcSDmitry Osipenko 		case 0:
2383dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2393dae08bcSDmitry Osipenko 			    state->blending[1].alpha)
2403dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_DEPENDENT;
2413dae08bcSDmitry Osipenko 			break;
2423dae08bcSDmitry Osipenko 
2433dae08bcSDmitry Osipenko 		case 1:
2443dae08bcSDmitry Osipenko 			/*
2453dae08bcSDmitry Osipenko 			 * When both middle and topmost windows have an alpha,
2463dae08bcSDmitry Osipenko 			 * these windows a mixed together and then the result
2473dae08bcSDmitry Osipenko 			 * is blended over the bottom window.
2483dae08bcSDmitry Osipenko 			 */
2493dae08bcSDmitry Osipenko 			if (state->blending[0].alpha &&
2503dae08bcSDmitry Osipenko 			    state->blending[0].top)
2513dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2523dae08bcSDmitry Osipenko 
2533dae08bcSDmitry Osipenko 			if (state->blending[1].alpha &&
2543dae08bcSDmitry Osipenko 			    state->blending[1].top)
2553dae08bcSDmitry Osipenko 				background[2] |= BLEND_CONTROL_ALPHA;
2563dae08bcSDmitry Osipenko 			break;
2573dae08bcSDmitry Osipenko 		}
2583dae08bcSDmitry Osipenko 	}
2593dae08bcSDmitry Osipenko 
2603dae08bcSDmitry Osipenko 	switch (state->base.normalized_zpos) {
261ab7d3f58SThierry Reding 	case 0:
262ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[0], DC_WIN_BLEND_2WIN_X);
263ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[1], DC_WIN_BLEND_2WIN_Y);
264ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
265ab7d3f58SThierry Reding 		break;
266ab7d3f58SThierry Reding 
267ab7d3f58SThierry Reding 	case 1:
2683dae08bcSDmitry Osipenko 		/*
2693dae08bcSDmitry Osipenko 		 * If window B / C is topmost, then X / Y registers are
2703dae08bcSDmitry Osipenko 		 * matching the order of blending[...] state indices,
2713dae08bcSDmitry Osipenko 		 * otherwise a swap is required.
2723dae08bcSDmitry Osipenko 		 */
2733dae08bcSDmitry Osipenko 		if (!state->blending[0].top && state->blending[1].top) {
2743dae08bcSDmitry Osipenko 			blending[0] = foreground;
2753dae08bcSDmitry Osipenko 			blending[1] = background[1];
2763dae08bcSDmitry Osipenko 		} else {
2773dae08bcSDmitry Osipenko 			blending[0] = background[0];
2783dae08bcSDmitry Osipenko 			blending[1] = foreground;
2793dae08bcSDmitry Osipenko 		}
2803dae08bcSDmitry Osipenko 
2813dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[0], DC_WIN_BLEND_2WIN_X);
2823dae08bcSDmitry Osipenko 		tegra_plane_writel(plane, blending[1], DC_WIN_BLEND_2WIN_Y);
283ebae8d07SThierry Reding 		tegra_plane_writel(plane, background[2], DC_WIN_BLEND_3WIN_XY);
284ab7d3f58SThierry Reding 		break;
285ab7d3f58SThierry Reding 
286ab7d3f58SThierry Reding 	case 2:
287ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_X);
288ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_2WIN_Y);
289ebae8d07SThierry Reding 		tegra_plane_writel(plane, foreground, DC_WIN_BLEND_3WIN_XY);
290ab7d3f58SThierry Reding 		break;
291ab7d3f58SThierry Reding 	}
292ab7d3f58SThierry Reding }
293ab7d3f58SThierry Reding 
tegra_plane_setup_blending(struct tegra_plane * plane,const struct tegra_dc_window * window)294ab7d3f58SThierry Reding static void tegra_plane_setup_blending(struct tegra_plane *plane,
295ab7d3f58SThierry Reding 				       const struct tegra_dc_window *window)
296ab7d3f58SThierry Reding {
297ab7d3f58SThierry Reding 	u32 value;
298ab7d3f58SThierry Reding 
299ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
300ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
301ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
302ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT);
303ab7d3f58SThierry Reding 
304ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
305ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
306ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
307ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT);
308ab7d3f58SThierry Reding 
309ab7d3f58SThierry Reding 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos);
310ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
311ab7d3f58SThierry Reding }
312ab7d3f58SThierry Reding 
313acc6a3a9SDmitry Osipenko static bool
tegra_plane_use_horizontal_filtering(struct tegra_plane * plane,const struct tegra_dc_window * window)314acc6a3a9SDmitry Osipenko tegra_plane_use_horizontal_filtering(struct tegra_plane *plane,
315acc6a3a9SDmitry Osipenko 				     const struct tegra_dc_window *window)
316acc6a3a9SDmitry Osipenko {
317acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
318acc6a3a9SDmitry Osipenko 
319acc6a3a9SDmitry Osipenko 	if (window->src.w == window->dst.w)
320acc6a3a9SDmitry Osipenko 		return false;
321acc6a3a9SDmitry Osipenko 
322acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
323acc6a3a9SDmitry Osipenko 		return false;
324acc6a3a9SDmitry Osipenko 
325acc6a3a9SDmitry Osipenko 	return true;
326acc6a3a9SDmitry Osipenko }
327acc6a3a9SDmitry Osipenko 
328acc6a3a9SDmitry Osipenko static bool
tegra_plane_use_vertical_filtering(struct tegra_plane * plane,const struct tegra_dc_window * window)329acc6a3a9SDmitry Osipenko tegra_plane_use_vertical_filtering(struct tegra_plane *plane,
330acc6a3a9SDmitry Osipenko 				   const struct tegra_dc_window *window)
331acc6a3a9SDmitry Osipenko {
332acc6a3a9SDmitry Osipenko 	struct tegra_dc *dc = plane->dc;
333acc6a3a9SDmitry Osipenko 
334acc6a3a9SDmitry Osipenko 	if (window->src.h == window->dst.h)
335acc6a3a9SDmitry Osipenko 		return false;
336acc6a3a9SDmitry Osipenko 
337acc6a3a9SDmitry Osipenko 	if (plane->index == 0 && dc->soc->has_win_a_without_filters)
338acc6a3a9SDmitry Osipenko 		return false;
339acc6a3a9SDmitry Osipenko 
340acc6a3a9SDmitry Osipenko 	if (plane->index == 2 && dc->soc->has_win_c_without_vert_filter)
341acc6a3a9SDmitry Osipenko 		return false;
342acc6a3a9SDmitry Osipenko 
343acc6a3a9SDmitry Osipenko 	return true;
344acc6a3a9SDmitry Osipenko }
345acc6a3a9SDmitry Osipenko 
tegra_dc_setup_window(struct tegra_plane * plane,const struct tegra_dc_window * window)3461087fac1SThierry Reding static void tegra_dc_setup_window(struct tegra_plane *plane,
34710288eeaSThierry Reding 				  const struct tegra_dc_window *window)
34810288eeaSThierry Reding {
34910288eeaSThierry Reding 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
3501087fac1SThierry Reding 	struct tegra_dc *dc = plane->dc;
351a649b133SThierry Reding 	unsigned int planes;
3521087fac1SThierry Reding 	u32 value;
353a649b133SThierry Reding 	bool yuv;
35410288eeaSThierry Reding 
35510288eeaSThierry Reding 	/*
35610288eeaSThierry Reding 	 * For YUV planar modes, the number of bytes per pixel takes into
35710288eeaSThierry Reding 	 * account only the luma component and therefore is 1.
35810288eeaSThierry Reding 	 */
359a649b133SThierry Reding 	yuv = tegra_plane_format_is_yuv(window->format, &planes, NULL);
36010288eeaSThierry Reding 	if (!yuv)
36110288eeaSThierry Reding 		bpp = window->bits_per_pixel / 8;
36210288eeaSThierry Reding 	else
363a649b133SThierry Reding 		bpp = (planes > 1) ? 1 : 2;
36410288eeaSThierry Reding 
3651087fac1SThierry Reding 	tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH);
3661087fac1SThierry Reding 	tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP);
36710288eeaSThierry Reding 
36810288eeaSThierry Reding 	value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
3691087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_POSITION);
37010288eeaSThierry Reding 
37110288eeaSThierry Reding 	value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
3721087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_SIZE);
37310288eeaSThierry Reding 
37410288eeaSThierry Reding 	h_offset = window->src.x * bpp;
37510288eeaSThierry Reding 	v_offset = window->src.y;
37610288eeaSThierry Reding 	h_size = window->src.w * bpp;
37710288eeaSThierry Reding 	v_size = window->src.h;
37810288eeaSThierry Reding 
379cd740777SDmitry Osipenko 	if (window->reflect_x)
380cd740777SDmitry Osipenko 		h_offset += (window->src.w - 1) * bpp;
381cd740777SDmitry Osipenko 
382cd740777SDmitry Osipenko 	if (window->reflect_y)
383cd740777SDmitry Osipenko 		v_offset += window->src.h - 1;
384cd740777SDmitry Osipenko 
38510288eeaSThierry Reding 	value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
3861087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE);
38710288eeaSThierry Reding 
38810288eeaSThierry Reding 	/*
38910288eeaSThierry Reding 	 * For DDA computations the number of bytes per pixel for YUV planar
39010288eeaSThierry Reding 	 * modes needs to take into account all Y, U and V components.
39110288eeaSThierry Reding 	 */
392a649b133SThierry Reding 	if (yuv && planes > 1)
39310288eeaSThierry Reding 		bpp = 2;
39410288eeaSThierry Reding 
39510288eeaSThierry Reding 	h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
39610288eeaSThierry Reding 	v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
39710288eeaSThierry Reding 
39810288eeaSThierry Reding 	value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
3991087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_DDA_INC);
40010288eeaSThierry Reding 
40110288eeaSThierry Reding 	h_dda = compute_initial_dda(window->src.x);
40210288eeaSThierry Reding 	v_dda = compute_initial_dda(window->src.y);
40310288eeaSThierry Reding 
4041087fac1SThierry Reding 	tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA);
4051087fac1SThierry Reding 	tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA);
40610288eeaSThierry Reding 
4071087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE);
4081087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE);
40910288eeaSThierry Reding 
4101087fac1SThierry Reding 	tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR);
41110288eeaSThierry Reding 
412a649b133SThierry Reding 	if (yuv && planes > 1) {
4131087fac1SThierry Reding 		tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U);
414a649b133SThierry Reding 
415a649b133SThierry Reding 		if (planes > 2)
4161087fac1SThierry Reding 			tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V);
417a649b133SThierry Reding 
41810288eeaSThierry Reding 		value = window->stride[1] << 16 | window->stride[0];
4191087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE);
42010288eeaSThierry Reding 	} else {
4211087fac1SThierry Reding 		tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE);
42210288eeaSThierry Reding 	}
42310288eeaSThierry Reding 
4241087fac1SThierry Reding 	tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET);
4251087fac1SThierry Reding 	tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET);
42610288eeaSThierry Reding 
427c134f019SThierry Reding 	if (dc->soc->supports_block_linear) {
428c134f019SThierry Reding 		unsigned long height = window->tiling.value;
429c134f019SThierry Reding 
430c134f019SThierry Reding 		switch (window->tiling.mode) {
431c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
432c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_PITCH;
433c134f019SThierry Reding 			break;
434c134f019SThierry Reding 
435c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
436c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_TILED;
437c134f019SThierry Reding 			break;
438c134f019SThierry Reding 
439c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
440c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
441c134f019SThierry Reding 				DC_WINBUF_SURFACE_KIND_BLOCK;
442c134f019SThierry Reding 			break;
443c134f019SThierry Reding 		}
444c134f019SThierry Reding 
4451087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND);
44610288eeaSThierry Reding 	} else {
447c134f019SThierry Reding 		switch (window->tiling.mode) {
448c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
44910288eeaSThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
45010288eeaSThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
451c134f019SThierry Reding 			break;
452c134f019SThierry Reding 
453c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
454c134f019SThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
455c134f019SThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_TILE;
456c134f019SThierry Reding 			break;
457c134f019SThierry Reding 
458c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
4594aa3df71SThierry Reding 			/*
4604aa3df71SThierry Reding 			 * No need to handle this here because ->atomic_check
4614aa3df71SThierry Reding 			 * will already have filtered it out.
4624aa3df71SThierry Reding 			 */
4634aa3df71SThierry Reding 			break;
46410288eeaSThierry Reding 		}
46510288eeaSThierry Reding 
4661087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE);
467c134f019SThierry Reding 	}
46810288eeaSThierry Reding 
46910288eeaSThierry Reding 	value = WIN_ENABLE;
47010288eeaSThierry Reding 
47110288eeaSThierry Reding 	if (yuv) {
47210288eeaSThierry Reding 		/* setup default colorspace conversion coefficients */
4731087fac1SThierry Reding 		tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF);
4741087fac1SThierry Reding 		tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB);
4751087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR);
4761087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR);
4771087fac1SThierry Reding 		tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG);
4781087fac1SThierry Reding 		tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG);
4791087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB);
4801087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB);
48110288eeaSThierry Reding 
48210288eeaSThierry Reding 		value |= CSC_ENABLE;
48310288eeaSThierry Reding 	} else if (window->bits_per_pixel < 24) {
48410288eeaSThierry Reding 		value |= COLOR_EXPAND;
48510288eeaSThierry Reding 	}
48610288eeaSThierry Reding 
487cd740777SDmitry Osipenko 	if (window->reflect_x)
488cd740777SDmitry Osipenko 		value |= H_DIRECTION;
489cd740777SDmitry Osipenko 
490e9e476f7SDmitry Osipenko 	if (window->reflect_y)
49110288eeaSThierry Reding 		value |= V_DIRECTION;
49210288eeaSThierry Reding 
493acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_horizontal_filtering(plane, window)) {
494acc6a3a9SDmitry Osipenko 		/*
495acc6a3a9SDmitry Osipenko 		 * Enable horizontal 6-tap filter and set filtering
496acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
497acc6a3a9SDmitry Osipenko 		 */
498acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x00008000, DC_WIN_H_FILTER_P(0));
499acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3e087ce1, DC_WIN_H_FILTER_P(1));
500acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3b117ac1, DC_WIN_H_FILTER_P(2));
501acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x591b73aa, DC_WIN_H_FILTER_P(3));
502acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x57256d9a, DC_WIN_H_FILTER_P(4));
503acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x552f668b, DC_WIN_H_FILTER_P(5));
504acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x73385e8b, DC_WIN_H_FILTER_P(6));
505acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x72435583, DC_WIN_H_FILTER_P(7));
506acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x714c4c8b, DC_WIN_H_FILTER_P(8));
507acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x70554393, DC_WIN_H_FILTER_P(9));
508acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x715e389b, DC_WIN_H_FILTER_P(10));
509acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x71662faa, DC_WIN_H_FILTER_P(11));
510acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x536d25ba, DC_WIN_H_FILTER_P(12));
511acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x55731bca, DC_WIN_H_FILTER_P(13));
512acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x387a11d9, DC_WIN_H_FILTER_P(14));
513acc6a3a9SDmitry Osipenko 		tegra_plane_writel(plane, 0x3c7c08f1, DC_WIN_H_FILTER_P(15));
514acc6a3a9SDmitry Osipenko 
515acc6a3a9SDmitry Osipenko 		value |= H_FILTER;
516acc6a3a9SDmitry Osipenko 	}
517acc6a3a9SDmitry Osipenko 
518acc6a3a9SDmitry Osipenko 	if (tegra_plane_use_vertical_filtering(plane, window)) {
519acc6a3a9SDmitry Osipenko 		unsigned int i, k;
520acc6a3a9SDmitry Osipenko 
521acc6a3a9SDmitry Osipenko 		/*
522acc6a3a9SDmitry Osipenko 		 * Enable vertical 2-tap filter and set filtering
523acc6a3a9SDmitry Osipenko 		 * coefficients to the default values defined in TRM.
524acc6a3a9SDmitry Osipenko 		 */
525acc6a3a9SDmitry Osipenko 		for (i = 0, k = 128; i < 16; i++, k -= 8)
526acc6a3a9SDmitry Osipenko 			tegra_plane_writel(plane, k, DC_WIN_V_FILTER_P(i));
527acc6a3a9SDmitry Osipenko 
528acc6a3a9SDmitry Osipenko 		value |= V_FILTER;
529acc6a3a9SDmitry Osipenko 	}
530acc6a3a9SDmitry Osipenko 
5311087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
53210288eeaSThierry Reding 
533a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending)
534ab7d3f58SThierry Reding 		tegra_plane_setup_blending_legacy(plane);
535a43d0a00SDmitry Osipenko 	else
536a43d0a00SDmitry Osipenko 		tegra_plane_setup_blending(plane, window);
537c7679306SThierry Reding }
538c7679306SThierry Reding 
539511c7023SThierry Reding static const u32 tegra20_primary_formats[] = {
540511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
541511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
542c7679306SThierry Reding 	DRM_FORMAT_RGB565,
543511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
544511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
545511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
546ebae8d07SThierry Reding 	/* non-native formats */
547ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
548ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
549ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
550ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
551511c7023SThierry Reding };
552511c7023SThierry Reding 
553e90124cbSThierry Reding static const u64 tegra20_modifiers[] = {
554e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
555e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_TEGRA_TILED,
556e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
557e90124cbSThierry Reding };
558e90124cbSThierry Reding 
559511c7023SThierry Reding static const u32 tegra114_primary_formats[] = {
560511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
561511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
562511c7023SThierry Reding 	DRM_FORMAT_RGB565,
563511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
564511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
565511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
566511c7023SThierry Reding 	/* new on Tegra114 */
567511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
568511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
569511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
570511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
571511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
572511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
573511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
574511c7023SThierry Reding 	DRM_FORMAT_BGR565,
575511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
576511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
577511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
578511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
579511c7023SThierry Reding };
580511c7023SThierry Reding 
581511c7023SThierry Reding static const u32 tegra124_primary_formats[] = {
582511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
583511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
584511c7023SThierry Reding 	DRM_FORMAT_RGB565,
585511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
586511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
587511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
588511c7023SThierry Reding 	/* new on Tegra114 */
589511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
590511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
591511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
592511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
593511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
594511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
595511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
596511c7023SThierry Reding 	DRM_FORMAT_BGR565,
597511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
598511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
599511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
600511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
601511c7023SThierry Reding 	/* new on Tegra124 */
602511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
603511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
604c7679306SThierry Reding };
605c7679306SThierry Reding 
606e90124cbSThierry Reding static const u64 tegra124_modifiers[] = {
607e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
608e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
609e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
610e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
611e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
612e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
613e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
614e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
615e90124cbSThierry Reding };
616e90124cbSThierry Reding 
tegra_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)6174aa3df71SThierry Reding static int tegra_plane_atomic_check(struct drm_plane *plane,
6187c11b99aSMaxime Ripard 				    struct drm_atomic_state *state)
6194aa3df71SThierry Reding {
6207c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
6217c11b99aSMaxime Ripard 										 plane);
622ba5c1649SMaxime Ripard 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
623cd740777SDmitry Osipenko 	unsigned int supported_rotation = DRM_MODE_ROTATE_0 |
624cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_X |
625cd740777SDmitry Osipenko 					  DRM_MODE_REFLECT_Y;
626ba5c1649SMaxime Ripard 	unsigned int rotation = new_plane_state->rotation;
6278f604f8cSThierry Reding 	struct tegra_bo_tiling *tiling = &plane_state->tiling;
62847802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
629ba5c1649SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
630c7679306SThierry Reding 	int err;
631c7679306SThierry Reding 
63204d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
63304d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
63404d5d5dfSDmitry Osipenko 
6354aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
63604d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
63704d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
6384aa3df71SThierry Reding 		return 0;
63904d5d5dfSDmitry Osipenko 	}
6404aa3df71SThierry Reding 
641ba5c1649SMaxime Ripard 	err = tegra_plane_format(new_plane_state->fb->format->format,
6423dae08bcSDmitry Osipenko 				 &plane_state->format,
6438f604f8cSThierry Reding 				 &plane_state->swap);
6444aa3df71SThierry Reding 	if (err < 0)
6454aa3df71SThierry Reding 		return err;
6464aa3df71SThierry Reding 
647ebae8d07SThierry Reding 	/*
648ebae8d07SThierry Reding 	 * Tegra20 and Tegra30 are special cases here because they support
649ebae8d07SThierry Reding 	 * only variants of specific formats with an alpha component, but not
650ebae8d07SThierry Reding 	 * the corresponding opaque formats. However, the opaque formats can
651ebae8d07SThierry Reding 	 * be emulated by disabling alpha blending for the plane.
652ebae8d07SThierry Reding 	 */
653a43d0a00SDmitry Osipenko 	if (dc->soc->has_legacy_blending) {
6543dae08bcSDmitry Osipenko 		err = tegra_plane_setup_legacy_state(tegra, plane_state);
655ebae8d07SThierry Reding 		if (err < 0)
656ebae8d07SThierry Reding 			return err;
657ebae8d07SThierry Reding 	}
658ebae8d07SThierry Reding 
659ba5c1649SMaxime Ripard 	err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
6608f604f8cSThierry Reding 	if (err < 0)
6618f604f8cSThierry Reding 		return err;
6628f604f8cSThierry Reding 
6638f604f8cSThierry Reding 	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
6644aa3df71SThierry Reding 	    !dc->soc->supports_block_linear) {
6654aa3df71SThierry Reding 		DRM_ERROR("hardware doesn't support block linear mode\n");
6664aa3df71SThierry Reding 		return -EINVAL;
6674aa3df71SThierry Reding 	}
6684aa3df71SThierry Reding 
669cd740777SDmitry Osipenko 	/*
670cd740777SDmitry Osipenko 	 * Older userspace used custom BO flag in order to specify the Y
671cd740777SDmitry Osipenko 	 * reflection, while modern userspace uses the generic DRM rotation
672cd740777SDmitry Osipenko 	 * property in order to achieve the same result.  The legacy BO flag
673cd740777SDmitry Osipenko 	 * duplicates the DRM rotation property when both are set.
674cd740777SDmitry Osipenko 	 */
675ba5c1649SMaxime Ripard 	if (tegra_fb_is_bottom_up(new_plane_state->fb))
676cd740777SDmitry Osipenko 		rotation |= DRM_MODE_REFLECT_Y;
677cd740777SDmitry Osipenko 
678cd740777SDmitry Osipenko 	rotation = drm_rotation_simplify(rotation, supported_rotation);
679cd740777SDmitry Osipenko 
680cd740777SDmitry Osipenko 	if (rotation & DRM_MODE_REFLECT_X)
681cd740777SDmitry Osipenko 		plane_state->reflect_x = true;
682cd740777SDmitry Osipenko 	else
683cd740777SDmitry Osipenko 		plane_state->reflect_x = false;
684995c5a50SThierry Reding 
685995c5a50SThierry Reding 	if (rotation & DRM_MODE_REFLECT_Y)
686e9e476f7SDmitry Osipenko 		plane_state->reflect_y = true;
687995c5a50SThierry Reding 	else
688e9e476f7SDmitry Osipenko 		plane_state->reflect_y = false;
689995c5a50SThierry Reding 
6904aa3df71SThierry Reding 	/*
6914aa3df71SThierry Reding 	 * Tegra doesn't support different strides for U and V planes so we
6924aa3df71SThierry Reding 	 * error out if the user tries to display a framebuffer with such a
6934aa3df71SThierry Reding 	 * configuration.
6944aa3df71SThierry Reding 	 */
695ba5c1649SMaxime Ripard 	if (new_plane_state->fb->format->num_planes > 2) {
696ba5c1649SMaxime Ripard 		if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
6974aa3df71SThierry Reding 			DRM_ERROR("unsupported UV-plane configuration\n");
6984aa3df71SThierry Reding 			return -EINVAL;
6994aa3df71SThierry Reding 		}
7004aa3df71SThierry Reding 	}
7014aa3df71SThierry Reding 
702ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
70347802b09SThierry Reding 	if (err < 0)
70447802b09SThierry Reding 		return err;
70547802b09SThierry Reding 
7064aa3df71SThierry Reding 	return 0;
7074aa3df71SThierry Reding }
7084aa3df71SThierry Reding 
tegra_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)709a4bfa096SThierry Reding static void tegra_plane_atomic_disable(struct drm_plane *plane,
710977697e2SMaxime Ripard 				       struct drm_atomic_state *state)
71180d3eef1SDmitry Osipenko {
712977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
713977697e2SMaxime Ripard 									   plane);
714a4bfa096SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
71580d3eef1SDmitry Osipenko 	u32 value;
71680d3eef1SDmitry Osipenko 
717a4bfa096SThierry Reding 	/* rien ne va plus */
718a4bfa096SThierry Reding 	if (!old_state || !old_state->crtc)
719a4bfa096SThierry Reding 		return;
720a4bfa096SThierry Reding 
7211087fac1SThierry Reding 	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
72280d3eef1SDmitry Osipenko 	value &= ~WIN_ENABLE;
7231087fac1SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
72480d3eef1SDmitry Osipenko }
72580d3eef1SDmitry Osipenko 
tegra_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)7264aa3df71SThierry Reding static void tegra_plane_atomic_update(struct drm_plane *plane,
727977697e2SMaxime Ripard 				      struct drm_atomic_state *state)
7284aa3df71SThierry Reding {
72937418bf1SMaxime Ripard 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
73037418bf1SMaxime Ripard 									   plane);
73141016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
732e05162c0SMaxime Ripard 	struct drm_framebuffer *fb = new_state->fb;
7334aa3df71SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
7344aa3df71SThierry Reding 	struct tegra_dc_window window;
7354aa3df71SThierry Reding 	unsigned int i;
7364aa3df71SThierry Reding 
7374aa3df71SThierry Reding 	/* rien ne va plus */
738e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
7394aa3df71SThierry Reding 		return;
7404aa3df71SThierry Reding 
741e05162c0SMaxime Ripard 	if (!new_state->visible)
742977697e2SMaxime Ripard 		return tegra_plane_atomic_disable(plane, state);
74380d3eef1SDmitry Osipenko 
744c7679306SThierry Reding 	memset(&window, 0, sizeof(window));
745e05162c0SMaxime Ripard 	window.src.x = new_state->src.x1 >> 16;
746e05162c0SMaxime Ripard 	window.src.y = new_state->src.y1 >> 16;
747e05162c0SMaxime Ripard 	window.src.w = drm_rect_width(&new_state->src) >> 16;
748e05162c0SMaxime Ripard 	window.src.h = drm_rect_height(&new_state->src) >> 16;
749e05162c0SMaxime Ripard 	window.dst.x = new_state->dst.x1;
750e05162c0SMaxime Ripard 	window.dst.y = new_state->dst.y1;
751e05162c0SMaxime Ripard 	window.dst.w = drm_rect_width(&new_state->dst);
752e05162c0SMaxime Ripard 	window.dst.h = drm_rect_height(&new_state->dst);
753272725c7SVille Syrjälä 	window.bits_per_pixel = fb->format->cpp[0] * 8;
75441016fe1SMaxime Ripard 	window.reflect_x = tegra_plane_state->reflect_x;
75541016fe1SMaxime Ripard 	window.reflect_y = tegra_plane_state->reflect_y;
756c7679306SThierry Reding 
7578f604f8cSThierry Reding 	/* copy from state */
758e05162c0SMaxime Ripard 	window.zpos = new_state->normalized_zpos;
75941016fe1SMaxime Ripard 	window.tiling = tegra_plane_state->tiling;
76041016fe1SMaxime Ripard 	window.format = tegra_plane_state->format;
76141016fe1SMaxime Ripard 	window.swap = tegra_plane_state->swap;
762c7679306SThierry Reding 
763bcb0b461SVille Syrjälä 	for (i = 0; i < fb->format->num_planes; i++) {
76441016fe1SMaxime Ripard 		window.base[i] = tegra_plane_state->iova[i] + fb->offsets[i];
76508ee0178SDmitry Osipenko 
76608ee0178SDmitry Osipenko 		/*
76708ee0178SDmitry Osipenko 		 * Tegra uses a shared stride for UV planes. Framebuffers are
76808ee0178SDmitry Osipenko 		 * already checked for this in the tegra_plane_atomic_check()
76908ee0178SDmitry Osipenko 		 * function, so it's safe to ignore the V-plane pitch here.
77008ee0178SDmitry Osipenko 		 */
77108ee0178SDmitry Osipenko 		if (i < 2)
7724aa3df71SThierry Reding 			window.stride[i] = fb->pitches[i];
773c7679306SThierry Reding 	}
774c7679306SThierry Reding 
7751087fac1SThierry Reding 	tegra_dc_setup_window(p, &window);
7764aa3df71SThierry Reding }
7774aa3df71SThierry Reding 
778a4bfa096SThierry Reding static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
7792e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
7802e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
7814aa3df71SThierry Reding 	.atomic_check = tegra_plane_atomic_check,
7824aa3df71SThierry Reding 	.atomic_disable = tegra_plane_atomic_disable,
783a4bfa096SThierry Reding 	.atomic_update = tegra_plane_atomic_update,
784c7679306SThierry Reding };
785c7679306SThierry Reding 
tegra_plane_get_possible_crtcs(struct drm_device * drm)78689f65018SThierry Reding static unsigned long tegra_plane_get_possible_crtcs(struct drm_device *drm)
787c7679306SThierry Reding {
788518e6227SThierry Reding 	/*
789518e6227SThierry Reding 	 * Ideally this would use drm_crtc_mask(), but that would require the
790518e6227SThierry Reding 	 * CRTC to already be in the mode_config's list of CRTCs. However, it
791518e6227SThierry Reding 	 * will only be added to that list in the drm_crtc_init_with_planes()
792518e6227SThierry Reding 	 * (in tegra_dc_init()), which in turn requires registration of these
793518e6227SThierry Reding 	 * planes. So we have ourselves a nice little chicken and egg problem
794518e6227SThierry Reding 	 * here.
795518e6227SThierry Reding 	 *
796518e6227SThierry Reding 	 * We work around this by manually creating the mask from the number
797518e6227SThierry Reding 	 * of CRTCs that have been registered, and should therefore always be
798518e6227SThierry Reding 	 * the same as drm_crtc_index() after registration.
799518e6227SThierry Reding 	 */
80089f65018SThierry Reding 	return 1 << drm->mode_config.num_crtc;
80189f65018SThierry Reding }
80289f65018SThierry Reding 
tegra_primary_plane_create(struct drm_device * drm,struct tegra_dc * dc)80389f65018SThierry Reding static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
80489f65018SThierry Reding 						    struct tegra_dc *dc)
80589f65018SThierry Reding {
80689f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
80747307954SThierry Reding 	enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
808c7679306SThierry Reding 	struct tegra_plane *plane;
809c7679306SThierry Reding 	unsigned int num_formats;
810e90124cbSThierry Reding 	const u64 *modifiers;
811c7679306SThierry Reding 	const u32 *formats;
812c7679306SThierry Reding 	int err;
813c7679306SThierry Reding 
814c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
815c7679306SThierry Reding 	if (!plane)
816c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
817c7679306SThierry Reding 
8181087fac1SThierry Reding 	/* Always use window A as primary window */
8191087fac1SThierry Reding 	plane->offset = 0xa00;
820c4755fb9SThierry Reding 	plane->index = 0;
8211087fac1SThierry Reding 	plane->dc = dc;
8221087fac1SThierry Reding 
8231087fac1SThierry Reding 	num_formats = dc->soc->num_primary_formats;
8241087fac1SThierry Reding 	formats = dc->soc->primary_formats;
825e90124cbSThierry Reding 	modifiers = dc->soc->modifiers;
826c4755fb9SThierry Reding 
82704d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
82804d5d5dfSDmitry Osipenko 	if (err) {
82904d5d5dfSDmitry Osipenko 		kfree(plane);
83004d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
83104d5d5dfSDmitry Osipenko 	}
83204d5d5dfSDmitry Osipenko 
833518e6227SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
834c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
835e90124cbSThierry Reding 				       num_formats, modifiers, type, NULL);
836c7679306SThierry Reding 	if (err < 0) {
837c7679306SThierry Reding 		kfree(plane);
838c7679306SThierry Reding 		return ERR_PTR(err);
839c7679306SThierry Reding 	}
840c7679306SThierry Reding 
841a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
8423dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
843ab7d3f58SThierry Reding 
844995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
845995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
846995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
8474fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
848cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
849995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
850995c5a50SThierry Reding 	if (err < 0)
851995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
852995c5a50SThierry Reding 			err);
853995c5a50SThierry Reding 
854c7679306SThierry Reding 	return &plane->base;
855c7679306SThierry Reding }
856c7679306SThierry Reding 
857d5ec699dSThierry Reding static const u32 tegra_legacy_cursor_plane_formats[] = {
858c7679306SThierry Reding 	DRM_FORMAT_RGBA8888,
859c7679306SThierry Reding };
860c7679306SThierry Reding 
861d5ec699dSThierry Reding static const u32 tegra_cursor_plane_formats[] = {
862d5ec699dSThierry Reding 	DRM_FORMAT_ARGB8888,
863d5ec699dSThierry Reding };
864d5ec699dSThierry Reding 
tegra_cursor_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)8654aa3df71SThierry Reding static int tegra_cursor_atomic_check(struct drm_plane *plane,
8667c11b99aSMaxime Ripard 				     struct drm_atomic_state *state)
867c7679306SThierry Reding {
8687c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
8697c11b99aSMaxime Ripard 										 plane);
87004d5d5dfSDmitry Osipenko 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
87147802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
87247802b09SThierry Reding 	int err;
87347802b09SThierry Reding 
87404d5d5dfSDmitry Osipenko 	plane_state->peak_memory_bandwidth = 0;
87504d5d5dfSDmitry Osipenko 	plane_state->avg_memory_bandwidth = 0;
87604d5d5dfSDmitry Osipenko 
8774aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
87804d5d5dfSDmitry Osipenko 	if (!new_plane_state->crtc) {
87904d5d5dfSDmitry Osipenko 		plane_state->total_peak_memory_bandwidth = 0;
8804aa3df71SThierry Reding 		return 0;
88104d5d5dfSDmitry Osipenko 	}
882c7679306SThierry Reding 
883c7679306SThierry Reding 	/* scaling not supported for cursor */
884ba5c1649SMaxime Ripard 	if ((new_plane_state->src_w >> 16 != new_plane_state->crtc_w) ||
885ba5c1649SMaxime Ripard 	    (new_plane_state->src_h >> 16 != new_plane_state->crtc_h))
886c7679306SThierry Reding 		return -EINVAL;
887c7679306SThierry Reding 
888c7679306SThierry Reding 	/* only square cursors supported */
889ba5c1649SMaxime Ripard 	if (new_plane_state->src_w != new_plane_state->src_h)
890c7679306SThierry Reding 		return -EINVAL;
891c7679306SThierry Reding 
892ba5c1649SMaxime Ripard 	if (new_plane_state->crtc_w != 32 && new_plane_state->crtc_w != 64 &&
893ba5c1649SMaxime Ripard 	    new_plane_state->crtc_w != 128 && new_plane_state->crtc_w != 256)
8944aa3df71SThierry Reding 		return -EINVAL;
8954aa3df71SThierry Reding 
896ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(tegra, new_plane_state);
89747802b09SThierry Reding 	if (err < 0)
89847802b09SThierry Reding 		return err;
89947802b09SThierry Reding 
9004aa3df71SThierry Reding 	return 0;
9014aa3df71SThierry Reding }
9024aa3df71SThierry Reding 
__tegra_cursor_atomic_update(struct drm_plane * plane,struct drm_plane_state * new_state)903cae7472eSThierry Reding static void __tegra_cursor_atomic_update(struct drm_plane *plane,
904cae7472eSThierry Reding 					 struct drm_plane_state *new_state)
9054aa3df71SThierry Reding {
90641016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
907e05162c0SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
908d5ec699dSThierry Reding 	struct tegra_drm *tegra = plane->dev->dev_private;
909d5ec699dSThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
910d5ec699dSThierry Reding 	u64 dma_mask = *dc->dev->dma_mask;
911d5ec699dSThierry Reding #endif
912d5ec699dSThierry Reding 	unsigned int x, y;
913d5ec699dSThierry Reding 	u32 value = 0;
9144aa3df71SThierry Reding 
9154aa3df71SThierry Reding 	/* rien ne va plus */
916e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
9174aa3df71SThierry Reding 		return;
9184aa3df71SThierry Reding 
919d5ec699dSThierry Reding 	/*
920d5ec699dSThierry Reding 	 * Legacy display supports hardware clipping of the cursor, but
921d5ec699dSThierry Reding 	 * nvdisplay relies on software to clip the cursor to the screen.
922d5ec699dSThierry Reding 	 */
923d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay)
924d5ec699dSThierry Reding 		value |= CURSOR_CLIP_DISPLAY;
925d5ec699dSThierry Reding 
926e05162c0SMaxime Ripard 	switch (new_state->crtc_w) {
927c7679306SThierry Reding 	case 32:
928c7679306SThierry Reding 		value |= CURSOR_SIZE_32x32;
929c7679306SThierry Reding 		break;
930c7679306SThierry Reding 
931c7679306SThierry Reding 	case 64:
932c7679306SThierry Reding 		value |= CURSOR_SIZE_64x64;
933c7679306SThierry Reding 		break;
934c7679306SThierry Reding 
935c7679306SThierry Reding 	case 128:
936c7679306SThierry Reding 		value |= CURSOR_SIZE_128x128;
937c7679306SThierry Reding 		break;
938c7679306SThierry Reding 
939c7679306SThierry Reding 	case 256:
940c7679306SThierry Reding 		value |= CURSOR_SIZE_256x256;
941c7679306SThierry Reding 		break;
942c7679306SThierry Reding 
943c7679306SThierry Reding 	default:
944c52e167bSThierry Reding 		WARN(1, "cursor size %ux%u not supported\n",
945e05162c0SMaxime Ripard 		     new_state->crtc_w, new_state->crtc_h);
9464aa3df71SThierry Reding 		return;
947c7679306SThierry Reding 	}
948c7679306SThierry Reding 
94941016fe1SMaxime Ripard 	value |= (tegra_plane_state->iova[0] >> 10) & 0x3fffff;
950c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
951c7679306SThierry Reding 
952c7679306SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
953d5ec699dSThierry Reding 	value = (tegra_plane_state->iova[0] >> 32) & (dma_mask >> 32);
954c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
955c7679306SThierry Reding #endif
956c7679306SThierry Reding 
957c7679306SThierry Reding 	/* enable cursor and set blend mode */
958c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
959c7679306SThierry Reding 	value |= CURSOR_ENABLE;
960c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
961c7679306SThierry Reding 
962c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
963c7679306SThierry Reding 	value &= ~CURSOR_DST_BLEND_MASK;
964c7679306SThierry Reding 	value &= ~CURSOR_SRC_BLEND_MASK;
965d5ec699dSThierry Reding 
966d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay)
967d5ec699dSThierry Reding 		value &= ~CURSOR_COMPOSITION_MODE_XOR;
968d5ec699dSThierry Reding 	else
969c7679306SThierry Reding 		value |= CURSOR_MODE_NORMAL;
970d5ec699dSThierry Reding 
971c7679306SThierry Reding 	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
972c7679306SThierry Reding 	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
973c7679306SThierry Reding 	value |= CURSOR_ALPHA;
974c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
975c7679306SThierry Reding 
976d5ec699dSThierry Reding 	/* nvdisplay relies on software for clipping */
977d5ec699dSThierry Reding 	if (dc->soc->has_nvdisplay) {
978d5ec699dSThierry Reding 		struct drm_rect src;
979d5ec699dSThierry Reding 
980d5ec699dSThierry Reding 		x = new_state->dst.x1;
981d5ec699dSThierry Reding 		y = new_state->dst.y1;
982d5ec699dSThierry Reding 
983d5ec699dSThierry Reding 		drm_rect_fp_to_int(&src, &new_state->src);
984d5ec699dSThierry Reding 
985d5ec699dSThierry Reding 		value = (src.y1 & tegra->vmask) << 16 | (src.x1 & tegra->hmask);
986d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_POINT_IN_CURSOR);
987d5ec699dSThierry Reding 
988d5ec699dSThierry Reding 		value = (drm_rect_height(&src) & tegra->vmask) << 16 |
989d5ec699dSThierry Reding 			(drm_rect_width(&src) & tegra->hmask);
990d5ec699dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_PCALC_HEAD_SET_CROPPED_SIZE_IN_CURSOR);
991d5ec699dSThierry Reding 	} else {
992d5ec699dSThierry Reding 		x = new_state->crtc_x;
993d5ec699dSThierry Reding 		y = new_state->crtc_y;
994d5ec699dSThierry Reding 	}
995d5ec699dSThierry Reding 
996c7679306SThierry Reding 	/* position the cursor */
997d5ec699dSThierry Reding 	value = ((y & tegra->vmask) << 16) | (x & tegra->hmask);
998c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
999c7679306SThierry Reding }
1000c7679306SThierry Reding 
tegra_cursor_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)1001cae7472eSThierry Reding static void tegra_cursor_atomic_update(struct drm_plane *plane,
1002cae7472eSThierry Reding 				       struct drm_atomic_state *state)
1003cae7472eSThierry Reding {
1004cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1005cae7472eSThierry Reding 
1006cae7472eSThierry Reding 	__tegra_cursor_atomic_update(plane, new_state);
1007cae7472eSThierry Reding }
1008cae7472eSThierry Reding 
tegra_cursor_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)10094aa3df71SThierry Reding static void tegra_cursor_atomic_disable(struct drm_plane *plane,
1010977697e2SMaxime Ripard 					struct drm_atomic_state *state)
1011c7679306SThierry Reding {
1012977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
1013977697e2SMaxime Ripard 									   plane);
10144aa3df71SThierry Reding 	struct tegra_dc *dc;
1015c7679306SThierry Reding 	u32 value;
1016c7679306SThierry Reding 
10174aa3df71SThierry Reding 	/* rien ne va plus */
10184aa3df71SThierry Reding 	if (!old_state || !old_state->crtc)
10194aa3df71SThierry Reding 		return;
10204aa3df71SThierry Reding 
10214aa3df71SThierry Reding 	dc = to_tegra_dc(old_state->crtc);
1022c7679306SThierry Reding 
1023c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
1024c7679306SThierry Reding 	value &= ~CURSOR_ENABLE;
1025c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
1026c7679306SThierry Reding }
1027c7679306SThierry Reding 
tegra_cursor_atomic_async_check(struct drm_plane * plane,struct drm_atomic_state * state)1028cae7472eSThierry Reding static int tegra_cursor_atomic_async_check(struct drm_plane *plane, struct drm_atomic_state *state)
1029cae7472eSThierry Reding {
1030cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1031cae7472eSThierry Reding 	struct drm_crtc_state *crtc_state;
1032cae7472eSThierry Reding 	int min_scale, max_scale;
1033cae7472eSThierry Reding 	int err;
1034cae7472eSThierry Reding 
1035cae7472eSThierry Reding 	crtc_state = drm_atomic_get_existing_crtc_state(state, new_state->crtc);
1036cae7472eSThierry Reding 	if (WARN_ON(!crtc_state))
1037cae7472eSThierry Reding 		return -EINVAL;
1038cae7472eSThierry Reding 
1039cae7472eSThierry Reding 	if (!crtc_state->active)
1040cae7472eSThierry Reding 		return -EINVAL;
1041cae7472eSThierry Reding 
1042cae7472eSThierry Reding 	if (plane->state->crtc != new_state->crtc ||
1043cae7472eSThierry Reding 	    plane->state->src_w != new_state->src_w ||
1044cae7472eSThierry Reding 	    plane->state->src_h != new_state->src_h ||
1045cae7472eSThierry Reding 	    plane->state->crtc_w != new_state->crtc_w ||
1046cae7472eSThierry Reding 	    plane->state->crtc_h != new_state->crtc_h ||
1047cae7472eSThierry Reding 	    plane->state->fb != new_state->fb ||
1048cae7472eSThierry Reding 	    plane->state->fb == NULL)
1049cae7472eSThierry Reding 		return -EINVAL;
1050cae7472eSThierry Reding 
1051cae7472eSThierry Reding 	min_scale = (1 << 16) / 8;
1052cae7472eSThierry Reding 	max_scale = (8 << 16) / 1;
1053cae7472eSThierry Reding 
1054cae7472eSThierry Reding 	err = drm_atomic_helper_check_plane_state(new_state, crtc_state, min_scale, max_scale,
1055cae7472eSThierry Reding 						  true, true);
1056cae7472eSThierry Reding 	if (err < 0)
1057cae7472eSThierry Reding 		return err;
1058cae7472eSThierry Reding 
1059cae7472eSThierry Reding 	if (new_state->visible != plane->state->visible)
1060cae7472eSThierry Reding 		return -EINVAL;
1061cae7472eSThierry Reding 
1062cae7472eSThierry Reding 	return 0;
1063cae7472eSThierry Reding }
1064cae7472eSThierry Reding 
tegra_cursor_atomic_async_update(struct drm_plane * plane,struct drm_atomic_state * state)1065cae7472eSThierry Reding static void tegra_cursor_atomic_async_update(struct drm_plane *plane,
1066cae7472eSThierry Reding 					     struct drm_atomic_state *state)
1067cae7472eSThierry Reding {
1068cae7472eSThierry Reding 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, plane);
1069cae7472eSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
1070cae7472eSThierry Reding 
1071cae7472eSThierry Reding 	plane->state->src_x = new_state->src_x;
1072cae7472eSThierry Reding 	plane->state->src_y = new_state->src_y;
1073cae7472eSThierry Reding 	plane->state->crtc_x = new_state->crtc_x;
1074cae7472eSThierry Reding 	plane->state->crtc_y = new_state->crtc_y;
1075cae7472eSThierry Reding 
1076cae7472eSThierry Reding 	if (new_state->visible) {
1077cae7472eSThierry Reding 		struct tegra_plane *p = to_tegra_plane(plane);
1078cae7472eSThierry Reding 		u32 value;
1079cae7472eSThierry Reding 
1080cae7472eSThierry Reding 		__tegra_cursor_atomic_update(plane, new_state);
1081cae7472eSThierry Reding 
1082cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) << 8 | GENERAL_UPDATE;
1083cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1084cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1085cae7472eSThierry Reding 
1086cae7472eSThierry Reding 		value = (WIN_A_ACT_REQ << p->index) | GENERAL_ACT_REQ;
1087cae7472eSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
1088cae7472eSThierry Reding 		(void)tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
1089cae7472eSThierry Reding 	}
1090cae7472eSThierry Reding }
1091cae7472eSThierry Reding 
10924aa3df71SThierry Reding static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
10932e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
10942e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
10954aa3df71SThierry Reding 	.atomic_check = tegra_cursor_atomic_check,
10964aa3df71SThierry Reding 	.atomic_update = tegra_cursor_atomic_update,
10974aa3df71SThierry Reding 	.atomic_disable = tegra_cursor_atomic_disable,
1098cae7472eSThierry Reding 	.atomic_async_check = tegra_cursor_atomic_async_check,
1099cae7472eSThierry Reding 	.atomic_async_update = tegra_cursor_atomic_async_update,
1100c7679306SThierry Reding };
1101c7679306SThierry Reding 
1102be4306adSDaniel Vetter static const uint64_t linear_modifiers[] = {
1103be4306adSDaniel Vetter 	DRM_FORMAT_MOD_LINEAR,
1104be4306adSDaniel Vetter 	DRM_FORMAT_MOD_INVALID
1105be4306adSDaniel Vetter };
1106be4306adSDaniel Vetter 
tegra_dc_cursor_plane_create(struct drm_device * drm,struct tegra_dc * dc)1107c7679306SThierry Reding static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
1108c7679306SThierry Reding 						      struct tegra_dc *dc)
1109c7679306SThierry Reding {
111089f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1111c7679306SThierry Reding 	struct tegra_plane *plane;
1112c7679306SThierry Reding 	unsigned int num_formats;
1113c7679306SThierry Reding 	const u32 *formats;
1114c7679306SThierry Reding 	int err;
1115c7679306SThierry Reding 
1116c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1117c7679306SThierry Reding 	if (!plane)
1118c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1119c7679306SThierry Reding 
112047802b09SThierry Reding 	/*
1121a1df3b24SThierry Reding 	 * This index is kind of fake. The cursor isn't a regular plane, but
1122a1df3b24SThierry Reding 	 * its update and activation request bits in DC_CMD_STATE_CONTROL do
1123a1df3b24SThierry Reding 	 * use the same programming. Setting this fake index here allows the
1124a1df3b24SThierry Reding 	 * code in tegra_add_plane_state() to do the right thing without the
1125a1df3b24SThierry Reding 	 * need to special-casing the cursor plane.
112647802b09SThierry Reding 	 */
112747802b09SThierry Reding 	plane->index = 6;
11281087fac1SThierry Reding 	plane->dc = dc;
112947802b09SThierry Reding 
1130d5ec699dSThierry Reding 	if (!dc->soc->has_nvdisplay) {
1131d5ec699dSThierry Reding 		num_formats = ARRAY_SIZE(tegra_legacy_cursor_plane_formats);
1132d5ec699dSThierry Reding 		formats = tegra_legacy_cursor_plane_formats;
113304d5d5dfSDmitry Osipenko 
113404d5d5dfSDmitry Osipenko 		err = tegra_plane_interconnect_init(plane);
113504d5d5dfSDmitry Osipenko 		if (err) {
113604d5d5dfSDmitry Osipenko 			kfree(plane);
113704d5d5dfSDmitry Osipenko 			return ERR_PTR(err);
113804d5d5dfSDmitry Osipenko 		}
1139d5ec699dSThierry Reding 	} else {
1140c7679306SThierry Reding 		num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
1141c7679306SThierry Reding 		formats = tegra_cursor_plane_formats;
1142d5ec699dSThierry Reding 	}
1143c7679306SThierry Reding 
114489f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1145c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
1146be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1147e6fc3b68SBen Widawsky 				       DRM_PLANE_TYPE_CURSOR, NULL);
1148c7679306SThierry Reding 	if (err < 0) {
1149c7679306SThierry Reding 		kfree(plane);
1150c7679306SThierry Reding 		return ERR_PTR(err);
1151c7679306SThierry Reding 	}
1152c7679306SThierry Reding 
11534aa3df71SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
1154fce3a51dSThierry Reding 	drm_plane_create_zpos_immutable_property(&plane->base, 255);
11554aa3df71SThierry Reding 
1156c7679306SThierry Reding 	return &plane->base;
1157c7679306SThierry Reding }
1158c7679306SThierry Reding 
1159511c7023SThierry Reding static const u32 tegra20_overlay_formats[] = {
1160511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1161511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1162dee8268fSThierry Reding 	DRM_FORMAT_RGB565,
1163511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1164511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1165511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1166ebae8d07SThierry Reding 	/* non-native formats */
1167ebae8d07SThierry Reding 	DRM_FORMAT_XRGB1555,
1168ebae8d07SThierry Reding 	DRM_FORMAT_RGBX5551,
1169ebae8d07SThierry Reding 	DRM_FORMAT_XBGR8888,
1170ebae8d07SThierry Reding 	DRM_FORMAT_XRGB8888,
1171511c7023SThierry Reding 	/* planar formats */
1172511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1173511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1174511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1175511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1176511c7023SThierry Reding };
1177511c7023SThierry Reding 
1178511c7023SThierry Reding static const u32 tegra114_overlay_formats[] = {
1179511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1180511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1181511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1182511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1183511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1184511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1185511c7023SThierry Reding 	/* new on Tegra114 */
1186511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1187511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1188511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1189511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1190511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1191511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1192511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1193511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1194511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1195511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1196511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1197511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1198511c7023SThierry Reding 	/* planar formats */
1199511c7023SThierry Reding 	DRM_FORMAT_UYVY,
1200511c7023SThierry Reding 	DRM_FORMAT_YUYV,
1201511c7023SThierry Reding 	DRM_FORMAT_YUV420,
1202511c7023SThierry Reding 	DRM_FORMAT_YUV422,
1203a649b133SThierry Reding 	/* semi-planar formats */
1204a649b133SThierry Reding 	DRM_FORMAT_NV12,
1205a649b133SThierry Reding 	DRM_FORMAT_NV21,
1206a649b133SThierry Reding 	DRM_FORMAT_NV16,
1207a649b133SThierry Reding 	DRM_FORMAT_NV61,
1208a649b133SThierry Reding 	DRM_FORMAT_NV24,
1209a649b133SThierry Reding 	DRM_FORMAT_NV42,
1210511c7023SThierry Reding };
1211511c7023SThierry Reding 
1212511c7023SThierry Reding static const u32 tegra124_overlay_formats[] = {
1213511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
1214511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
1215511c7023SThierry Reding 	DRM_FORMAT_RGB565,
1216511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
1217511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
1218511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
1219511c7023SThierry Reding 	/* new on Tegra114 */
1220511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
1221511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
1222511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
1223511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
1224511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
1225511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
1226511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
1227511c7023SThierry Reding 	DRM_FORMAT_BGR565,
1228511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
1229511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
1230511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
1231511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
1232511c7023SThierry Reding 	/* new on Tegra124 */
1233511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
1234511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
1235511c7023SThierry Reding 	/* planar formats */
1236dee8268fSThierry Reding 	DRM_FORMAT_UYVY,
1237f925390eSThierry Reding 	DRM_FORMAT_YUYV,
1238cf5086d3SThierry Reding 	DRM_FORMAT_YVYU,
1239cf5086d3SThierry Reding 	DRM_FORMAT_VYUY,
1240cf5086d3SThierry Reding 	DRM_FORMAT_YUV420, /* YU12 */
1241cf5086d3SThierry Reding 	DRM_FORMAT_YUV422, /* YU16 */
1242cf5086d3SThierry Reding 	DRM_FORMAT_YUV444, /* YU24 */
1243a649b133SThierry Reding 	/* semi-planar formats */
1244a649b133SThierry Reding 	DRM_FORMAT_NV12,
1245a649b133SThierry Reding 	DRM_FORMAT_NV21,
1246a649b133SThierry Reding 	DRM_FORMAT_NV16,
1247a649b133SThierry Reding 	DRM_FORMAT_NV61,
1248a649b133SThierry Reding 	DRM_FORMAT_NV24,
1249a649b133SThierry Reding 	DRM_FORMAT_NV42,
1250dee8268fSThierry Reding };
1251dee8268fSThierry Reding 
tegra_dc_overlay_plane_create(struct drm_device * drm,struct tegra_dc * dc,unsigned int index,bool cursor)1252c7679306SThierry Reding static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
1253c7679306SThierry Reding 						       struct tegra_dc *dc,
12549f446d83SDmitry Osipenko 						       unsigned int index,
12559f446d83SDmitry Osipenko 						       bool cursor)
1256dee8268fSThierry Reding {
125789f65018SThierry Reding 	unsigned long possible_crtcs = tegra_plane_get_possible_crtcs(drm);
1258dee8268fSThierry Reding 	struct tegra_plane *plane;
1259c7679306SThierry Reding 	unsigned int num_formats;
12609f446d83SDmitry Osipenko 	enum drm_plane_type type;
1261c7679306SThierry Reding 	const u32 *formats;
1262c7679306SThierry Reding 	int err;
1263dee8268fSThierry Reding 
1264f002abc1SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
1265dee8268fSThierry Reding 	if (!plane)
1266c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
1267dee8268fSThierry Reding 
12681087fac1SThierry Reding 	plane->offset = 0xa00 + 0x200 * index;
1269c7679306SThierry Reding 	plane->index = index;
12701087fac1SThierry Reding 	plane->dc = dc;
1271dee8268fSThierry Reding 
1272511c7023SThierry Reding 	num_formats = dc->soc->num_overlay_formats;
1273511c7023SThierry Reding 	formats = dc->soc->overlay_formats;
1274c7679306SThierry Reding 
127504d5d5dfSDmitry Osipenko 	err = tegra_plane_interconnect_init(plane);
127604d5d5dfSDmitry Osipenko 	if (err) {
127704d5d5dfSDmitry Osipenko 		kfree(plane);
127804d5d5dfSDmitry Osipenko 		return ERR_PTR(err);
127904d5d5dfSDmitry Osipenko 	}
128004d5d5dfSDmitry Osipenko 
12819f446d83SDmitry Osipenko 	if (!cursor)
12829f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_OVERLAY;
12839f446d83SDmitry Osipenko 	else
12849f446d83SDmitry Osipenko 		type = DRM_PLANE_TYPE_CURSOR;
12859f446d83SDmitry Osipenko 
128689f65018SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
1287301e0ddbSThierry Reding 				       &tegra_plane_funcs, formats,
1288be4306adSDaniel Vetter 				       num_formats, linear_modifiers,
1289be4306adSDaniel Vetter 				       type, NULL);
1290f002abc1SThierry Reding 	if (err < 0) {
1291f002abc1SThierry Reding 		kfree(plane);
1292c7679306SThierry Reding 		return ERR_PTR(err);
1293dee8268fSThierry Reding 	}
1294c7679306SThierry Reding 
1295a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
12963dae08bcSDmitry Osipenko 	drm_plane_create_zpos_property(&plane->base, plane->index, 0, 255);
1297ab7d3f58SThierry Reding 
1298995c5a50SThierry Reding 	err = drm_plane_create_rotation_property(&plane->base,
1299995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0,
1300995c5a50SThierry Reding 						 DRM_MODE_ROTATE_0 |
13014fba6d22SDmitry Osipenko 						 DRM_MODE_ROTATE_180 |
1302cd740777SDmitry Osipenko 						 DRM_MODE_REFLECT_X |
1303995c5a50SThierry Reding 						 DRM_MODE_REFLECT_Y);
1304995c5a50SThierry Reding 	if (err < 0)
1305995c5a50SThierry Reding 		dev_err(dc->dev, "failed to create rotation property: %d\n",
1306995c5a50SThierry Reding 			err);
1307995c5a50SThierry Reding 
1308c7679306SThierry Reding 	return &plane->base;
1309c7679306SThierry Reding }
1310c7679306SThierry Reding 
tegra_dc_add_shared_planes(struct drm_device * drm,struct tegra_dc * dc)131147307954SThierry Reding static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
131247307954SThierry Reding 						    struct tegra_dc *dc)
1313c7679306SThierry Reding {
131447307954SThierry Reding 	struct drm_plane *plane, *primary = NULL;
131547307954SThierry Reding 	unsigned int i, j;
131647307954SThierry Reding 
131747307954SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
131847307954SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
131947307954SThierry Reding 
132047307954SThierry Reding 		if (wgrp->dc == dc->pipe) {
132147307954SThierry Reding 			for (j = 0; j < wgrp->num_windows; j++) {
132247307954SThierry Reding 				unsigned int index = wgrp->windows[j];
132347307954SThierry Reding 
132447307954SThierry Reding 				plane = tegra_shared_plane_create(drm, dc,
132547307954SThierry Reding 								  wgrp->index,
132647307954SThierry Reding 								  index);
132747307954SThierry Reding 				if (IS_ERR(plane))
132847307954SThierry Reding 					return plane;
132947307954SThierry Reding 
133047307954SThierry Reding 				/*
133147307954SThierry Reding 				 * Choose the first shared plane owned by this
133247307954SThierry Reding 				 * head as the primary plane.
133347307954SThierry Reding 				 */
133447307954SThierry Reding 				if (!primary) {
133547307954SThierry Reding 					plane->type = DRM_PLANE_TYPE_PRIMARY;
133647307954SThierry Reding 					primary = plane;
133747307954SThierry Reding 				}
133847307954SThierry Reding 			}
133947307954SThierry Reding 		}
134047307954SThierry Reding 	}
134147307954SThierry Reding 
134247307954SThierry Reding 	return primary;
134347307954SThierry Reding }
134447307954SThierry Reding 
tegra_dc_add_planes(struct drm_device * drm,struct tegra_dc * dc)134547307954SThierry Reding static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
134647307954SThierry Reding 					     struct tegra_dc *dc)
134747307954SThierry Reding {
13488f62142eSThierry Reding 	struct drm_plane *planes[2], *primary;
13499f446d83SDmitry Osipenko 	unsigned int planes_num;
1350c7679306SThierry Reding 	unsigned int i;
13518f62142eSThierry Reding 	int err;
1352c7679306SThierry Reding 
135347307954SThierry Reding 	primary = tegra_primary_plane_create(drm, dc);
135447307954SThierry Reding 	if (IS_ERR(primary))
135547307954SThierry Reding 		return primary;
135647307954SThierry Reding 
13579f446d83SDmitry Osipenko 	if (dc->soc->supports_cursor)
13589f446d83SDmitry Osipenko 		planes_num = 2;
13599f446d83SDmitry Osipenko 	else
13609f446d83SDmitry Osipenko 		planes_num = 1;
13619f446d83SDmitry Osipenko 
13629f446d83SDmitry Osipenko 	for (i = 0; i < planes_num; i++) {
13639f446d83SDmitry Osipenko 		planes[i] = tegra_dc_overlay_plane_create(drm, dc, 1 + i,
13649f446d83SDmitry Osipenko 							  false);
13658f62142eSThierry Reding 		if (IS_ERR(planes[i])) {
13668f62142eSThierry Reding 			err = PTR_ERR(planes[i]);
13678f62142eSThierry Reding 
13688f62142eSThierry Reding 			while (i--)
136940dc962dSThierry Reding 				planes[i]->funcs->destroy(planes[i]);
13708f62142eSThierry Reding 
137140dc962dSThierry Reding 			primary->funcs->destroy(primary);
13728f62142eSThierry Reding 			return ERR_PTR(err);
137347307954SThierry Reding 		}
1374f002abc1SThierry Reding 	}
1375dee8268fSThierry Reding 
137647307954SThierry Reding 	return primary;
1377dee8268fSThierry Reding }
1378dee8268fSThierry Reding 
tegra_dc_destroy(struct drm_crtc * crtc)1379f002abc1SThierry Reding static void tegra_dc_destroy(struct drm_crtc *crtc)
1380f002abc1SThierry Reding {
1381f002abc1SThierry Reding 	drm_crtc_cleanup(crtc);
1382f002abc1SThierry Reding }
1383f002abc1SThierry Reding 
tegra_crtc_reset(struct drm_crtc * crtc)1384ca915b10SThierry Reding static void tegra_crtc_reset(struct drm_crtc *crtc)
1385ca915b10SThierry Reding {
1386b7e0b04aSMaarten Lankhorst 	struct tegra_dc_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
1387ca915b10SThierry Reding 
13883b59b7acSThierry Reding 	if (crtc->state)
1389b7e0b04aSMaarten Lankhorst 		tegra_crtc_atomic_destroy_state(crtc, crtc->state);
13903b59b7acSThierry Reding 
1391b7e0b04aSMaarten Lankhorst 	__drm_atomic_helper_crtc_reset(crtc, &state->base);
1392ca915b10SThierry Reding }
1393ca915b10SThierry Reding 
1394ca915b10SThierry Reding static struct drm_crtc_state *
tegra_crtc_atomic_duplicate_state(struct drm_crtc * crtc)1395ca915b10SThierry Reding tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
1396ca915b10SThierry Reding {
1397ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc->state);
1398ca915b10SThierry Reding 	struct tegra_dc_state *copy;
1399ca915b10SThierry Reding 
14003b59b7acSThierry Reding 	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
1401ca915b10SThierry Reding 	if (!copy)
1402ca915b10SThierry Reding 		return NULL;
1403ca915b10SThierry Reding 
14043b59b7acSThierry Reding 	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
14053b59b7acSThierry Reding 	copy->clk = state->clk;
14063b59b7acSThierry Reding 	copy->pclk = state->pclk;
14073b59b7acSThierry Reding 	copy->div = state->div;
14083b59b7acSThierry Reding 	copy->planes = state->planes;
1409ca915b10SThierry Reding 
1410ca915b10SThierry Reding 	return &copy->base;
1411ca915b10SThierry Reding }
1412ca915b10SThierry Reding 
tegra_crtc_atomic_destroy_state(struct drm_crtc * crtc,struct drm_crtc_state * state)1413ca915b10SThierry Reding static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
1414ca915b10SThierry Reding 					    struct drm_crtc_state *state)
1415ca915b10SThierry Reding {
1416ec2dc6a0SDaniel Vetter 	__drm_atomic_helper_crtc_destroy_state(state);
1417ca915b10SThierry Reding 	kfree(state);
1418ca915b10SThierry Reding }
1419ca915b10SThierry Reding 
1420b95800eeSThierry Reding #define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
1421b95800eeSThierry Reding 
1422b95800eeSThierry Reding static const struct debugfs_reg32 tegra_dc_regs[] = {
1423b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT),
1424b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL),
1425b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR),
1426b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT),
1427b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL),
1428b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR),
1429b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT),
1430b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL),
1431b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR),
1432b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT),
1433b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL),
1434b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR),
1435b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC),
1436b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0),
1437b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND),
1438b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE),
1439b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL),
1440b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_STATUS),
1441b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_MASK),
1442b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_ENABLE),
1443b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_TYPE),
1444b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_POLARITY),
1445b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1),
1446b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2),
1447b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3),
1448b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_ACCESS),
1449b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_CONTROL),
1450b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER),
1451b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL),
1452b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CONTROL),
1453b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM),
1454b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)),
1455b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)),
1456b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)),
1457b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)),
1458b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)),
1459b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)),
1460b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)),
1461b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)),
1462b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)),
1463b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)),
1464b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)),
1465b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)),
1466b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)),
1467b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)),
1468b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)),
1469b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)),
1470b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)),
1471b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)),
1472b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)),
1473b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)),
1474b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)),
1475b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)),
1476b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)),
1477b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
1478b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
1479b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
1480b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
1481b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
1482b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
1483b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
1484b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_CONTROL),
1485b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
1486b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
1487b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD),
1488b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_CS_DC),
1489b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A),
1490b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B),
1491b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_CTRL),
1492b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER),
1493b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED),
1494b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0),
1495b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1),
1496b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS),
1497b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY),
1498b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER),
1499b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS),
1500b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_REF_TO_SYNC),
1501b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SYNC_WIDTH),
1502b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BACK_PORCH),
1503b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_ACTIVE),
1504b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_FRONT_PORCH),
1505b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL),
1506b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A),
1507b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B),
1508b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C),
1509b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D),
1510b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL),
1511b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A),
1512b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B),
1513b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C),
1514b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D),
1515b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL),
1516b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A),
1517b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B),
1518b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C),
1519b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D),
1520b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL),
1521b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A),
1522b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B),
1523b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C),
1524b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL),
1525b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A),
1526b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B),
1527b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C),
1528b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL),
1529b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A),
1530b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL),
1531b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A),
1532b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M0_CONTROL),
1533b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M1_CONTROL),
1534b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DI_CONTROL),
1535b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_CONTROL),
1536b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_A),
1537b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_B),
1538b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_C),
1539b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_D),
1540b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL),
1541b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL),
1542b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL),
1543b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS),
1544b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS),
1545b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS),
1546b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS),
1547b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BORDER_COLOR),
1548b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER),
1549b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER),
1550b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER),
1551b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER),
1552b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND),
1553b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND),
1554b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR),
1555b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS),
1556b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION),
1557b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS),
1558b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL),
1559b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A),
1560b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B),
1561b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C),
1562b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D),
1563b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL),
1564b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST),
1565b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST),
1566b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST),
1567b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST),
1568b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL),
1569b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL),
1570b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CONTROL),
1571b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF),
1572b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(0)),
1573b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(1)),
1574b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(2)),
1575b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(3)),
1576b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(4)),
1577b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(5)),
1578b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(6)),
1579b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(7)),
1580b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(8)),
1581b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL),
1582b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT),
1583b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)),
1584b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)),
1585b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)),
1586b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)),
1587b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)),
1588b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)),
1589b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)),
1590b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)),
1591b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)),
1592b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)),
1593b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)),
1594b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)),
1595b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL),
1596b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES),
1597b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES),
1598b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI),
1599b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL),
1600b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_WIN_OPTIONS),
1601b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BYTE_SWAP),
1602b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL),
1603b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_COLOR_DEPTH),
1604b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_POSITION),
1605b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_SIZE),
1606b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE),
1607b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA),
1608b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA),
1609b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DDA_INC),
1610b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_LINE_STRIDE),
1611b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUF_STRIDE),
1612b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE),
1613b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE),
1614b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DV_CONTROL),
1615b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_NOKEY),
1616b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_1WIN),
1617b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X),
1618b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y),
1619b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY),
1620b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL),
1621b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR),
1622b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS),
1623b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U),
1624b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS),
1625b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V),
1626b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS),
1627b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET),
1628b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS),
1629b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET),
1630b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS),
1631b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS),
1632b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS),
1633b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS),
1634b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS),
1635b95800eeSThierry Reding };
1636b95800eeSThierry Reding 
tegra_dc_show_regs(struct seq_file * s,void * data)1637b95800eeSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data)
1638b95800eeSThierry Reding {
1639b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1640b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1641b95800eeSThierry Reding 	unsigned int i;
1642b95800eeSThierry Reding 	int err = 0;
1643b95800eeSThierry Reding 
1644b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1645b95800eeSThierry Reding 
1646b95800eeSThierry Reding 	if (!dc->base.state->active) {
1647b95800eeSThierry Reding 		err = -EBUSY;
1648b95800eeSThierry Reding 		goto unlock;
1649b95800eeSThierry Reding 	}
1650b95800eeSThierry Reding 
1651b95800eeSThierry Reding 	for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) {
1652b95800eeSThierry Reding 		unsigned int offset = tegra_dc_regs[i].offset;
1653b95800eeSThierry Reding 
1654b95800eeSThierry Reding 		seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name,
1655b95800eeSThierry Reding 			   offset, tegra_dc_readl(dc, offset));
1656b95800eeSThierry Reding 	}
1657b95800eeSThierry Reding 
1658b95800eeSThierry Reding unlock:
1659b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1660b95800eeSThierry Reding 	return err;
1661b95800eeSThierry Reding }
1662b95800eeSThierry Reding 
tegra_dc_show_crc(struct seq_file * s,void * data)1663b95800eeSThierry Reding static int tegra_dc_show_crc(struct seq_file *s, void *data)
1664b95800eeSThierry Reding {
1665b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1666b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1667b95800eeSThierry Reding 	int err = 0;
1668b95800eeSThierry Reding 	u32 value;
1669b95800eeSThierry Reding 
1670b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1671b95800eeSThierry Reding 
1672b95800eeSThierry Reding 	if (!dc->base.state->active) {
1673b95800eeSThierry Reding 		err = -EBUSY;
1674b95800eeSThierry Reding 		goto unlock;
1675b95800eeSThierry Reding 	}
1676b95800eeSThierry Reding 
1677b95800eeSThierry Reding 	value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
1678b95800eeSThierry Reding 	tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
1679b95800eeSThierry Reding 	tegra_dc_commit(dc);
1680b95800eeSThierry Reding 
1681b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1682b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1683b95800eeSThierry Reding 
1684b95800eeSThierry Reding 	value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM);
1685b95800eeSThierry Reding 	seq_printf(s, "%08x\n", value);
1686b95800eeSThierry Reding 
1687b95800eeSThierry Reding 	tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
1688b95800eeSThierry Reding 
1689b95800eeSThierry Reding unlock:
1690b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1691b95800eeSThierry Reding 	return err;
1692b95800eeSThierry Reding }
1693b95800eeSThierry Reding 
tegra_dc_show_stats(struct seq_file * s,void * data)1694b95800eeSThierry Reding static int tegra_dc_show_stats(struct seq_file *s, void *data)
1695b95800eeSThierry Reding {
1696b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1697b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1698b95800eeSThierry Reding 
1699b95800eeSThierry Reding 	seq_printf(s, "frames: %lu\n", dc->stats.frames);
1700b95800eeSThierry Reding 	seq_printf(s, "vblank: %lu\n", dc->stats.vblank);
1701b95800eeSThierry Reding 	seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
1702b95800eeSThierry Reding 	seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
1703b95800eeSThierry Reding 
1704ad85b084SDmitry Osipenko 	seq_printf(s, "frames total: %lu\n", dc->stats.frames_total);
1705ad85b084SDmitry Osipenko 	seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total);
1706ad85b084SDmitry Osipenko 	seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total);
1707ad85b084SDmitry Osipenko 	seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total);
1708ad85b084SDmitry Osipenko 
1709b95800eeSThierry Reding 	return 0;
1710b95800eeSThierry Reding }
1711b95800eeSThierry Reding 
1712b95800eeSThierry Reding static struct drm_info_list debugfs_files[] = {
1713b95800eeSThierry Reding 	{ "regs", tegra_dc_show_regs, 0, NULL },
1714b95800eeSThierry Reding 	{ "crc", tegra_dc_show_crc, 0, NULL },
1715b95800eeSThierry Reding 	{ "stats", tegra_dc_show_stats, 0, NULL },
1716b95800eeSThierry Reding };
1717b95800eeSThierry Reding 
tegra_dc_late_register(struct drm_crtc * crtc)1718b95800eeSThierry Reding static int tegra_dc_late_register(struct drm_crtc *crtc)
1719b95800eeSThierry Reding {
1720b95800eeSThierry Reding 	unsigned int i, count = ARRAY_SIZE(debugfs_files);
1721b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
172239f55c61SArnd Bergmann 	struct dentry *root;
1723b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1724b95800eeSThierry Reding 
172539f55c61SArnd Bergmann #ifdef CONFIG_DEBUG_FS
172639f55c61SArnd Bergmann 	root = crtc->debugfs_entry;
172739f55c61SArnd Bergmann #else
172839f55c61SArnd Bergmann 	root = NULL;
172939f55c61SArnd Bergmann #endif
173039f55c61SArnd Bergmann 
1731b95800eeSThierry Reding 	dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
1732b95800eeSThierry Reding 				    GFP_KERNEL);
1733b95800eeSThierry Reding 	if (!dc->debugfs_files)
1734b95800eeSThierry Reding 		return -ENOMEM;
1735b95800eeSThierry Reding 
1736b95800eeSThierry Reding 	for (i = 0; i < count; i++)
1737b95800eeSThierry Reding 		dc->debugfs_files[i].data = dc;
1738b95800eeSThierry Reding 
1739ad6d94f2SWambui Karuga 	drm_debugfs_create_files(dc->debugfs_files, count, root, minor);
1740b95800eeSThierry Reding 
1741b95800eeSThierry Reding 	return 0;
1742b95800eeSThierry Reding }
1743b95800eeSThierry Reding 
tegra_dc_early_unregister(struct drm_crtc * crtc)1744b95800eeSThierry Reding static void tegra_dc_early_unregister(struct drm_crtc *crtc)
1745b95800eeSThierry Reding {
1746b95800eeSThierry Reding 	unsigned int count = ARRAY_SIZE(debugfs_files);
1747b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
1748b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1749b95800eeSThierry Reding 
1750b95800eeSThierry Reding 	drm_debugfs_remove_files(dc->debugfs_files, count, minor);
1751b95800eeSThierry Reding 	kfree(dc->debugfs_files);
1752b95800eeSThierry Reding 	dc->debugfs_files = NULL;
1753b95800eeSThierry Reding }
1754b95800eeSThierry Reding 
tegra_dc_get_vblank_counter(struct drm_crtc * crtc)1755c49c81e2SThierry Reding static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
1756c49c81e2SThierry Reding {
1757c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1758c49c81e2SThierry Reding 
175947307954SThierry Reding 	/* XXX vblank syncpoints don't work with nvdisplay yet */
176047307954SThierry Reding 	if (dc->syncpt && !dc->soc->has_nvdisplay)
1761c49c81e2SThierry Reding 		return host1x_syncpt_read(dc->syncpt);
1762c49c81e2SThierry Reding 
1763c49c81e2SThierry Reding 	/* fallback to software emulated VBLANK counter */
17643abe2413SDhinakaran Pandiyan 	return (u32)drm_crtc_vblank_count(&dc->base);
1765c49c81e2SThierry Reding }
1766c49c81e2SThierry Reding 
tegra_dc_enable_vblank(struct drm_crtc * crtc)1767c49c81e2SThierry Reding static int tegra_dc_enable_vblank(struct drm_crtc *crtc)
1768c49c81e2SThierry Reding {
1769c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1770363541e8SThierry Reding 	u32 value;
1771c49c81e2SThierry Reding 
1772c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1773c49c81e2SThierry Reding 	value |= VBLANK_INT;
1774c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1775c49c81e2SThierry Reding 
1776c49c81e2SThierry Reding 	return 0;
1777c49c81e2SThierry Reding }
1778c49c81e2SThierry Reding 
tegra_dc_disable_vblank(struct drm_crtc * crtc)1779c49c81e2SThierry Reding static void tegra_dc_disable_vblank(struct drm_crtc *crtc)
1780c49c81e2SThierry Reding {
1781c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1782363541e8SThierry Reding 	u32 value;
1783c49c81e2SThierry Reding 
1784c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1785c49c81e2SThierry Reding 	value &= ~VBLANK_INT;
1786c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1787c49c81e2SThierry Reding }
1788c49c81e2SThierry Reding 
1789dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = {
17901503ca47SThierry Reding 	.page_flip = drm_atomic_helper_page_flip,
179174f48791SThierry Reding 	.set_config = drm_atomic_helper_set_config,
1792f002abc1SThierry Reding 	.destroy = tegra_dc_destroy,
1793ca915b10SThierry Reding 	.reset = tegra_crtc_reset,
1794ca915b10SThierry Reding 	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
1795ca915b10SThierry Reding 	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
1796b95800eeSThierry Reding 	.late_register = tegra_dc_late_register,
1797b95800eeSThierry Reding 	.early_unregister = tegra_dc_early_unregister,
179810437d9bSShawn Guo 	.get_vblank_counter = tegra_dc_get_vblank_counter,
179910437d9bSShawn Guo 	.enable_vblank = tegra_dc_enable_vblank,
180010437d9bSShawn Guo 	.disable_vblank = tegra_dc_disable_vblank,
1801dee8268fSThierry Reding };
1802dee8268fSThierry Reding 
tegra_dc_set_timings(struct tegra_dc * dc,struct drm_display_mode * mode)1803dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc,
1804dee8268fSThierry Reding 				struct drm_display_mode *mode)
1805dee8268fSThierry Reding {
18060444c0ffSThierry Reding 	unsigned int h_ref_to_sync = 1;
18070444c0ffSThierry Reding 	unsigned int v_ref_to_sync = 1;
1808dee8268fSThierry Reding 	unsigned long value;
1809dee8268fSThierry Reding 
181047307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
1811dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
1812dee8268fSThierry Reding 
1813dee8268fSThierry Reding 		value = (v_ref_to_sync << 16) | h_ref_to_sync;
1814dee8268fSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
181547307954SThierry Reding 	}
1816dee8268fSThierry Reding 
1817dee8268fSThierry Reding 	value = ((mode->vsync_end - mode->vsync_start) << 16) |
1818dee8268fSThierry Reding 		((mode->hsync_end - mode->hsync_start) <<  0);
1819dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
1820dee8268fSThierry Reding 
1821dee8268fSThierry Reding 	value = ((mode->vtotal - mode->vsync_end) << 16) |
1822dee8268fSThierry Reding 		((mode->htotal - mode->hsync_end) <<  0);
1823dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
1824dee8268fSThierry Reding 
1825dee8268fSThierry Reding 	value = ((mode->vsync_start - mode->vdisplay) << 16) |
1826dee8268fSThierry Reding 		((mode->hsync_start - mode->hdisplay) <<  0);
1827dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
1828dee8268fSThierry Reding 
1829dee8268fSThierry Reding 	value = (mode->vdisplay << 16) | mode->hdisplay;
1830dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
1831dee8268fSThierry Reding 
1832dee8268fSThierry Reding 	return 0;
1833dee8268fSThierry Reding }
1834dee8268fSThierry Reding 
18359d910b60SThierry Reding /**
18369d910b60SThierry Reding  * tegra_dc_state_setup_clock - check clock settings and store them in atomic
18379d910b60SThierry Reding  *     state
18389d910b60SThierry Reding  * @dc: display controller
18399d910b60SThierry Reding  * @crtc_state: CRTC atomic state
18409d910b60SThierry Reding  * @clk: parent clock for display controller
18419d910b60SThierry Reding  * @pclk: pixel clock
18429d910b60SThierry Reding  * @div: shift clock divider
18439d910b60SThierry Reding  *
18449d910b60SThierry Reding  * Returns:
18459d910b60SThierry Reding  * 0 on success or a negative error-code on failure.
18469d910b60SThierry Reding  */
tegra_dc_state_setup_clock(struct tegra_dc * dc,struct drm_crtc_state * crtc_state,struct clk * clk,unsigned long pclk,unsigned int div)1847ca915b10SThierry Reding int tegra_dc_state_setup_clock(struct tegra_dc *dc,
1848ca915b10SThierry Reding 			       struct drm_crtc_state *crtc_state,
1849ca915b10SThierry Reding 			       struct clk *clk, unsigned long pclk,
1850ca915b10SThierry Reding 			       unsigned int div)
1851ca915b10SThierry Reding {
1852ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc_state);
1853ca915b10SThierry Reding 
1854d2982748SThierry Reding 	if (!clk_has_parent(dc->clk, clk))
1855d2982748SThierry Reding 		return -EINVAL;
1856d2982748SThierry Reding 
1857ca915b10SThierry Reding 	state->clk = clk;
1858ca915b10SThierry Reding 	state->pclk = pclk;
1859ca915b10SThierry Reding 	state->div = div;
1860ca915b10SThierry Reding 
1861ca915b10SThierry Reding 	return 0;
1862ca915b10SThierry Reding }
1863ca915b10SThierry Reding 
tegra_dc_update_voltage_state(struct tegra_dc * dc,struct tegra_dc_state * state)18644ce3048cSDmitry Osipenko static void tegra_dc_update_voltage_state(struct tegra_dc *dc,
18654ce3048cSDmitry Osipenko 					  struct tegra_dc_state *state)
18664ce3048cSDmitry Osipenko {
18674ce3048cSDmitry Osipenko 	unsigned long rate, pstate;
18684ce3048cSDmitry Osipenko 	struct dev_pm_opp *opp;
18694ce3048cSDmitry Osipenko 	int err;
18704ce3048cSDmitry Osipenko 
18714ce3048cSDmitry Osipenko 	if (!dc->has_opp_table)
18724ce3048cSDmitry Osipenko 		return;
18734ce3048cSDmitry Osipenko 
18744ce3048cSDmitry Osipenko 	/* calculate actual pixel clock rate which depends on internal divider */
18754ce3048cSDmitry Osipenko 	rate = DIV_ROUND_UP(clk_get_rate(dc->clk) * 2, state->div + 2);
18764ce3048cSDmitry Osipenko 
18774ce3048cSDmitry Osipenko 	/* find suitable OPP for the rate */
18784ce3048cSDmitry Osipenko 	opp = dev_pm_opp_find_freq_ceil(dc->dev, &rate);
18794ce3048cSDmitry Osipenko 
18804ce3048cSDmitry Osipenko 	/*
18814ce3048cSDmitry Osipenko 	 * Very high resolution modes may results in a clock rate that is
18824ce3048cSDmitry Osipenko 	 * above the characterized maximum. In this case it's okay to fall
18834ce3048cSDmitry Osipenko 	 * back to the characterized maximum.
18844ce3048cSDmitry Osipenko 	 */
18854ce3048cSDmitry Osipenko 	if (opp == ERR_PTR(-ERANGE))
18864ce3048cSDmitry Osipenko 		opp = dev_pm_opp_find_freq_floor(dc->dev, &rate);
18874ce3048cSDmitry Osipenko 
18884ce3048cSDmitry Osipenko 	if (IS_ERR(opp)) {
18894ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to find OPP for %luHz: %pe\n",
18904ce3048cSDmitry Osipenko 			rate, opp);
18914ce3048cSDmitry Osipenko 		return;
18924ce3048cSDmitry Osipenko 	}
18934ce3048cSDmitry Osipenko 
18944ce3048cSDmitry Osipenko 	pstate = dev_pm_opp_get_required_pstate(opp, 0);
18954ce3048cSDmitry Osipenko 	dev_pm_opp_put(opp);
18964ce3048cSDmitry Osipenko 
18974ce3048cSDmitry Osipenko 	/*
18984ce3048cSDmitry Osipenko 	 * The minimum core voltage depends on the pixel clock rate (which
18994ce3048cSDmitry Osipenko 	 * depends on internal clock divider of the CRTC) and not on the
19004ce3048cSDmitry Osipenko 	 * rate of the display controller clock. This is why we're not using
19014ce3048cSDmitry Osipenko 	 * dev_pm_opp_set_rate() API and instead controlling the power domain
19024ce3048cSDmitry Osipenko 	 * directly.
19034ce3048cSDmitry Osipenko 	 */
19044ce3048cSDmitry Osipenko 	err = dev_pm_genpd_set_performance_state(dc->dev, pstate);
19054ce3048cSDmitry Osipenko 	if (err)
19064ce3048cSDmitry Osipenko 		dev_err(dc->dev, "failed to set power domain state to %lu: %d\n",
19074ce3048cSDmitry Osipenko 			pstate, err);
19084ce3048cSDmitry Osipenko }
19094ce3048cSDmitry Osipenko 
tegra_dc_set_clock_rate(struct tegra_dc * dc,struct tegra_dc_state * state)19100c921b6dSDmitry Osipenko static void tegra_dc_set_clock_rate(struct tegra_dc *dc,
191176d59ed0SThierry Reding 				    struct tegra_dc_state *state)
191276d59ed0SThierry Reding {
191376d59ed0SThierry Reding 	int err;
191476d59ed0SThierry Reding 
191576d59ed0SThierry Reding 	err = clk_set_parent(dc->clk, state->clk);
191676d59ed0SThierry Reding 	if (err < 0)
191776d59ed0SThierry Reding 		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
191876d59ed0SThierry Reding 
191976d59ed0SThierry Reding 	/*
192076d59ed0SThierry Reding 	 * Outputs may not want to change the parent clock rate. This is only
192176d59ed0SThierry Reding 	 * relevant to Tegra20 where only a single display PLL is available.
192276d59ed0SThierry Reding 	 * Since that PLL would typically be used for HDMI, an internal LVDS
192376d59ed0SThierry Reding 	 * panel would need to be driven by some other clock such as PLL_P
192476d59ed0SThierry Reding 	 * which is shared with other peripherals. Changing the clock rate
192576d59ed0SThierry Reding 	 * should therefore be avoided.
192676d59ed0SThierry Reding 	 */
192776d59ed0SThierry Reding 	if (state->pclk > 0) {
192876d59ed0SThierry Reding 		err = clk_set_rate(state->clk, state->pclk);
192976d59ed0SThierry Reding 		if (err < 0)
193076d59ed0SThierry Reding 			dev_err(dc->dev,
193176d59ed0SThierry Reding 				"failed to set clock rate to %lu Hz\n",
193276d59ed0SThierry Reding 				state->pclk);
1933f8fb97c9SDmitry Osipenko 
1934f8fb97c9SDmitry Osipenko 		err = clk_set_rate(dc->clk, state->pclk);
1935f8fb97c9SDmitry Osipenko 		if (err < 0)
1936f8fb97c9SDmitry Osipenko 			dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n",
1937f8fb97c9SDmitry Osipenko 				dc->clk, state->pclk, err);
193876d59ed0SThierry Reding 	}
193976d59ed0SThierry Reding 
194076d59ed0SThierry Reding 	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
194176d59ed0SThierry Reding 		      state->div);
194276d59ed0SThierry Reding 	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
19434ce3048cSDmitry Osipenko 
19444ce3048cSDmitry Osipenko 	tegra_dc_update_voltage_state(dc, state);
194576d59ed0SThierry Reding }
194676d59ed0SThierry Reding 
tegra_dc_stop(struct tegra_dc * dc)1947003fc848SThierry Reding static void tegra_dc_stop(struct tegra_dc *dc)
1948003fc848SThierry Reding {
1949003fc848SThierry Reding 	u32 value;
1950003fc848SThierry Reding 
1951003fc848SThierry Reding 	/* stop the display controller */
1952003fc848SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
1953003fc848SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
1954003fc848SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
1955003fc848SThierry Reding 
1956003fc848SThierry Reding 	tegra_dc_commit(dc);
1957003fc848SThierry Reding }
1958003fc848SThierry Reding 
tegra_dc_idle(struct tegra_dc * dc)1959003fc848SThierry Reding static bool tegra_dc_idle(struct tegra_dc *dc)
1960003fc848SThierry Reding {
1961003fc848SThierry Reding 	u32 value;
1962003fc848SThierry Reding 
1963003fc848SThierry Reding 	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
1964003fc848SThierry Reding 
1965003fc848SThierry Reding 	return (value & DISP_CTRL_MODE_MASK) == 0;
1966003fc848SThierry Reding }
1967003fc848SThierry Reding 
tegra_dc_wait_idle(struct tegra_dc * dc,unsigned long timeout)1968003fc848SThierry Reding static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
1969003fc848SThierry Reding {
1970003fc848SThierry Reding 	timeout = jiffies + msecs_to_jiffies(timeout);
1971003fc848SThierry Reding 
1972003fc848SThierry Reding 	while (time_before(jiffies, timeout)) {
1973003fc848SThierry Reding 		if (tegra_dc_idle(dc))
1974003fc848SThierry Reding 			return 0;
1975003fc848SThierry Reding 
1976003fc848SThierry Reding 		usleep_range(1000, 2000);
1977003fc848SThierry Reding 	}
1978003fc848SThierry Reding 
1979003fc848SThierry Reding 	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
1980003fc848SThierry Reding 	return -ETIMEDOUT;
1981003fc848SThierry Reding }
1982003fc848SThierry Reding 
198304d5d5dfSDmitry Osipenko static void
tegra_crtc_update_memory_bandwidth(struct drm_crtc * crtc,struct drm_atomic_state * state,bool prepare_bandwidth_transition)198404d5d5dfSDmitry Osipenko tegra_crtc_update_memory_bandwidth(struct drm_crtc *crtc,
198504d5d5dfSDmitry Osipenko 				   struct drm_atomic_state *state,
198604d5d5dfSDmitry Osipenko 				   bool prepare_bandwidth_transition)
198704d5d5dfSDmitry Osipenko {
198804d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *old_tegra_state, *new_tegra_state;
198904d5d5dfSDmitry Osipenko 	u32 i, new_avg_bw, old_avg_bw, new_peak_bw, old_peak_bw;
199004d5d5dfSDmitry Osipenko 	const struct drm_plane_state *old_plane_state;
199104d5d5dfSDmitry Osipenko 	const struct drm_crtc_state *old_crtc_state;
199204d5d5dfSDmitry Osipenko 	struct tegra_dc_window window, old_window;
199304d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
199404d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
199504d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
199604d5d5dfSDmitry Osipenko 
199704d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
199804d5d5dfSDmitry Osipenko 		return;
199904d5d5dfSDmitry Osipenko 
200004d5d5dfSDmitry Osipenko 	old_crtc_state = drm_atomic_get_old_crtc_state(state, crtc);
200104d5d5dfSDmitry Osipenko 
200204d5d5dfSDmitry Osipenko 	if (!crtc->state->active) {
200304d5d5dfSDmitry Osipenko 		if (!old_crtc_state->active)
200404d5d5dfSDmitry Osipenko 			return;
200504d5d5dfSDmitry Osipenko 
200604d5d5dfSDmitry Osipenko 		/*
200704d5d5dfSDmitry Osipenko 		 * When CRTC is disabled on DPMS, the state of attached planes
200804d5d5dfSDmitry Osipenko 		 * is kept unchanged. Hence we need to enforce removal of the
200904d5d5dfSDmitry Osipenko 		 * bandwidths from the ICC paths.
201004d5d5dfSDmitry Osipenko 		 */
201104d5d5dfSDmitry Osipenko 		drm_atomic_crtc_for_each_plane(plane, crtc) {
201204d5d5dfSDmitry Osipenko 			tegra = to_tegra_plane(plane);
201304d5d5dfSDmitry Osipenko 
201404d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem, 0, 0);
201504d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
201604d5d5dfSDmitry Osipenko 		}
201704d5d5dfSDmitry Osipenko 
201804d5d5dfSDmitry Osipenko 		return;
201904d5d5dfSDmitry Osipenko 	}
202004d5d5dfSDmitry Osipenko 
202104d5d5dfSDmitry Osipenko 	for_each_old_plane_in_state(old_crtc_state->state, plane,
202204d5d5dfSDmitry Osipenko 				    old_plane_state, i) {
202304d5d5dfSDmitry Osipenko 		old_tegra_state = to_const_tegra_plane_state(old_plane_state);
202404d5d5dfSDmitry Osipenko 		new_tegra_state = to_const_tegra_plane_state(plane->state);
202504d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(plane);
202604d5d5dfSDmitry Osipenko 
202704d5d5dfSDmitry Osipenko 		/*
202804d5d5dfSDmitry Osipenko 		 * We're iterating over the global atomic state and it contains
202904d5d5dfSDmitry Osipenko 		 * planes from another CRTC, hence we need to filter out the
203004d5d5dfSDmitry Osipenko 		 * planes unrelated to this CRTC.
203104d5d5dfSDmitry Osipenko 		 */
203204d5d5dfSDmitry Osipenko 		if (tegra->dc != dc)
203304d5d5dfSDmitry Osipenko 			continue;
203404d5d5dfSDmitry Osipenko 
203504d5d5dfSDmitry Osipenko 		new_avg_bw = new_tegra_state->avg_memory_bandwidth;
203604d5d5dfSDmitry Osipenko 		old_avg_bw = old_tegra_state->avg_memory_bandwidth;
203704d5d5dfSDmitry Osipenko 
203804d5d5dfSDmitry Osipenko 		new_peak_bw = new_tegra_state->total_peak_memory_bandwidth;
203904d5d5dfSDmitry Osipenko 		old_peak_bw = old_tegra_state->total_peak_memory_bandwidth;
204004d5d5dfSDmitry Osipenko 
204104d5d5dfSDmitry Osipenko 		/*
204204d5d5dfSDmitry Osipenko 		 * See the comment related to !crtc->state->active above,
204304d5d5dfSDmitry Osipenko 		 * which explains why bandwidths need to be updated when
204404d5d5dfSDmitry Osipenko 		 * CRTC is turning ON.
204504d5d5dfSDmitry Osipenko 		 */
204604d5d5dfSDmitry Osipenko 		if (new_avg_bw == old_avg_bw && new_peak_bw == old_peak_bw &&
204704d5d5dfSDmitry Osipenko 		    old_crtc_state->active)
204804d5d5dfSDmitry Osipenko 			continue;
204904d5d5dfSDmitry Osipenko 
205004d5d5dfSDmitry Osipenko 		window.src.h = drm_rect_height(&plane->state->src) >> 16;
205104d5d5dfSDmitry Osipenko 		window.dst.h = drm_rect_height(&plane->state->dst);
205204d5d5dfSDmitry Osipenko 
205304d5d5dfSDmitry Osipenko 		old_window.src.h = drm_rect_height(&old_plane_state->src) >> 16;
205404d5d5dfSDmitry Osipenko 		old_window.dst.h = drm_rect_height(&old_plane_state->dst);
205504d5d5dfSDmitry Osipenko 
205604d5d5dfSDmitry Osipenko 		/*
205704d5d5dfSDmitry Osipenko 		 * During the preparation phase (atomic_begin), the memory
205804d5d5dfSDmitry Osipenko 		 * freq should go high before the DC changes are committed
205904d5d5dfSDmitry Osipenko 		 * if bandwidth requirement goes up, otherwise memory freq
206004d5d5dfSDmitry Osipenko 		 * should to stay high if BW requirement goes down.  The
206104d5d5dfSDmitry Osipenko 		 * opposite applies to the completion phase (post_commit).
206204d5d5dfSDmitry Osipenko 		 */
206304d5d5dfSDmitry Osipenko 		if (prepare_bandwidth_transition) {
206404d5d5dfSDmitry Osipenko 			new_avg_bw = max(old_avg_bw, new_avg_bw);
206504d5d5dfSDmitry Osipenko 			new_peak_bw = max(old_peak_bw, new_peak_bw);
206604d5d5dfSDmitry Osipenko 
206704d5d5dfSDmitry Osipenko 			if (tegra_plane_use_vertical_filtering(tegra, &old_window))
206804d5d5dfSDmitry Osipenko 				window = old_window;
206904d5d5dfSDmitry Osipenko 		}
207004d5d5dfSDmitry Osipenko 
207104d5d5dfSDmitry Osipenko 		icc_set_bw(tegra->icc_mem, new_avg_bw, new_peak_bw);
207204d5d5dfSDmitry Osipenko 
207304d5d5dfSDmitry Osipenko 		if (tegra_plane_use_vertical_filtering(tegra, &window))
207404d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, new_avg_bw, new_peak_bw);
207504d5d5dfSDmitry Osipenko 		else
207604d5d5dfSDmitry Osipenko 			icc_set_bw(tegra->icc_mem_vfilter, 0, 0);
207704d5d5dfSDmitry Osipenko 	}
207804d5d5dfSDmitry Osipenko }
207904d5d5dfSDmitry Osipenko 
tegra_crtc_atomic_disable(struct drm_crtc * crtc,struct drm_atomic_state * state)208064581714SLaurent Pinchart static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
2081351f950dSMaxime Ripard 				      struct drm_atomic_state *state)
2082003fc848SThierry Reding {
2083003fc848SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2084003fc848SThierry Reding 	u32 value;
2085fd67e9c6SThierry Reding 	int err;
2086003fc848SThierry Reding 
2087003fc848SThierry Reding 	if (!tegra_dc_idle(dc)) {
2088003fc848SThierry Reding 		tegra_dc_stop(dc);
2089003fc848SThierry Reding 
2090003fc848SThierry Reding 		/*
2091003fc848SThierry Reding 		 * Ignore the return value, there isn't anything useful to do
2092003fc848SThierry Reding 		 * in case this fails.
2093003fc848SThierry Reding 		 */
2094003fc848SThierry Reding 		tegra_dc_wait_idle(dc, 100);
2095003fc848SThierry Reding 	}
2096003fc848SThierry Reding 
2097003fc848SThierry Reding 	/*
2098003fc848SThierry Reding 	 * This should really be part of the RGB encoder driver, but clearing
2099003fc848SThierry Reding 	 * these bits has the side-effect of stopping the display controller.
2100003fc848SThierry Reding 	 * When that happens no VBLANK interrupts will be raised. At the same
2101003fc848SThierry Reding 	 * time the encoder is disabled before the display controller, so the
2102003fc848SThierry Reding 	 * above code is always going to timeout waiting for the controller
2103003fc848SThierry Reding 	 * to go idle.
2104003fc848SThierry Reding 	 *
2105003fc848SThierry Reding 	 * Given the close coupling between the RGB encoder and the display
2106003fc848SThierry Reding 	 * controller doing it here is still kind of okay. None of the other
2107003fc848SThierry Reding 	 * encoder drivers require these bits to be cleared.
2108003fc848SThierry Reding 	 *
2109003fc848SThierry Reding 	 * XXX: Perhaps given that the display controller is switched off at
2110003fc848SThierry Reding 	 * this point anyway maybe clearing these bits isn't even useful for
2111003fc848SThierry Reding 	 * the RGB encoder?
2112003fc848SThierry Reding 	 */
2113003fc848SThierry Reding 	if (dc->rgb) {
2114003fc848SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2115003fc848SThierry Reding 		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2116003fc848SThierry Reding 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
2117003fc848SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
2118003fc848SThierry Reding 	}
2119003fc848SThierry Reding 
2120003fc848SThierry Reding 	tegra_dc_stats_reset(&dc->stats);
2121003fc848SThierry Reding 	drm_crtc_vblank_off(crtc);
212233a8eb8dSThierry Reding 
21239d99ab6eSThierry Reding 	spin_lock_irq(&crtc->dev->event_lock);
21249d99ab6eSThierry Reding 
21259d99ab6eSThierry Reding 	if (crtc->state->event) {
21269d99ab6eSThierry Reding 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
21279d99ab6eSThierry Reding 		crtc->state->event = NULL;
21289d99ab6eSThierry Reding 	}
21299d99ab6eSThierry Reding 
21309d99ab6eSThierry Reding 	spin_unlock_irq(&crtc->dev->event_lock);
21319d99ab6eSThierry Reding 
2132fd67e9c6SThierry Reding 	err = host1x_client_suspend(&dc->client);
2133fd67e9c6SThierry Reding 	if (err < 0)
2134fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to suspend: %d\n", err);
21354ce3048cSDmitry Osipenko 
21364ce3048cSDmitry Osipenko 	if (dc->has_opp_table) {
21374ce3048cSDmitry Osipenko 		err = dev_pm_genpd_set_performance_state(dc->dev, 0);
21384ce3048cSDmitry Osipenko 		if (err)
21394ce3048cSDmitry Osipenko 			dev_err(dc->dev,
21404ce3048cSDmitry Osipenko 				"failed to clear power domain state: %d\n", err);
21414ce3048cSDmitry Osipenko 	}
2142003fc848SThierry Reding }
2143003fc848SThierry Reding 
tegra_crtc_atomic_enable(struct drm_crtc * crtc,struct drm_atomic_state * state)21440b20a0f8SLaurent Pinchart static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
2145351f950dSMaxime Ripard 				     struct drm_atomic_state *state)
2146dee8268fSThierry Reding {
21474aa3df71SThierry Reding 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
2148351f950dSMaxime Ripard 	struct tegra_dc_state *crtc_state = to_dc_state(crtc->state);
2149dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
2150dbb3f2f7SThierry Reding 	u32 value;
2151fd67e9c6SThierry Reding 	int err;
2152dee8268fSThierry Reding 
21530c921b6dSDmitry Osipenko 	/* apply PLL changes */
21540c921b6dSDmitry Osipenko 	tegra_dc_set_clock_rate(dc, crtc_state);
21550c921b6dSDmitry Osipenko 
2156fd67e9c6SThierry Reding 	err = host1x_client_resume(&dc->client);
2157fd67e9c6SThierry Reding 	if (err < 0) {
2158fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to resume: %d\n", err);
2159fd67e9c6SThierry Reding 		return;
2160fd67e9c6SThierry Reding 	}
216133a8eb8dSThierry Reding 
216233a8eb8dSThierry Reding 	/* initialize display controller */
216333a8eb8dSThierry Reding 	if (dc->syncpt) {
216447307954SThierry Reding 		u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
216547307954SThierry Reding 
216647307954SThierry Reding 		if (dc->soc->has_nvdisplay)
216747307954SThierry Reding 			enable = 1 << 31;
216847307954SThierry Reding 		else
216947307954SThierry Reding 			enable = 1 << 8;
217033a8eb8dSThierry Reding 
217133a8eb8dSThierry Reding 		value = SYNCPT_CNTRL_NO_STALL;
217233a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
217333a8eb8dSThierry Reding 
217447307954SThierry Reding 		value = enable | syncpt;
217533a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
217633a8eb8dSThierry Reding 	}
217733a8eb8dSThierry Reding 
217847307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
217947307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
218047307954SThierry Reding 			DSC_OBUF_UF_INT;
218147307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
218247307954SThierry Reding 
218347307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
218447307954SThierry Reding 			DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
218547307954SThierry Reding 			HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
218647307954SThierry Reding 			REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
218747307954SThierry Reding 			VBLANK_INT | FRAME_END_INT;
218847307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
218947307954SThierry Reding 
219047307954SThierry Reding 		value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
219147307954SThierry Reding 			FRAME_END_INT;
219247307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
219347307954SThierry Reding 
219447307954SThierry Reding 		value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
219547307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
219647307954SThierry Reding 
219747307954SThierry Reding 		tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
219847307954SThierry Reding 	} else {
219933a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
220033a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
220133a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
220233a8eb8dSThierry Reding 
220333a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
220433a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
220533a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
220633a8eb8dSThierry Reding 
220733a8eb8dSThierry Reding 		/* initialize timer */
220833a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
220933a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
221033a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
221133a8eb8dSThierry Reding 
221233a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
221333a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
221433a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
221533a8eb8dSThierry Reding 
221633a8eb8dSThierry Reding 		value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
221733a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
221833a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
221933a8eb8dSThierry Reding 
222033a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
222133a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
222233a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
222347307954SThierry Reding 	}
222433a8eb8dSThierry Reding 
22257116e9a8SThierry Reding 	if (dc->soc->supports_background_color)
22267116e9a8SThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
22277116e9a8SThierry Reding 	else
222833a8eb8dSThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
222933a8eb8dSThierry Reding 
22300c921b6dSDmitry Osipenko 	/* apply pixel clock changes */
22310c921b6dSDmitry Osipenko 	if (!dc->soc->has_nvdisplay) {
22320c921b6dSDmitry Osipenko 		value = SHIFT_CLK_DIVIDER(crtc_state->div) | PIXEL_CLK_DIVIDER_PCD1;
22330c921b6dSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
22340c921b6dSDmitry Osipenko 	}
223576d59ed0SThierry Reding 
2236dee8268fSThierry Reding 	/* program display mode */
2237dee8268fSThierry Reding 	tegra_dc_set_timings(dc, mode);
2238dee8268fSThierry Reding 
22398620fc62SThierry Reding 	/* interlacing isn't supported yet, so disable it */
22408620fc62SThierry Reding 	if (dc->soc->supports_interlacing) {
22418620fc62SThierry Reding 		value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
22428620fc62SThierry Reding 		value &= ~INTERLACE_ENABLE;
22438620fc62SThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
22448620fc62SThierry Reding 	}
2245666cb873SThierry Reding 
2246666cb873SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
2247666cb873SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
2248666cb873SThierry Reding 	value |= DISP_CTRL_MODE_C_DISPLAY;
2249666cb873SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
2250666cb873SThierry Reding 
225147307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
2252666cb873SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
2253666cb873SThierry Reding 		value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
2254666cb873SThierry Reding 			 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
2255666cb873SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
225647307954SThierry Reding 	}
225747307954SThierry Reding 
225847307954SThierry Reding 	/* enable underflow reporting and display red for missing pixels */
225947307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
226047307954SThierry Reding 		value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
226147307954SThierry Reding 		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
226247307954SThierry Reding 	}
2263666cb873SThierry Reding 
2264f7d6c6aeSDmitry Osipenko 	if (dc->rgb) {
2265f7d6c6aeSDmitry Osipenko 		/* XXX: parameterize? */
2266f7d6c6aeSDmitry Osipenko 		value = SC0_H_QUALIFIER_NONE | SC1_H_QUALIFIER_NONE;
2267f7d6c6aeSDmitry Osipenko 		tegra_dc_writel(dc, value, DC_DISP_SHIFT_CLOCK_OPTIONS);
2268f7d6c6aeSDmitry Osipenko 	}
2269f7d6c6aeSDmitry Osipenko 
2270666cb873SThierry Reding 	tegra_dc_commit(dc);
2271dee8268fSThierry Reding 
22728ff64c17SThierry Reding 	drm_crtc_vblank_on(crtc);
2273dee8268fSThierry Reding }
2274dee8268fSThierry Reding 
tegra_crtc_atomic_begin(struct drm_crtc * crtc,struct drm_atomic_state * state)2275613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
2276f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22774aa3df71SThierry Reding {
22789d99ab6eSThierry Reding 	unsigned long flags;
22791503ca47SThierry Reding 
228004d5d5dfSDmitry Osipenko 	tegra_crtc_update_memory_bandwidth(crtc, state, true);
228104d5d5dfSDmitry Osipenko 
22821503ca47SThierry Reding 	if (crtc->state->event) {
22839d99ab6eSThierry Reding 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
22841503ca47SThierry Reding 
22859d99ab6eSThierry Reding 		if (drm_crtc_vblank_get(crtc) != 0)
22869d99ab6eSThierry Reding 			drm_crtc_send_vblank_event(crtc, crtc->state->event);
22879d99ab6eSThierry Reding 		else
22889d99ab6eSThierry Reding 			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
22891503ca47SThierry Reding 
22909d99ab6eSThierry Reding 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
22919d99ab6eSThierry Reding 
22921503ca47SThierry Reding 		crtc->state->event = NULL;
22931503ca47SThierry Reding 	}
22944aa3df71SThierry Reding }
22954aa3df71SThierry Reding 
tegra_crtc_atomic_flush(struct drm_crtc * crtc,struct drm_atomic_state * state)2296613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
2297f6ebe9f9SMaxime Ripard 				    struct drm_atomic_state *state)
22984aa3df71SThierry Reding {
2299253f28b6SMaxime Ripard 	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state,
2300253f28b6SMaxime Ripard 									  crtc);
2301253f28b6SMaxime Ripard 	struct tegra_dc_state *dc_state = to_dc_state(crtc_state);
230247802b09SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
230347307954SThierry Reding 	u32 value;
230447802b09SThierry Reding 
2305253f28b6SMaxime Ripard 	value = dc_state->planes << 8 | GENERAL_UPDATE;
230647307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
230747307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
230847307954SThierry Reding 
2309253f28b6SMaxime Ripard 	value = dc_state->planes | GENERAL_ACT_REQ;
231047307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
231147307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
23124aa3df71SThierry Reding }
23134aa3df71SThierry Reding 
tegra_plane_is_cursor(const struct drm_plane_state * state)231404d5d5dfSDmitry Osipenko static bool tegra_plane_is_cursor(const struct drm_plane_state *state)
231504d5d5dfSDmitry Osipenko {
231604d5d5dfSDmitry Osipenko 	const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc;
231704d5d5dfSDmitry Osipenko 	const struct drm_format_info *fmt = state->fb->format;
231804d5d5dfSDmitry Osipenko 	unsigned int src_w = drm_rect_width(&state->src) >> 16;
231904d5d5dfSDmitry Osipenko 	unsigned int dst_w = drm_rect_width(&state->dst);
232004d5d5dfSDmitry Osipenko 
232104d5d5dfSDmitry Osipenko 	if (state->plane->type != DRM_PLANE_TYPE_CURSOR)
232204d5d5dfSDmitry Osipenko 		return false;
232304d5d5dfSDmitry Osipenko 
232404d5d5dfSDmitry Osipenko 	if (soc->supports_cursor)
232504d5d5dfSDmitry Osipenko 		return true;
232604d5d5dfSDmitry Osipenko 
232704d5d5dfSDmitry Osipenko 	if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256)
232804d5d5dfSDmitry Osipenko 		return false;
232904d5d5dfSDmitry Osipenko 
233004d5d5dfSDmitry Osipenko 	return true;
233104d5d5dfSDmitry Osipenko }
233204d5d5dfSDmitry Osipenko 
233304d5d5dfSDmitry Osipenko static unsigned long
tegra_plane_overlap_mask(struct drm_crtc_state * state,const struct drm_plane_state * plane_state)233404d5d5dfSDmitry Osipenko tegra_plane_overlap_mask(struct drm_crtc_state *state,
233504d5d5dfSDmitry Osipenko 			 const struct drm_plane_state *plane_state)
233604d5d5dfSDmitry Osipenko {
233704d5d5dfSDmitry Osipenko 	const struct drm_plane_state *other_state;
233804d5d5dfSDmitry Osipenko 	const struct tegra_plane *tegra;
233904d5d5dfSDmitry Osipenko 	unsigned long overlap_mask = 0;
234004d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
234104d5d5dfSDmitry Osipenko 	struct drm_rect rect;
234204d5d5dfSDmitry Osipenko 
234304d5d5dfSDmitry Osipenko 	if (!plane_state->visible || !plane_state->fb)
234404d5d5dfSDmitry Osipenko 		return 0;
234504d5d5dfSDmitry Osipenko 
234604d5d5dfSDmitry Osipenko 	/*
234704d5d5dfSDmitry Osipenko 	 * Data-prefetch FIFO will easily help to overcome temporal memory
234804d5d5dfSDmitry Osipenko 	 * pressure if other plane overlaps with the cursor plane.
234904d5d5dfSDmitry Osipenko 	 */
235004d5d5dfSDmitry Osipenko 	if (tegra_plane_is_cursor(plane_state))
235104d5d5dfSDmitry Osipenko 		return 0;
235204d5d5dfSDmitry Osipenko 
235304d5d5dfSDmitry Osipenko 	drm_atomic_crtc_state_for_each_plane_state(plane, other_state, state) {
235404d5d5dfSDmitry Osipenko 		rect = plane_state->dst;
235504d5d5dfSDmitry Osipenko 
235604d5d5dfSDmitry Osipenko 		tegra = to_tegra_plane(other_state->plane);
235704d5d5dfSDmitry Osipenko 
235804d5d5dfSDmitry Osipenko 		if (!other_state->visible || !other_state->fb)
235904d5d5dfSDmitry Osipenko 			continue;
236004d5d5dfSDmitry Osipenko 
236104d5d5dfSDmitry Osipenko 		/*
236204d5d5dfSDmitry Osipenko 		 * Ignore cursor plane overlaps because it's not practical to
236304d5d5dfSDmitry Osipenko 		 * assume that it contributes to the bandwidth in overlapping
236404d5d5dfSDmitry Osipenko 		 * area if window width is small.
236504d5d5dfSDmitry Osipenko 		 */
236604d5d5dfSDmitry Osipenko 		if (tegra_plane_is_cursor(other_state))
236704d5d5dfSDmitry Osipenko 			continue;
236804d5d5dfSDmitry Osipenko 
236904d5d5dfSDmitry Osipenko 		if (drm_rect_intersect(&rect, &other_state->dst))
237004d5d5dfSDmitry Osipenko 			overlap_mask |= BIT(tegra->index);
237104d5d5dfSDmitry Osipenko 	}
237204d5d5dfSDmitry Osipenko 
237304d5d5dfSDmitry Osipenko 	return overlap_mask;
237404d5d5dfSDmitry Osipenko }
237504d5d5dfSDmitry Osipenko 
tegra_crtc_calculate_memory_bandwidth(struct drm_crtc * crtc,struct drm_atomic_state * state)237604d5d5dfSDmitry Osipenko static int tegra_crtc_calculate_memory_bandwidth(struct drm_crtc *crtc,
237704d5d5dfSDmitry Osipenko 						 struct drm_atomic_state *state)
237804d5d5dfSDmitry Osipenko {
237904d5d5dfSDmitry Osipenko 	ulong overlap_mask[TEGRA_DC_LEGACY_PLANES_NUM] = {}, mask;
238004d5d5dfSDmitry Osipenko 	u32 plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM] = {};
238104d5d5dfSDmitry Osipenko 	bool all_planes_overlap_simultaneously = true;
238204d5d5dfSDmitry Osipenko 	const struct tegra_plane_state *tegra_state;
238304d5d5dfSDmitry Osipenko 	const struct drm_plane_state *plane_state;
238404d5d5dfSDmitry Osipenko 	struct tegra_dc *dc = to_tegra_dc(crtc);
238504d5d5dfSDmitry Osipenko 	struct drm_crtc_state *new_state;
238604d5d5dfSDmitry Osipenko 	struct tegra_plane *tegra;
238704d5d5dfSDmitry Osipenko 	struct drm_plane *plane;
238804d5d5dfSDmitry Osipenko 
238904d5d5dfSDmitry Osipenko 	/*
239004d5d5dfSDmitry Osipenko 	 * The nv-display uses shared planes.  The algorithm below assumes
239104d5d5dfSDmitry Osipenko 	 * maximum 3 planes per-CRTC, this assumption isn't applicable to
239204d5d5dfSDmitry Osipenko 	 * the nv-display.  Note that T124 support has additional windows,
239304d5d5dfSDmitry Osipenko 	 * but currently they aren't supported by the driver.
239404d5d5dfSDmitry Osipenko 	 */
239504d5d5dfSDmitry Osipenko 	if (dc->soc->has_nvdisplay)
239604d5d5dfSDmitry Osipenko 		return 0;
239704d5d5dfSDmitry Osipenko 
239804d5d5dfSDmitry Osipenko 	new_state = drm_atomic_get_new_crtc_state(state, crtc);
239904d5d5dfSDmitry Osipenko 
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 
tegra_crtc_atomic_check(struct drm_crtc * crtc,struct drm_atomic_state * state)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 
tegra_crtc_atomic_post_commit(struct drm_crtc * crtc,struct drm_atomic_state * state)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 
tegra_dc_irq(int irq,void * data)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 
tegra_dc_has_window_groups(struct tegra_dc * dc)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 
tegra_dc_early_init(struct host1x_client * client)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 
tegra_dc_init(struct host1x_client * client)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 
tegra_dc_exit(struct host1x_client * client)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 
tegra_dc_late_exit(struct host1x_client * client)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 
tegra_dc_runtime_suspend(struct host1x_client * client)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 
tegra_dc_runtime_resume(struct host1x_client * client)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 
tegra_dc_parse_dt(struct tegra_dc * dc)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 
tegra_dc_match_by_pipe(struct device * dev,const void * data)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 
tegra_dc_couple(struct tegra_dc * dc)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 
tegra_dc_init_opp_table(struct tegra_dc * dc)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 
tegra_dc_probe(struct platform_device * pdev)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);
32077ad4384dSZhang Zekun 	if (err < 0) {
32087ad4384dSZhang Zekun 		clk_disable_unprepare(dc->clk);
3209a2f2f740SThierry Reding 		return err;
32107ad4384dSZhang Zekun 	}
3211a2f2f740SThierry Reding 
3212a2f2f740SThierry Reding 	usleep_range(2000, 4000);
3213a2f2f740SThierry Reding 
3214a2f2f740SThierry Reding 	clk_disable_unprepare(dc->clk);
321533a8eb8dSThierry Reding 
32169c012700SThierry Reding 	if (dc->soc->has_powergate) {
32179c012700SThierry Reding 		if (dc->pipe == 0)
32189c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DIS;
32199c012700SThierry Reding 		else
32209c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DISB;
32219c012700SThierry Reding 
322233a8eb8dSThierry Reding 		tegra_powergate_power_off(dc->powergate);
32239c012700SThierry Reding 	}
3224dee8268fSThierry Reding 
32254ce3048cSDmitry Osipenko 	err = tegra_dc_init_opp_table(dc);
32264ce3048cSDmitry Osipenko 	if (err < 0)
32274ce3048cSDmitry Osipenko 		return err;
32284ce3048cSDmitry Osipenko 
3229a858ac8fSDmitry Osipenko 	dc->regs = devm_platform_ioremap_resource(pdev, 0);
3230dee8268fSThierry Reding 	if (IS_ERR(dc->regs))
3231dee8268fSThierry Reding 		return PTR_ERR(dc->regs);
3232dee8268fSThierry Reding 
3233dee8268fSThierry Reding 	dc->irq = platform_get_irq(pdev, 0);
32345f1df70fSTang Bin 	if (dc->irq < 0)
3235dee8268fSThierry Reding 		return -ENXIO;
3236dee8268fSThierry Reding 
3237dee8268fSThierry Reding 	err = tegra_dc_rgb_probe(dc);
3238f07f04a5SDmitry Osipenko 	if (err < 0 && err != -ENODEV)
3239f07f04a5SDmitry Osipenko 		return dev_err_probe(&pdev->dev, err,
3240f07f04a5SDmitry Osipenko 				     "failed to probe RGB output\n");
3241dee8268fSThierry Reding 
324233a8eb8dSThierry Reding 	platform_set_drvdata(pdev, dc);
324333a8eb8dSThierry Reding 	pm_runtime_enable(&pdev->dev);
324433a8eb8dSThierry Reding 
324533a8eb8dSThierry Reding 	INIT_LIST_HEAD(&dc->client.list);
324633a8eb8dSThierry Reding 	dc->client.ops = &dc_client_ops;
324733a8eb8dSThierry Reding 	dc->client.dev = &pdev->dev;
324833a8eb8dSThierry Reding 
3249dee8268fSThierry Reding 	err = host1x_client_register(&dc->client);
3250dee8268fSThierry Reding 	if (err < 0) {
3251dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
3252dee8268fSThierry Reding 			err);
32530411ea89SDmitry Osipenko 		goto disable_pm;
3254dee8268fSThierry Reding 	}
3255dee8268fSThierry Reding 
3256dee8268fSThierry Reding 	return 0;
32570411ea89SDmitry Osipenko 
32580411ea89SDmitry Osipenko disable_pm:
32590411ea89SDmitry Osipenko 	pm_runtime_disable(&pdev->dev);
32600411ea89SDmitry Osipenko 	tegra_dc_rgb_remove(dc);
32610411ea89SDmitry Osipenko 
32620411ea89SDmitry Osipenko 	return err;
3263dee8268fSThierry Reding }
3264dee8268fSThierry Reding 
tegra_dc_remove(struct platform_device * pdev)326530d92e0fSUwe Kleine-König static void tegra_dc_remove(struct platform_device *pdev)
3266dee8268fSThierry Reding {
3267dee8268fSThierry Reding 	struct tegra_dc *dc = platform_get_drvdata(pdev);
3268dee8268fSThierry Reding 
32691d83d1a2SUwe Kleine-König 	host1x_client_unregister(&dc->client);
3270dee8268fSThierry Reding 
327143740540SUwe Kleine-König 	tegra_dc_rgb_remove(dc);
327259d29c0eSThierry Reding 
327333a8eb8dSThierry Reding 	pm_runtime_disable(&pdev->dev);
327433a8eb8dSThierry Reding }
327533a8eb8dSThierry Reding 
3276dee8268fSThierry Reding struct platform_driver tegra_dc_driver = {
3277dee8268fSThierry Reding 	.driver = {
3278dee8268fSThierry Reding 		.name = "tegra-dc",
3279dee8268fSThierry Reding 		.of_match_table = tegra_dc_of_match,
3280dee8268fSThierry Reding 	},
3281dee8268fSThierry Reding 	.probe = tegra_dc_probe,
328230d92e0fSUwe Kleine-König 	.remove_new = tegra_dc_remove,
3283dee8268fSThierry Reding };
3284