xref: /openbmc/linux/drivers/gpu/drm/tegra/dc.c (revision bc8828bd08bd2a645caeb64d299d67faca7a3b4f)
1dee8268fSThierry Reding /*
2dee8268fSThierry Reding  * Copyright (C) 2012 Avionic Design GmbH
3dee8268fSThierry Reding  * Copyright (C) 2012 NVIDIA CORPORATION.  All rights reserved.
4dee8268fSThierry Reding  *
5dee8268fSThierry Reding  * This program is free software; you can redistribute it and/or modify
6dee8268fSThierry Reding  * it under the terms of the GNU General Public License version 2 as
7dee8268fSThierry Reding  * published by the Free Software Foundation.
8dee8268fSThierry Reding  */
9dee8268fSThierry Reding 
10dee8268fSThierry Reding #include <linux/clk.h>
11dee8268fSThierry Reding #include <linux/debugfs.h>
12df06b759SThierry Reding #include <linux/iommu.h>
13b9ff7aeaSThierry Reding #include <linux/of_device.h>
1433a8eb8dSThierry Reding #include <linux/pm_runtime.h>
15ca48080aSStephen Warren #include <linux/reset.h>
16dee8268fSThierry Reding 
179c012700SThierry Reding #include <soc/tegra/pmc.h>
189c012700SThierry Reding 
19dee8268fSThierry Reding #include "dc.h"
20dee8268fSThierry Reding #include "drm.h"
21dee8268fSThierry Reding #include "gem.h"
2247307954SThierry Reding #include "hub.h"
235acd3514SThierry Reding #include "plane.h"
24dee8268fSThierry Reding 
259d44189fSThierry Reding #include <drm/drm_atomic.h>
264aa3df71SThierry Reding #include <drm/drm_atomic_helper.h>
273cb9ae4fSDaniel Vetter #include <drm/drm_plane_helper.h>
283cb9ae4fSDaniel Vetter 
29791ddb1eSThierry Reding static void tegra_dc_stats_reset(struct tegra_dc_stats *stats)
30791ddb1eSThierry Reding {
31791ddb1eSThierry Reding 	stats->frames = 0;
32791ddb1eSThierry Reding 	stats->vblank = 0;
33791ddb1eSThierry Reding 	stats->underflow = 0;
34791ddb1eSThierry Reding 	stats->overflow = 0;
35791ddb1eSThierry Reding }
36791ddb1eSThierry Reding 
371087fac1SThierry Reding /* Reads the active copy of a register. */
3886df256fSThierry Reding static u32 tegra_dc_readl_active(struct tegra_dc *dc, unsigned long offset)
3986df256fSThierry Reding {
4086df256fSThierry Reding 	u32 value;
4186df256fSThierry Reding 
4286df256fSThierry Reding 	tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
4386df256fSThierry Reding 	value = tegra_dc_readl(dc, offset);
4486df256fSThierry Reding 	tegra_dc_writel(dc, 0, DC_CMD_STATE_ACCESS);
4586df256fSThierry Reding 
4686df256fSThierry Reding 	return value;
4786df256fSThierry Reding }
4886df256fSThierry Reding 
491087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
501087fac1SThierry Reding 					      unsigned int offset)
511087fac1SThierry Reding {
521087fac1SThierry Reding 	if (offset >= 0x500 && offset <= 0x638) {
531087fac1SThierry Reding 		offset = 0x000 + (offset - 0x500);
541087fac1SThierry Reding 		return plane->offset + offset;
551087fac1SThierry Reding 	}
561087fac1SThierry Reding 
571087fac1SThierry Reding 	if (offset >= 0x700 && offset <= 0x719) {
581087fac1SThierry Reding 		offset = 0x180 + (offset - 0x700);
591087fac1SThierry Reding 		return plane->offset + offset;
601087fac1SThierry Reding 	}
611087fac1SThierry Reding 
621087fac1SThierry Reding 	if (offset >= 0x800 && offset <= 0x839) {
631087fac1SThierry Reding 		offset = 0x1c0 + (offset - 0x800);
641087fac1SThierry Reding 		return plane->offset + offset;
651087fac1SThierry Reding 	}
661087fac1SThierry Reding 
671087fac1SThierry Reding 	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
681087fac1SThierry Reding 
691087fac1SThierry Reding 	return plane->offset + offset;
701087fac1SThierry Reding }
711087fac1SThierry Reding 
721087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane,
731087fac1SThierry Reding 				    unsigned int offset)
741087fac1SThierry Reding {
751087fac1SThierry Reding 	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
761087fac1SThierry Reding }
771087fac1SThierry Reding 
781087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
791087fac1SThierry Reding 				      unsigned int offset)
801087fac1SThierry Reding {
811087fac1SThierry Reding 	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
821087fac1SThierry Reding }
831087fac1SThierry Reding 
84c57997bcSThierry Reding bool tegra_dc_has_output(struct tegra_dc *dc, struct device *dev)
85c57997bcSThierry Reding {
86c57997bcSThierry Reding 	struct device_node *np = dc->dev->of_node;
87c57997bcSThierry Reding 	struct of_phandle_iterator it;
88c57997bcSThierry Reding 	int err;
89c57997bcSThierry Reding 
90c57997bcSThierry Reding 	of_for_each_phandle(&it, err, np, "nvidia,outputs", NULL, 0)
91c57997bcSThierry Reding 		if (it.node == dev->of_node)
92c57997bcSThierry Reding 			return true;
93c57997bcSThierry Reding 
94c57997bcSThierry Reding 	return false;
95c57997bcSThierry Reding }
96c57997bcSThierry Reding 
9786df256fSThierry Reding /*
98d700ba7aSThierry Reding  * Double-buffered registers have two copies: ASSEMBLY and ACTIVE. When the
99d700ba7aSThierry Reding  * *_ACT_REQ bits are set the ASSEMBLY copy is latched into the ACTIVE copy.
100d700ba7aSThierry Reding  * Latching happens mmediately if the display controller is in STOP mode or
101d700ba7aSThierry Reding  * on the next frame boundary otherwise.
102d700ba7aSThierry Reding  *
103d700ba7aSThierry Reding  * Triple-buffered registers have three copies: ASSEMBLY, ARM and ACTIVE. The
104d700ba7aSThierry Reding  * ASSEMBLY copy is latched into the ARM copy immediately after *_UPDATE bits
105d700ba7aSThierry Reding  * are written. When the *_ACT_REQ bits are written, the ARM copy is latched
106d700ba7aSThierry Reding  * into the ACTIVE copy, either immediately if the display controller is in
107d700ba7aSThierry Reding  * STOP mode, or at the next frame boundary otherwise.
108d700ba7aSThierry Reding  */
10962b9e063SThierry Reding void tegra_dc_commit(struct tegra_dc *dc)
110205d48edSThierry Reding {
111205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ << 8, DC_CMD_STATE_CONTROL);
112205d48edSThierry Reding 	tegra_dc_writel(dc, GENERAL_ACT_REQ, DC_CMD_STATE_CONTROL);
113205d48edSThierry Reding }
114205d48edSThierry Reding 
11510288eeaSThierry Reding static inline u32 compute_dda_inc(unsigned int in, unsigned int out, bool v,
11610288eeaSThierry Reding 				  unsigned int bpp)
11710288eeaSThierry Reding {
11810288eeaSThierry Reding 	fixed20_12 outf = dfixed_init(out);
11910288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
12010288eeaSThierry Reding 	u32 dda_inc;
12110288eeaSThierry Reding 	int max;
12210288eeaSThierry Reding 
12310288eeaSThierry Reding 	if (v)
12410288eeaSThierry Reding 		max = 15;
12510288eeaSThierry Reding 	else {
12610288eeaSThierry Reding 		switch (bpp) {
12710288eeaSThierry Reding 		case 2:
12810288eeaSThierry Reding 			max = 8;
12910288eeaSThierry Reding 			break;
13010288eeaSThierry Reding 
13110288eeaSThierry Reding 		default:
13210288eeaSThierry Reding 			WARN_ON_ONCE(1);
13310288eeaSThierry Reding 			/* fallthrough */
13410288eeaSThierry Reding 		case 4:
13510288eeaSThierry Reding 			max = 4;
13610288eeaSThierry Reding 			break;
13710288eeaSThierry Reding 		}
13810288eeaSThierry Reding 	}
13910288eeaSThierry Reding 
14010288eeaSThierry Reding 	outf.full = max_t(u32, outf.full - dfixed_const(1), dfixed_const(1));
14110288eeaSThierry Reding 	inf.full -= dfixed_const(1);
14210288eeaSThierry Reding 
14310288eeaSThierry Reding 	dda_inc = dfixed_div(inf, outf);
14410288eeaSThierry Reding 	dda_inc = min_t(u32, dda_inc, dfixed_const(max));
14510288eeaSThierry Reding 
14610288eeaSThierry Reding 	return dda_inc;
14710288eeaSThierry Reding }
14810288eeaSThierry Reding 
14910288eeaSThierry Reding static inline u32 compute_initial_dda(unsigned int in)
15010288eeaSThierry Reding {
15110288eeaSThierry Reding 	fixed20_12 inf = dfixed_init(in);
15210288eeaSThierry Reding 	return dfixed_frac(inf);
15310288eeaSThierry Reding }
15410288eeaSThierry Reding 
155ab7d3f58SThierry Reding static void tegra_plane_setup_blending_legacy(struct tegra_plane *plane)
156ab7d3f58SThierry Reding {
157ab7d3f58SThierry Reding 	/*
158ab7d3f58SThierry Reding 	 * Disable blending and assume Window A is the bottom-most window,
159ab7d3f58SThierry Reding 	 * Window C is the top-most window and Window B is in the middle.
160ab7d3f58SThierry Reding 	 */
161ab7d3f58SThierry Reding 	tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_NOKEY);
162ab7d3f58SThierry Reding 	tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_1WIN);
163ab7d3f58SThierry Reding 
164ab7d3f58SThierry Reding 	switch (plane->index) {
165ab7d3f58SThierry Reding 	case 0:
166ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_X);
167ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
168ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
169ab7d3f58SThierry Reding 		break;
170ab7d3f58SThierry Reding 
171ab7d3f58SThierry Reding 	case 1:
172ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
173ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_2WIN_Y);
174ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0x000000, DC_WIN_BLEND_3WIN_XY);
175ab7d3f58SThierry Reding 		break;
176ab7d3f58SThierry Reding 
177ab7d3f58SThierry Reding 	case 2:
178ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_X);
179ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_2WIN_Y);
180ab7d3f58SThierry Reding 		tegra_plane_writel(plane, 0xffff00, DC_WIN_BLEND_3WIN_XY);
181ab7d3f58SThierry Reding 		break;
182ab7d3f58SThierry Reding 	}
183ab7d3f58SThierry Reding }
184ab7d3f58SThierry Reding 
185ab7d3f58SThierry Reding static void tegra_plane_setup_blending(struct tegra_plane *plane,
186ab7d3f58SThierry Reding 				       const struct tegra_dc_window *window)
187ab7d3f58SThierry Reding {
188ab7d3f58SThierry Reding 	u32 value;
189ab7d3f58SThierry Reding 
190ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
191ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
192ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
193ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_MATCH_SELECT);
194ab7d3f58SThierry Reding 
195ab7d3f58SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
196ab7d3f58SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
197ab7d3f58SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
198ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_NOMATCH_SELECT);
199ab7d3f58SThierry Reding 
200ab7d3f58SThierry Reding 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - window->zpos);
201ab7d3f58SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_BLEND_LAYER_CONTROL);
202ab7d3f58SThierry Reding }
203ab7d3f58SThierry Reding 
2041087fac1SThierry Reding static void tegra_dc_setup_window(struct tegra_plane *plane,
20510288eeaSThierry Reding 				  const struct tegra_dc_window *window)
20610288eeaSThierry Reding {
20710288eeaSThierry Reding 	unsigned h_offset, v_offset, h_size, v_size, h_dda, v_dda, bpp;
2081087fac1SThierry Reding 	struct tegra_dc *dc = plane->dc;
20910288eeaSThierry Reding 	bool yuv, planar;
2101087fac1SThierry Reding 	u32 value;
21110288eeaSThierry Reding 
21210288eeaSThierry Reding 	/*
21310288eeaSThierry Reding 	 * For YUV planar modes, the number of bytes per pixel takes into
21410288eeaSThierry Reding 	 * account only the luma component and therefore is 1.
21510288eeaSThierry Reding 	 */
2165acd3514SThierry Reding 	yuv = tegra_plane_format_is_yuv(window->format, &planar);
21710288eeaSThierry Reding 	if (!yuv)
21810288eeaSThierry Reding 		bpp = window->bits_per_pixel / 8;
21910288eeaSThierry Reding 	else
22010288eeaSThierry Reding 		bpp = planar ? 1 : 2;
22110288eeaSThierry Reding 
2221087fac1SThierry Reding 	tegra_plane_writel(plane, window->format, DC_WIN_COLOR_DEPTH);
2231087fac1SThierry Reding 	tegra_plane_writel(plane, window->swap, DC_WIN_BYTE_SWAP);
22410288eeaSThierry Reding 
22510288eeaSThierry Reding 	value = V_POSITION(window->dst.y) | H_POSITION(window->dst.x);
2261087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_POSITION);
22710288eeaSThierry Reding 
22810288eeaSThierry Reding 	value = V_SIZE(window->dst.h) | H_SIZE(window->dst.w);
2291087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_SIZE);
23010288eeaSThierry Reding 
23110288eeaSThierry Reding 	h_offset = window->src.x * bpp;
23210288eeaSThierry Reding 	v_offset = window->src.y;
23310288eeaSThierry Reding 	h_size = window->src.w * bpp;
23410288eeaSThierry Reding 	v_size = window->src.h;
23510288eeaSThierry Reding 
23610288eeaSThierry Reding 	value = V_PRESCALED_SIZE(v_size) | H_PRESCALED_SIZE(h_size);
2371087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_PRESCALED_SIZE);
23810288eeaSThierry Reding 
23910288eeaSThierry Reding 	/*
24010288eeaSThierry Reding 	 * For DDA computations the number of bytes per pixel for YUV planar
24110288eeaSThierry Reding 	 * modes needs to take into account all Y, U and V components.
24210288eeaSThierry Reding 	 */
24310288eeaSThierry Reding 	if (yuv && planar)
24410288eeaSThierry Reding 		bpp = 2;
24510288eeaSThierry Reding 
24610288eeaSThierry Reding 	h_dda = compute_dda_inc(window->src.w, window->dst.w, false, bpp);
24710288eeaSThierry Reding 	v_dda = compute_dda_inc(window->src.h, window->dst.h, true, bpp);
24810288eeaSThierry Reding 
24910288eeaSThierry Reding 	value = V_DDA_INC(v_dda) | H_DDA_INC(h_dda);
2501087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_DDA_INC);
25110288eeaSThierry Reding 
25210288eeaSThierry Reding 	h_dda = compute_initial_dda(window->src.x);
25310288eeaSThierry Reding 	v_dda = compute_initial_dda(window->src.y);
25410288eeaSThierry Reding 
2551087fac1SThierry Reding 	tegra_plane_writel(plane, h_dda, DC_WIN_H_INITIAL_DDA);
2561087fac1SThierry Reding 	tegra_plane_writel(plane, v_dda, DC_WIN_V_INITIAL_DDA);
25710288eeaSThierry Reding 
2581087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_UV_BUF_STRIDE);
2591087fac1SThierry Reding 	tegra_plane_writel(plane, 0, DC_WIN_BUF_STRIDE);
26010288eeaSThierry Reding 
2611087fac1SThierry Reding 	tegra_plane_writel(plane, window->base[0], DC_WINBUF_START_ADDR);
26210288eeaSThierry Reding 
26310288eeaSThierry Reding 	if (yuv && planar) {
2641087fac1SThierry Reding 		tegra_plane_writel(plane, window->base[1], DC_WINBUF_START_ADDR_U);
2651087fac1SThierry Reding 		tegra_plane_writel(plane, window->base[2], DC_WINBUF_START_ADDR_V);
26610288eeaSThierry Reding 		value = window->stride[1] << 16 | window->stride[0];
2671087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_LINE_STRIDE);
26810288eeaSThierry Reding 	} else {
2691087fac1SThierry Reding 		tegra_plane_writel(plane, window->stride[0], DC_WIN_LINE_STRIDE);
27010288eeaSThierry Reding 	}
27110288eeaSThierry Reding 
27210288eeaSThierry Reding 	if (window->bottom_up)
27310288eeaSThierry Reding 		v_offset += window->src.h - 1;
27410288eeaSThierry Reding 
2751087fac1SThierry Reding 	tegra_plane_writel(plane, h_offset, DC_WINBUF_ADDR_H_OFFSET);
2761087fac1SThierry Reding 	tegra_plane_writel(plane, v_offset, DC_WINBUF_ADDR_V_OFFSET);
27710288eeaSThierry Reding 
278c134f019SThierry Reding 	if (dc->soc->supports_block_linear) {
279c134f019SThierry Reding 		unsigned long height = window->tiling.value;
280c134f019SThierry Reding 
281c134f019SThierry Reding 		switch (window->tiling.mode) {
282c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
283c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_PITCH;
284c134f019SThierry Reding 			break;
285c134f019SThierry Reding 
286c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
287c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_TILED;
288c134f019SThierry Reding 			break;
289c134f019SThierry Reding 
290c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
291c134f019SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
292c134f019SThierry Reding 				DC_WINBUF_SURFACE_KIND_BLOCK;
293c134f019SThierry Reding 			break;
294c134f019SThierry Reding 		}
295c134f019SThierry Reding 
2961087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WINBUF_SURFACE_KIND);
29710288eeaSThierry Reding 	} else {
298c134f019SThierry Reding 		switch (window->tiling.mode) {
299c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
30010288eeaSThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_LINEAR_UV |
30110288eeaSThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_LINEAR;
302c134f019SThierry Reding 			break;
303c134f019SThierry Reding 
304c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
305c134f019SThierry Reding 			value = DC_WIN_BUFFER_ADDR_MODE_TILE_UV |
306c134f019SThierry Reding 				DC_WIN_BUFFER_ADDR_MODE_TILE;
307c134f019SThierry Reding 			break;
308c134f019SThierry Reding 
309c134f019SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
3104aa3df71SThierry Reding 			/*
3114aa3df71SThierry Reding 			 * No need to handle this here because ->atomic_check
3124aa3df71SThierry Reding 			 * will already have filtered it out.
3134aa3df71SThierry Reding 			 */
3144aa3df71SThierry Reding 			break;
31510288eeaSThierry Reding 		}
31610288eeaSThierry Reding 
3171087fac1SThierry Reding 		tegra_plane_writel(plane, value, DC_WIN_BUFFER_ADDR_MODE);
318c134f019SThierry Reding 	}
31910288eeaSThierry Reding 
32010288eeaSThierry Reding 	value = WIN_ENABLE;
32110288eeaSThierry Reding 
32210288eeaSThierry Reding 	if (yuv) {
32310288eeaSThierry Reding 		/* setup default colorspace conversion coefficients */
3241087fac1SThierry Reding 		tegra_plane_writel(plane, 0x00f0, DC_WIN_CSC_YOF);
3251087fac1SThierry Reding 		tegra_plane_writel(plane, 0x012a, DC_WIN_CSC_KYRGB);
3261087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KUR);
3271087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0198, DC_WIN_CSC_KVR);
3281087fac1SThierry Reding 		tegra_plane_writel(plane, 0x039b, DC_WIN_CSC_KUG);
3291087fac1SThierry Reding 		tegra_plane_writel(plane, 0x032f, DC_WIN_CSC_KVG);
3301087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0204, DC_WIN_CSC_KUB);
3311087fac1SThierry Reding 		tegra_plane_writel(plane, 0x0000, DC_WIN_CSC_KVB);
33210288eeaSThierry Reding 
33310288eeaSThierry Reding 		value |= CSC_ENABLE;
33410288eeaSThierry Reding 	} else if (window->bits_per_pixel < 24) {
33510288eeaSThierry Reding 		value |= COLOR_EXPAND;
33610288eeaSThierry Reding 	}
33710288eeaSThierry Reding 
33810288eeaSThierry Reding 	if (window->bottom_up)
33910288eeaSThierry Reding 		value |= V_DIRECTION;
34010288eeaSThierry Reding 
3411087fac1SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_WIN_OPTIONS);
34210288eeaSThierry Reding 
343ab7d3f58SThierry Reding 	if (dc->soc->supports_blending)
344ab7d3f58SThierry Reding 		tegra_plane_setup_blending(plane, window);
345ab7d3f58SThierry Reding 	else
346ab7d3f58SThierry Reding 		tegra_plane_setup_blending_legacy(plane);
347c7679306SThierry Reding }
348c7679306SThierry Reding 
349511c7023SThierry Reding static const u32 tegra20_primary_formats[] = {
350511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
351511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
352c7679306SThierry Reding 	DRM_FORMAT_RGB565,
353511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
354511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
355511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
356511c7023SThierry Reding };
357511c7023SThierry Reding 
358511c7023SThierry Reding static const u32 tegra114_primary_formats[] = {
359511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
360511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
361511c7023SThierry Reding 	DRM_FORMAT_RGB565,
362511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
363511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
364511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
365511c7023SThierry Reding 	/* new on Tegra114 */
366511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
367511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
368511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
369511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
370511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
371511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
372511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
373511c7023SThierry Reding 	DRM_FORMAT_BGR565,
374511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
375511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
376511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
377511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
378511c7023SThierry Reding };
379511c7023SThierry Reding 
380511c7023SThierry Reding static const u32 tegra124_primary_formats[] = {
381511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
382511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
383511c7023SThierry Reding 	DRM_FORMAT_RGB565,
384511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
385511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
386511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
387511c7023SThierry Reding 	/* new on Tegra114 */
388511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
389511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
390511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
391511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
392511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
393511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
394511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
395511c7023SThierry Reding 	DRM_FORMAT_BGR565,
396511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
397511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
398511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
399511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
400511c7023SThierry Reding 	/* new on Tegra124 */
401511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
402511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
403c7679306SThierry Reding };
404c7679306SThierry Reding 
4054aa3df71SThierry Reding static int tegra_plane_atomic_check(struct drm_plane *plane,
4064aa3df71SThierry Reding 				    struct drm_plane_state *state)
4074aa3df71SThierry Reding {
4088f604f8cSThierry Reding 	struct tegra_plane_state *plane_state = to_tegra_plane_state(state);
4098f604f8cSThierry Reding 	struct tegra_bo_tiling *tiling = &plane_state->tiling;
41047802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
4114aa3df71SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
412c7679306SThierry Reding 	int err;
413c7679306SThierry Reding 
4144aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
4154aa3df71SThierry Reding 	if (!state->crtc)
4164aa3df71SThierry Reding 		return 0;
4174aa3df71SThierry Reding 
4185acd3514SThierry Reding 	err = tegra_plane_format(state->fb->format->format,
4195acd3514SThierry Reding 				 &plane_state->format,
4208f604f8cSThierry Reding 				 &plane_state->swap);
4214aa3df71SThierry Reding 	if (err < 0)
4224aa3df71SThierry Reding 		return err;
4234aa3df71SThierry Reding 
4248f604f8cSThierry Reding 	err = tegra_fb_get_tiling(state->fb, tiling);
4258f604f8cSThierry Reding 	if (err < 0)
4268f604f8cSThierry Reding 		return err;
4278f604f8cSThierry Reding 
4288f604f8cSThierry Reding 	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
4294aa3df71SThierry Reding 	    !dc->soc->supports_block_linear) {
4304aa3df71SThierry Reding 		DRM_ERROR("hardware doesn't support block linear mode\n");
4314aa3df71SThierry Reding 		return -EINVAL;
4324aa3df71SThierry Reding 	}
4334aa3df71SThierry Reding 
4344aa3df71SThierry Reding 	/*
4354aa3df71SThierry Reding 	 * Tegra doesn't support different strides for U and V planes so we
4364aa3df71SThierry Reding 	 * error out if the user tries to display a framebuffer with such a
4374aa3df71SThierry Reding 	 * configuration.
4384aa3df71SThierry Reding 	 */
439bcb0b461SVille Syrjälä 	if (state->fb->format->num_planes > 2) {
4404aa3df71SThierry Reding 		if (state->fb->pitches[2] != state->fb->pitches[1]) {
4414aa3df71SThierry Reding 			DRM_ERROR("unsupported UV-plane configuration\n");
4424aa3df71SThierry Reding 			return -EINVAL;
4434aa3df71SThierry Reding 		}
4444aa3df71SThierry Reding 	}
4454aa3df71SThierry Reding 
44647802b09SThierry Reding 	err = tegra_plane_state_add(tegra, state);
44747802b09SThierry Reding 	if (err < 0)
44847802b09SThierry Reding 		return err;
44947802b09SThierry Reding 
4504aa3df71SThierry Reding 	return 0;
4514aa3df71SThierry Reding }
4524aa3df71SThierry Reding 
453a4bfa096SThierry Reding static void tegra_plane_atomic_disable(struct drm_plane *plane,
454a4bfa096SThierry Reding 				       struct drm_plane_state *old_state)
45580d3eef1SDmitry Osipenko {
456a4bfa096SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
45780d3eef1SDmitry Osipenko 	u32 value;
45880d3eef1SDmitry Osipenko 
459a4bfa096SThierry Reding 	/* rien ne va plus */
460a4bfa096SThierry Reding 	if (!old_state || !old_state->crtc)
461a4bfa096SThierry Reding 		return;
462a4bfa096SThierry Reding 
4631087fac1SThierry Reding 	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
46480d3eef1SDmitry Osipenko 	value &= ~WIN_ENABLE;
4651087fac1SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
46680d3eef1SDmitry Osipenko }
46780d3eef1SDmitry Osipenko 
4684aa3df71SThierry Reding static void tegra_plane_atomic_update(struct drm_plane *plane,
4694aa3df71SThierry Reding 				      struct drm_plane_state *old_state)
4704aa3df71SThierry Reding {
4718f604f8cSThierry Reding 	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
4724aa3df71SThierry Reding 	struct drm_framebuffer *fb = plane->state->fb;
4734aa3df71SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
4744aa3df71SThierry Reding 	struct tegra_dc_window window;
4754aa3df71SThierry Reding 	unsigned int i;
4764aa3df71SThierry Reding 
4774aa3df71SThierry Reding 	/* rien ne va plus */
4784aa3df71SThierry Reding 	if (!plane->state->crtc || !plane->state->fb)
4794aa3df71SThierry Reding 		return;
4804aa3df71SThierry Reding 
48180d3eef1SDmitry Osipenko 	if (!plane->state->visible)
482a4bfa096SThierry Reding 		return tegra_plane_atomic_disable(plane, old_state);
48380d3eef1SDmitry Osipenko 
484c7679306SThierry Reding 	memset(&window, 0, sizeof(window));
4857d205857SDmitry Osipenko 	window.src.x = plane->state->src.x1 >> 16;
4867d205857SDmitry Osipenko 	window.src.y = plane->state->src.y1 >> 16;
4877d205857SDmitry Osipenko 	window.src.w = drm_rect_width(&plane->state->src) >> 16;
4887d205857SDmitry Osipenko 	window.src.h = drm_rect_height(&plane->state->src) >> 16;
4897d205857SDmitry Osipenko 	window.dst.x = plane->state->dst.x1;
4907d205857SDmitry Osipenko 	window.dst.y = plane->state->dst.y1;
4917d205857SDmitry Osipenko 	window.dst.w = drm_rect_width(&plane->state->dst);
4927d205857SDmitry Osipenko 	window.dst.h = drm_rect_height(&plane->state->dst);
493272725c7SVille Syrjälä 	window.bits_per_pixel = fb->format->cpp[0] * 8;
494c7679306SThierry Reding 	window.bottom_up = tegra_fb_is_bottom_up(fb);
495c7679306SThierry Reding 
4968f604f8cSThierry Reding 	/* copy from state */
497ab7d3f58SThierry Reding 	window.zpos = plane->state->normalized_zpos;
4988f604f8cSThierry Reding 	window.tiling = state->tiling;
4998f604f8cSThierry Reding 	window.format = state->format;
5008f604f8cSThierry Reding 	window.swap = state->swap;
501c7679306SThierry Reding 
502bcb0b461SVille Syrjälä 	for (i = 0; i < fb->format->num_planes; i++) {
5034aa3df71SThierry Reding 		struct tegra_bo *bo = tegra_fb_get_plane(fb, i);
504c7679306SThierry Reding 
5054aa3df71SThierry Reding 		window.base[i] = bo->paddr + fb->offsets[i];
50608ee0178SDmitry Osipenko 
50708ee0178SDmitry Osipenko 		/*
50808ee0178SDmitry Osipenko 		 * Tegra uses a shared stride for UV planes. Framebuffers are
50908ee0178SDmitry Osipenko 		 * already checked for this in the tegra_plane_atomic_check()
51008ee0178SDmitry Osipenko 		 * function, so it's safe to ignore the V-plane pitch here.
51108ee0178SDmitry Osipenko 		 */
51208ee0178SDmitry Osipenko 		if (i < 2)
5134aa3df71SThierry Reding 			window.stride[i] = fb->pitches[i];
514c7679306SThierry Reding 	}
515c7679306SThierry Reding 
5161087fac1SThierry Reding 	tegra_dc_setup_window(p, &window);
5174aa3df71SThierry Reding }
5184aa3df71SThierry Reding 
519a4bfa096SThierry Reding static const struct drm_plane_helper_funcs tegra_plane_helper_funcs = {
5204aa3df71SThierry Reding 	.atomic_check = tegra_plane_atomic_check,
5214aa3df71SThierry Reding 	.atomic_disable = tegra_plane_atomic_disable,
522a4bfa096SThierry Reding 	.atomic_update = tegra_plane_atomic_update,
523c7679306SThierry Reding };
524c7679306SThierry Reding 
52547307954SThierry Reding static struct drm_plane *tegra_primary_plane_create(struct drm_device *drm,
526c7679306SThierry Reding 						    struct tegra_dc *dc)
527c7679306SThierry Reding {
528518e6227SThierry Reding 	/*
529518e6227SThierry Reding 	 * Ideally this would use drm_crtc_mask(), but that would require the
530518e6227SThierry Reding 	 * CRTC to already be in the mode_config's list of CRTCs. However, it
531518e6227SThierry Reding 	 * will only be added to that list in the drm_crtc_init_with_planes()
532518e6227SThierry Reding 	 * (in tegra_dc_init()), which in turn requires registration of these
533518e6227SThierry Reding 	 * planes. So we have ourselves a nice little chicken and egg problem
534518e6227SThierry Reding 	 * here.
535518e6227SThierry Reding 	 *
536518e6227SThierry Reding 	 * We work around this by manually creating the mask from the number
537518e6227SThierry Reding 	 * of CRTCs that have been registered, and should therefore always be
538518e6227SThierry Reding 	 * the same as drm_crtc_index() after registration.
539518e6227SThierry Reding 	 */
540518e6227SThierry Reding 	unsigned long possible_crtcs = 1 << drm->mode_config.num_crtc;
54147307954SThierry Reding 	enum drm_plane_type type = DRM_PLANE_TYPE_PRIMARY;
542c7679306SThierry Reding 	struct tegra_plane *plane;
543c7679306SThierry Reding 	unsigned int num_formats;
544c7679306SThierry Reding 	const u32 *formats;
545c7679306SThierry Reding 	int err;
546c7679306SThierry Reding 
547c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
548c7679306SThierry Reding 	if (!plane)
549c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
550c7679306SThierry Reding 
5511087fac1SThierry Reding 	/* Always use window A as primary window */
5521087fac1SThierry Reding 	plane->offset = 0xa00;
553c4755fb9SThierry Reding 	plane->index = 0;
5541087fac1SThierry Reding 	plane->dc = dc;
5551087fac1SThierry Reding 
5561087fac1SThierry Reding 	num_formats = dc->soc->num_primary_formats;
5571087fac1SThierry Reding 	formats = dc->soc->primary_formats;
558c4755fb9SThierry Reding 
559518e6227SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
560c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
56147307954SThierry Reding 				       num_formats, NULL, type, NULL);
562c7679306SThierry Reding 	if (err < 0) {
563c7679306SThierry Reding 		kfree(plane);
564c7679306SThierry Reding 		return ERR_PTR(err);
565c7679306SThierry Reding 	}
566c7679306SThierry Reding 
567a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
5684aa3df71SThierry Reding 
569ab7d3f58SThierry Reding 	if (dc->soc->supports_blending)
570ab7d3f58SThierry Reding 		drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
571ab7d3f58SThierry Reding 
572c7679306SThierry Reding 	return &plane->base;
573c7679306SThierry Reding }
574c7679306SThierry Reding 
575c7679306SThierry Reding static const u32 tegra_cursor_plane_formats[] = {
576c7679306SThierry Reding 	DRM_FORMAT_RGBA8888,
577c7679306SThierry Reding };
578c7679306SThierry Reding 
5794aa3df71SThierry Reding static int tegra_cursor_atomic_check(struct drm_plane *plane,
5804aa3df71SThierry Reding 				     struct drm_plane_state *state)
581c7679306SThierry Reding {
58247802b09SThierry Reding 	struct tegra_plane *tegra = to_tegra_plane(plane);
58347802b09SThierry Reding 	int err;
58447802b09SThierry Reding 
5854aa3df71SThierry Reding 	/* no need for further checks if the plane is being disabled */
5864aa3df71SThierry Reding 	if (!state->crtc)
5874aa3df71SThierry Reding 		return 0;
588c7679306SThierry Reding 
589c7679306SThierry Reding 	/* scaling not supported for cursor */
5904aa3df71SThierry Reding 	if ((state->src_w >> 16 != state->crtc_w) ||
5914aa3df71SThierry Reding 	    (state->src_h >> 16 != state->crtc_h))
592c7679306SThierry Reding 		return -EINVAL;
593c7679306SThierry Reding 
594c7679306SThierry Reding 	/* only square cursors supported */
5954aa3df71SThierry Reding 	if (state->src_w != state->src_h)
596c7679306SThierry Reding 		return -EINVAL;
597c7679306SThierry Reding 
5984aa3df71SThierry Reding 	if (state->crtc_w != 32 && state->crtc_w != 64 &&
5994aa3df71SThierry Reding 	    state->crtc_w != 128 && state->crtc_w != 256)
6004aa3df71SThierry Reding 		return -EINVAL;
6014aa3df71SThierry Reding 
60247802b09SThierry Reding 	err = tegra_plane_state_add(tegra, state);
60347802b09SThierry Reding 	if (err < 0)
60447802b09SThierry Reding 		return err;
60547802b09SThierry Reding 
6064aa3df71SThierry Reding 	return 0;
6074aa3df71SThierry Reding }
6084aa3df71SThierry Reding 
6094aa3df71SThierry Reding static void tegra_cursor_atomic_update(struct drm_plane *plane,
6104aa3df71SThierry Reding 				       struct drm_plane_state *old_state)
6114aa3df71SThierry Reding {
6124aa3df71SThierry Reding 	struct tegra_bo *bo = tegra_fb_get_plane(plane->state->fb, 0);
6134aa3df71SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
6144aa3df71SThierry Reding 	struct drm_plane_state *state = plane->state;
6154aa3df71SThierry Reding 	u32 value = CURSOR_CLIP_DISPLAY;
6164aa3df71SThierry Reding 
6174aa3df71SThierry Reding 	/* rien ne va plus */
6184aa3df71SThierry Reding 	if (!plane->state->crtc || !plane->state->fb)
6194aa3df71SThierry Reding 		return;
6204aa3df71SThierry Reding 
6214aa3df71SThierry Reding 	switch (state->crtc_w) {
622c7679306SThierry Reding 	case 32:
623c7679306SThierry Reding 		value |= CURSOR_SIZE_32x32;
624c7679306SThierry Reding 		break;
625c7679306SThierry Reding 
626c7679306SThierry Reding 	case 64:
627c7679306SThierry Reding 		value |= CURSOR_SIZE_64x64;
628c7679306SThierry Reding 		break;
629c7679306SThierry Reding 
630c7679306SThierry Reding 	case 128:
631c7679306SThierry Reding 		value |= CURSOR_SIZE_128x128;
632c7679306SThierry Reding 		break;
633c7679306SThierry Reding 
634c7679306SThierry Reding 	case 256:
635c7679306SThierry Reding 		value |= CURSOR_SIZE_256x256;
636c7679306SThierry Reding 		break;
637c7679306SThierry Reding 
638c7679306SThierry Reding 	default:
6394aa3df71SThierry Reding 		WARN(1, "cursor size %ux%u not supported\n", state->crtc_w,
6404aa3df71SThierry Reding 		     state->crtc_h);
6414aa3df71SThierry Reding 		return;
642c7679306SThierry Reding 	}
643c7679306SThierry Reding 
644c7679306SThierry Reding 	value |= (bo->paddr >> 10) & 0x3fffff;
645c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR);
646c7679306SThierry Reding 
647c7679306SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
648c7679306SThierry Reding 	value = (bo->paddr >> 32) & 0x3;
649c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_START_ADDR_HI);
650c7679306SThierry Reding #endif
651c7679306SThierry Reding 
652c7679306SThierry Reding 	/* enable cursor and set blend mode */
653c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
654c7679306SThierry Reding 	value |= CURSOR_ENABLE;
655c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
656c7679306SThierry Reding 
657c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_BLEND_CURSOR_CONTROL);
658c7679306SThierry Reding 	value &= ~CURSOR_DST_BLEND_MASK;
659c7679306SThierry Reding 	value &= ~CURSOR_SRC_BLEND_MASK;
660c7679306SThierry Reding 	value |= CURSOR_MODE_NORMAL;
661c7679306SThierry Reding 	value |= CURSOR_DST_BLEND_NEG_K1_TIMES_SRC;
662c7679306SThierry Reding 	value |= CURSOR_SRC_BLEND_K1_TIMES_SRC;
663c7679306SThierry Reding 	value |= CURSOR_ALPHA;
664c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BLEND_CURSOR_CONTROL);
665c7679306SThierry Reding 
666c7679306SThierry Reding 	/* position the cursor */
6674aa3df71SThierry Reding 	value = (state->crtc_y & 0x3fff) << 16 | (state->crtc_x & 0x3fff);
668c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_CURSOR_POSITION);
669c7679306SThierry Reding }
670c7679306SThierry Reding 
6714aa3df71SThierry Reding static void tegra_cursor_atomic_disable(struct drm_plane *plane,
6724aa3df71SThierry Reding 					struct drm_plane_state *old_state)
673c7679306SThierry Reding {
6744aa3df71SThierry Reding 	struct tegra_dc *dc;
675c7679306SThierry Reding 	u32 value;
676c7679306SThierry Reding 
6774aa3df71SThierry Reding 	/* rien ne va plus */
6784aa3df71SThierry Reding 	if (!old_state || !old_state->crtc)
6794aa3df71SThierry Reding 		return;
6804aa3df71SThierry Reding 
6814aa3df71SThierry Reding 	dc = to_tegra_dc(old_state->crtc);
682c7679306SThierry Reding 
683c7679306SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_DISP_WIN_OPTIONS);
684c7679306SThierry Reding 	value &= ~CURSOR_ENABLE;
685c7679306SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_DISP_WIN_OPTIONS);
686c7679306SThierry Reding }
687c7679306SThierry Reding 
6884aa3df71SThierry Reding static const struct drm_plane_helper_funcs tegra_cursor_plane_helper_funcs = {
6894aa3df71SThierry Reding 	.atomic_check = tegra_cursor_atomic_check,
6904aa3df71SThierry Reding 	.atomic_update = tegra_cursor_atomic_update,
6914aa3df71SThierry Reding 	.atomic_disable = tegra_cursor_atomic_disable,
692c7679306SThierry Reding };
693c7679306SThierry Reding 
694c7679306SThierry Reding static struct drm_plane *tegra_dc_cursor_plane_create(struct drm_device *drm,
695c7679306SThierry Reding 						      struct tegra_dc *dc)
696c7679306SThierry Reding {
697c7679306SThierry Reding 	struct tegra_plane *plane;
698c7679306SThierry Reding 	unsigned int num_formats;
699c7679306SThierry Reding 	const u32 *formats;
700c7679306SThierry Reding 	int err;
701c7679306SThierry Reding 
702c7679306SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
703c7679306SThierry Reding 	if (!plane)
704c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
705c7679306SThierry Reding 
70647802b09SThierry Reding 	/*
707a1df3b24SThierry Reding 	 * This index is kind of fake. The cursor isn't a regular plane, but
708a1df3b24SThierry Reding 	 * its update and activation request bits in DC_CMD_STATE_CONTROL do
709a1df3b24SThierry Reding 	 * use the same programming. Setting this fake index here allows the
710a1df3b24SThierry Reding 	 * code in tegra_add_plane_state() to do the right thing without the
711a1df3b24SThierry Reding 	 * need to special-casing the cursor plane.
71247802b09SThierry Reding 	 */
71347802b09SThierry Reding 	plane->index = 6;
7141087fac1SThierry Reding 	plane->dc = dc;
71547802b09SThierry Reding 
716c7679306SThierry Reding 	num_formats = ARRAY_SIZE(tegra_cursor_plane_formats);
717c7679306SThierry Reding 	formats = tegra_cursor_plane_formats;
718c7679306SThierry Reding 
719c7679306SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
720c1cb4b61SThierry Reding 				       &tegra_plane_funcs, formats,
721e6fc3b68SBen Widawsky 				       num_formats, NULL,
722e6fc3b68SBen Widawsky 				       DRM_PLANE_TYPE_CURSOR, NULL);
723c7679306SThierry Reding 	if (err < 0) {
724c7679306SThierry Reding 		kfree(plane);
725c7679306SThierry Reding 		return ERR_PTR(err);
726c7679306SThierry Reding 	}
727c7679306SThierry Reding 
7284aa3df71SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_cursor_plane_helper_funcs);
7294aa3df71SThierry Reding 
730c7679306SThierry Reding 	return &plane->base;
731c7679306SThierry Reding }
732c7679306SThierry Reding 
733511c7023SThierry Reding static const u32 tegra20_overlay_formats[] = {
734511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
735511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
736dee8268fSThierry Reding 	DRM_FORMAT_RGB565,
737511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
738511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
739511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
740511c7023SThierry Reding 	/* planar formats */
741511c7023SThierry Reding 	DRM_FORMAT_UYVY,
742511c7023SThierry Reding 	DRM_FORMAT_YUYV,
743511c7023SThierry Reding 	DRM_FORMAT_YUV420,
744511c7023SThierry Reding 	DRM_FORMAT_YUV422,
745511c7023SThierry Reding };
746511c7023SThierry Reding 
747511c7023SThierry Reding static const u32 tegra114_overlay_formats[] = {
748511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
749511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
750511c7023SThierry Reding 	DRM_FORMAT_RGB565,
751511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
752511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
753511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
754511c7023SThierry Reding 	/* new on Tegra114 */
755511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
756511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
757511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
758511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
759511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
760511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
761511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
762511c7023SThierry Reding 	DRM_FORMAT_BGR565,
763511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
764511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
765511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
766511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
767511c7023SThierry Reding 	/* planar formats */
768511c7023SThierry Reding 	DRM_FORMAT_UYVY,
769511c7023SThierry Reding 	DRM_FORMAT_YUYV,
770511c7023SThierry Reding 	DRM_FORMAT_YUV420,
771511c7023SThierry Reding 	DRM_FORMAT_YUV422,
772511c7023SThierry Reding };
773511c7023SThierry Reding 
774511c7023SThierry Reding static const u32 tegra124_overlay_formats[] = {
775511c7023SThierry Reding 	DRM_FORMAT_ARGB4444,
776511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
777511c7023SThierry Reding 	DRM_FORMAT_RGB565,
778511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
779511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
780511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
781511c7023SThierry Reding 	/* new on Tegra114 */
782511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
783511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
784511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
785511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
786511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
787511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
788511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
789511c7023SThierry Reding 	DRM_FORMAT_BGR565,
790511c7023SThierry Reding 	DRM_FORMAT_BGRA8888,
791511c7023SThierry Reding 	DRM_FORMAT_RGBA8888,
792511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
793511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
794511c7023SThierry Reding 	/* new on Tegra124 */
795511c7023SThierry Reding 	DRM_FORMAT_RGBX8888,
796511c7023SThierry Reding 	DRM_FORMAT_BGRX8888,
797511c7023SThierry Reding 	/* planar formats */
798dee8268fSThierry Reding 	DRM_FORMAT_UYVY,
799f925390eSThierry Reding 	DRM_FORMAT_YUYV,
800dee8268fSThierry Reding 	DRM_FORMAT_YUV420,
801dee8268fSThierry Reding 	DRM_FORMAT_YUV422,
802dee8268fSThierry Reding };
803dee8268fSThierry Reding 
804c7679306SThierry Reding static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
805c7679306SThierry Reding 						       struct tegra_dc *dc,
806c7679306SThierry Reding 						       unsigned int index)
807dee8268fSThierry Reding {
808dee8268fSThierry Reding 	struct tegra_plane *plane;
809c7679306SThierry Reding 	unsigned int num_formats;
810c7679306SThierry Reding 	const u32 *formats;
811c7679306SThierry Reding 	int err;
812dee8268fSThierry Reding 
813f002abc1SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
814dee8268fSThierry Reding 	if (!plane)
815c7679306SThierry Reding 		return ERR_PTR(-ENOMEM);
816dee8268fSThierry Reding 
8171087fac1SThierry Reding 	plane->offset = 0xa00 + 0x200 * index;
818c7679306SThierry Reding 	plane->index = index;
8191087fac1SThierry Reding 	plane->dc = dc;
820dee8268fSThierry Reding 
821511c7023SThierry Reding 	num_formats = dc->soc->num_overlay_formats;
822511c7023SThierry Reding 	formats = dc->soc->overlay_formats;
823c7679306SThierry Reding 
824c7679306SThierry Reding 	err = drm_universal_plane_init(drm, &plane->base, 1 << dc->pipe,
825301e0ddbSThierry Reding 				       &tegra_plane_funcs, formats,
826e6fc3b68SBen Widawsky 				       num_formats, NULL,
827e6fc3b68SBen Widawsky 				       DRM_PLANE_TYPE_OVERLAY, NULL);
828f002abc1SThierry Reding 	if (err < 0) {
829f002abc1SThierry Reding 		kfree(plane);
830c7679306SThierry Reding 		return ERR_PTR(err);
831dee8268fSThierry Reding 	}
832c7679306SThierry Reding 
833a4bfa096SThierry Reding 	drm_plane_helper_add(&plane->base, &tegra_plane_helper_funcs);
8344aa3df71SThierry Reding 
835ab7d3f58SThierry Reding 	if (dc->soc->supports_blending)
836ab7d3f58SThierry Reding 		drm_plane_create_zpos_property(&plane->base, 0, 0, 255);
837ab7d3f58SThierry Reding 
838c7679306SThierry Reding 	return &plane->base;
839c7679306SThierry Reding }
840c7679306SThierry Reding 
84147307954SThierry Reding static struct drm_plane *tegra_dc_add_shared_planes(struct drm_device *drm,
84247307954SThierry Reding 						    struct tegra_dc *dc)
843c7679306SThierry Reding {
84447307954SThierry Reding 	struct drm_plane *plane, *primary = NULL;
84547307954SThierry Reding 	unsigned int i, j;
84647307954SThierry Reding 
84747307954SThierry Reding 	for (i = 0; i < dc->soc->num_wgrps; i++) {
84847307954SThierry Reding 		const struct tegra_windowgroup_soc *wgrp = &dc->soc->wgrps[i];
84947307954SThierry Reding 
85047307954SThierry Reding 		if (wgrp->dc == dc->pipe) {
85147307954SThierry Reding 			for (j = 0; j < wgrp->num_windows; j++) {
85247307954SThierry Reding 				unsigned int index = wgrp->windows[j];
85347307954SThierry Reding 
85447307954SThierry Reding 				plane = tegra_shared_plane_create(drm, dc,
85547307954SThierry Reding 								  wgrp->index,
85647307954SThierry Reding 								  index);
85747307954SThierry Reding 				if (IS_ERR(plane))
85847307954SThierry Reding 					return plane;
85947307954SThierry Reding 
86047307954SThierry Reding 				/*
86147307954SThierry Reding 				 * Choose the first shared plane owned by this
86247307954SThierry Reding 				 * head as the primary plane.
86347307954SThierry Reding 				 */
86447307954SThierry Reding 				if (!primary) {
86547307954SThierry Reding 					plane->type = DRM_PLANE_TYPE_PRIMARY;
86647307954SThierry Reding 					primary = plane;
86747307954SThierry Reding 				}
86847307954SThierry Reding 			}
86947307954SThierry Reding 		}
87047307954SThierry Reding 	}
87147307954SThierry Reding 
87247307954SThierry Reding 	return primary;
87347307954SThierry Reding }
87447307954SThierry Reding 
87547307954SThierry Reding static struct drm_plane *tegra_dc_add_planes(struct drm_device *drm,
87647307954SThierry Reding 					     struct tegra_dc *dc)
87747307954SThierry Reding {
87847307954SThierry Reding 	struct drm_plane *plane, *primary;
879c7679306SThierry Reding 	unsigned int i;
880c7679306SThierry Reding 
88147307954SThierry Reding 	primary = tegra_primary_plane_create(drm, dc);
88247307954SThierry Reding 	if (IS_ERR(primary))
88347307954SThierry Reding 		return primary;
88447307954SThierry Reding 
885c7679306SThierry Reding 	for (i = 0; i < 2; i++) {
886c7679306SThierry Reding 		plane = tegra_dc_overlay_plane_create(drm, dc, 1 + i);
88747307954SThierry Reding 		if (IS_ERR(plane)) {
88847307954SThierry Reding 			/* XXX tegra_plane_destroy() */
88947307954SThierry Reding 			drm_plane_cleanup(primary);
89047307954SThierry Reding 			kfree(primary);
89147307954SThierry Reding 			return plane;
89247307954SThierry Reding 		}
893f002abc1SThierry Reding 	}
894dee8268fSThierry Reding 
89547307954SThierry Reding 	return primary;
896dee8268fSThierry Reding }
897dee8268fSThierry Reding 
898f002abc1SThierry Reding static void tegra_dc_destroy(struct drm_crtc *crtc)
899f002abc1SThierry Reding {
900f002abc1SThierry Reding 	drm_crtc_cleanup(crtc);
901f002abc1SThierry Reding }
902f002abc1SThierry Reding 
903ca915b10SThierry Reding static void tegra_crtc_reset(struct drm_crtc *crtc)
904ca915b10SThierry Reding {
905ca915b10SThierry Reding 	struct tegra_dc_state *state;
906ca915b10SThierry Reding 
9073b59b7acSThierry Reding 	if (crtc->state)
908ec2dc6a0SDaniel Vetter 		__drm_atomic_helper_crtc_destroy_state(crtc->state);
9093b59b7acSThierry Reding 
910ca915b10SThierry Reding 	kfree(crtc->state);
911ca915b10SThierry Reding 	crtc->state = NULL;
912ca915b10SThierry Reding 
913ca915b10SThierry Reding 	state = kzalloc(sizeof(*state), GFP_KERNEL);
914332bbe70SThierry Reding 	if (state) {
915ca915b10SThierry Reding 		crtc->state = &state->base;
916332bbe70SThierry Reding 		crtc->state->crtc = crtc;
917332bbe70SThierry Reding 	}
91831930d4dSThierry Reding 
91931930d4dSThierry Reding 	drm_crtc_vblank_reset(crtc);
920ca915b10SThierry Reding }
921ca915b10SThierry Reding 
922ca915b10SThierry Reding static struct drm_crtc_state *
923ca915b10SThierry Reding tegra_crtc_atomic_duplicate_state(struct drm_crtc *crtc)
924ca915b10SThierry Reding {
925ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc->state);
926ca915b10SThierry Reding 	struct tegra_dc_state *copy;
927ca915b10SThierry Reding 
9283b59b7acSThierry Reding 	copy = kmalloc(sizeof(*copy), GFP_KERNEL);
929ca915b10SThierry Reding 	if (!copy)
930ca915b10SThierry Reding 		return NULL;
931ca915b10SThierry Reding 
9323b59b7acSThierry Reding 	__drm_atomic_helper_crtc_duplicate_state(crtc, &copy->base);
9333b59b7acSThierry Reding 	copy->clk = state->clk;
9343b59b7acSThierry Reding 	copy->pclk = state->pclk;
9353b59b7acSThierry Reding 	copy->div = state->div;
9363b59b7acSThierry Reding 	copy->planes = state->planes;
937ca915b10SThierry Reding 
938ca915b10SThierry Reding 	return &copy->base;
939ca915b10SThierry Reding }
940ca915b10SThierry Reding 
941ca915b10SThierry Reding static void tegra_crtc_atomic_destroy_state(struct drm_crtc *crtc,
942ca915b10SThierry Reding 					    struct drm_crtc_state *state)
943ca915b10SThierry Reding {
944ec2dc6a0SDaniel Vetter 	__drm_atomic_helper_crtc_destroy_state(state);
945ca915b10SThierry Reding 	kfree(state);
946ca915b10SThierry Reding }
947ca915b10SThierry Reding 
948b95800eeSThierry Reding #define DEBUGFS_REG32(_name) { .name = #_name, .offset = _name }
949b95800eeSThierry Reding 
950b95800eeSThierry Reding static const struct debugfs_reg32 tegra_dc_regs[] = {
951b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT),
952b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_CNTRL),
953b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_GENERAL_INCR_SYNCPT_ERROR),
954b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT),
955b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_CNTRL),
956b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_A_INCR_SYNCPT_ERROR),
957b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT),
958b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_CNTRL),
959b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_B_INCR_SYNCPT_ERROR),
960b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT),
961b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_CNTRL),
962b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_WIN_C_INCR_SYNCPT_ERROR),
963b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_CONT_SYNCPT_VSYNC),
964b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND_OPTION0),
965b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_COMMAND),
966b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE),
967b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_POWER_CONTROL),
968b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_STATUS),
969b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_MASK),
970b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_ENABLE),
971b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_TYPE),
972b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_INT_POLARITY),
973b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE1),
974b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE2),
975b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_SIGNAL_RAISE3),
976b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_ACCESS),
977b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_STATE_CONTROL),
978b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_DISPLAY_WINDOW_HEADER),
979b95800eeSThierry Reding 	DEBUGFS_REG32(DC_CMD_REG_ACT_CONTROL),
980b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CONTROL),
981b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM),
982b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(0)),
983b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(1)),
984b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(2)),
985b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_ENABLE(3)),
986b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(0)),
987b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(1)),
988b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(2)),
989b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_POLARITY(3)),
990b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(0)),
991b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(1)),
992b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(2)),
993b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_DATA(3)),
994b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(0)),
995b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(1)),
996b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(2)),
997b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_ENABLE(3)),
998b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(0)),
999b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_INPUT_DATA(1)),
1000b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(0)),
1001b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(1)),
1002b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(2)),
1003b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(3)),
1004b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(4)),
1005b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(5)),
1006b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_OUTPUT_SELECT(6)),
1007b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_MISC_CONTROL),
1008b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_CONTROL),
1009b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM0_DUTY_CYCLE),
1010b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_CONTROL),
1011b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_PIN_PM1_DUTY_CYCLE),
1012b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_CONTROL),
1013b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SPI_START_BYTE),
1014b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_AB),
1015b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_WRITE_DATA_CD),
1016b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_HSPI_CS_DC),
1017b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_A),
1018b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_SCRATCH_REGISTER_B),
1019b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_CTRL),
1020b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_GPIO_DEBOUNCE_COUNTER),
1021b95800eeSThierry Reding 	DEBUGFS_REG32(DC_COM_CRC_CHECKSUM_LATCHED),
1022b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS0),
1023b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_SIGNAL_OPTIONS1),
1024b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_WIN_OPTIONS),
1025b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY),
1026b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER),
1027b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_TIMING_OPTIONS),
1028b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_REF_TO_SYNC),
1029b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SYNC_WIDTH),
1030b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BACK_PORCH),
1031b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_ACTIVE),
1032b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_FRONT_PORCH),
1033b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_CONTROL),
1034b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_A),
1035b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_B),
1036b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_C),
1037b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE0_POSITION_D),
1038b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_CONTROL),
1039b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_A),
1040b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_B),
1041b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_C),
1042b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE1_POSITION_D),
1043b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_CONTROL),
1044b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_A),
1045b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_B),
1046b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_C),
1047b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_H_PULSE2_POSITION_D),
1048b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_CONTROL),
1049b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_A),
1050b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_B),
1051b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE0_POSITION_C),
1052b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_CONTROL),
1053b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_A),
1054b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_B),
1055b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE1_POSITION_C),
1056b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_CONTROL),
1057b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE2_POSITION_A),
1058b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_CONTROL),
1059b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_V_PULSE3_POSITION_A),
1060b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M0_CONTROL),
1061b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_M1_CONTROL),
1062b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DI_CONTROL),
1063b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_CONTROL),
1064b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_A),
1065b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_B),
1066b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_C),
1067b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_PP_SELECT_D),
1068b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_CLOCK_CONTROL),
1069b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_INTERFACE_CONTROL),
1070b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_COLOR_CONTROL),
1071b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SHIFT_CLOCK_OPTIONS),
1072b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DATA_ENABLE_OPTIONS),
1073b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SERIAL_INTERFACE_OPTIONS),
1074b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_LCD_SPI_OPTIONS),
1075b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BORDER_COLOR),
1076b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_LOWER),
1077b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY0_UPPER),
1078b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_LOWER),
1079b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_COLOR_KEY1_UPPER),
1080b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_FOREGROUND),
1081b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_BACKGROUND),
1082b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR),
1083b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_NS),
1084b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION),
1085b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_POSITION_NS),
1086b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_INIT_SEQ_CONTROL),
1087b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_A),
1088b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_B),
1089b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_C),
1090b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SPI_INIT_SEQ_DATA_D),
1091b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_MCCIF_FIFOCTRL),
1092b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0A_HYST),
1093b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY0B_HYST),
1094b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1A_HYST),
1095b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_MCCIF_DISPLAY1B_HYST),
1096b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DAC_CRT_CTRL),
1097b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DISP_MISC_CONTROL),
1098b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CONTROL),
1099b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_CSC_COEFF),
1100b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(0)),
1101b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(1)),
1102b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(2)),
1103b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(3)),
1104b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(4)),
1105b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(5)),
1106b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(6)),
1107b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(7)),
1108b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_LUT(8)),
1109b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_FLICKER_CONTROL),
1110b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_DC_PIXEL_COUNT),
1111b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(0)),
1112b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(1)),
1113b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(2)),
1114b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(3)),
1115b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(4)),
1116b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(5)),
1117b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(6)),
1118b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HISTOGRAM(7)),
1119b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(0)),
1120b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(1)),
1121b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(2)),
1122b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_TF(3)),
1123b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_BL_CONTROL),
1124b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_HW_K_VALUES),
1125b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_SD_MAN_K_VALUES),
1126b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_CURSOR_START_ADDR_HI),
1127b95800eeSThierry Reding 	DEBUGFS_REG32(DC_DISP_BLEND_CURSOR_CONTROL),
1128b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_WIN_OPTIONS),
1129b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BYTE_SWAP),
1130b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_CONTROL),
1131b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_COLOR_DEPTH),
1132b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_POSITION),
1133b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_SIZE),
1134b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_PRESCALED_SIZE),
1135b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_H_INITIAL_DDA),
1136b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_V_INITIAL_DDA),
1137b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DDA_INC),
1138b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_LINE_STRIDE),
1139b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUF_STRIDE),
1140b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_UV_BUF_STRIDE),
1141b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BUFFER_ADDR_MODE),
1142b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_DV_CONTROL),
1143b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_NOKEY),
1144b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_1WIN),
1145b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_X),
1146b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_2WIN_Y),
1147b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_BLEND_3WIN_XY),
1148b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WIN_HP_FETCH_CONTROL),
1149b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR),
1150b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_NS),
1151b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U),
1152b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_U_NS),
1153b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V),
1154b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_START_ADDR_V_NS),
1155b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET),
1156b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_H_OFFSET_NS),
1157b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET),
1158b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_ADDR_V_OFFSET_NS),
1159b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_UFLOW_STATUS),
1160b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_AD_UFLOW_STATUS),
1161b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_BD_UFLOW_STATUS),
1162b95800eeSThierry Reding 	DEBUGFS_REG32(DC_WINBUF_CD_UFLOW_STATUS),
1163b95800eeSThierry Reding };
1164b95800eeSThierry Reding 
1165b95800eeSThierry Reding static int tegra_dc_show_regs(struct seq_file *s, void *data)
1166b95800eeSThierry Reding {
1167b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1168b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1169b95800eeSThierry Reding 	unsigned int i;
1170b95800eeSThierry Reding 	int err = 0;
1171b95800eeSThierry Reding 
1172b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1173b95800eeSThierry Reding 
1174b95800eeSThierry Reding 	if (!dc->base.state->active) {
1175b95800eeSThierry Reding 		err = -EBUSY;
1176b95800eeSThierry Reding 		goto unlock;
1177b95800eeSThierry Reding 	}
1178b95800eeSThierry Reding 
1179b95800eeSThierry Reding 	for (i = 0; i < ARRAY_SIZE(tegra_dc_regs); i++) {
1180b95800eeSThierry Reding 		unsigned int offset = tegra_dc_regs[i].offset;
1181b95800eeSThierry Reding 
1182b95800eeSThierry Reding 		seq_printf(s, "%-40s %#05x %08x\n", tegra_dc_regs[i].name,
1183b95800eeSThierry Reding 			   offset, tegra_dc_readl(dc, offset));
1184b95800eeSThierry Reding 	}
1185b95800eeSThierry Reding 
1186b95800eeSThierry Reding unlock:
1187b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1188b95800eeSThierry Reding 	return err;
1189b95800eeSThierry Reding }
1190b95800eeSThierry Reding 
1191b95800eeSThierry Reding static int tegra_dc_show_crc(struct seq_file *s, void *data)
1192b95800eeSThierry Reding {
1193b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1194b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1195b95800eeSThierry Reding 	int err = 0;
1196b95800eeSThierry Reding 	u32 value;
1197b95800eeSThierry Reding 
1198b95800eeSThierry Reding 	drm_modeset_lock(&dc->base.mutex, NULL);
1199b95800eeSThierry Reding 
1200b95800eeSThierry Reding 	if (!dc->base.state->active) {
1201b95800eeSThierry Reding 		err = -EBUSY;
1202b95800eeSThierry Reding 		goto unlock;
1203b95800eeSThierry Reding 	}
1204b95800eeSThierry Reding 
1205b95800eeSThierry Reding 	value = DC_COM_CRC_CONTROL_ACTIVE_DATA | DC_COM_CRC_CONTROL_ENABLE;
1206b95800eeSThierry Reding 	tegra_dc_writel(dc, value, DC_COM_CRC_CONTROL);
1207b95800eeSThierry Reding 	tegra_dc_commit(dc);
1208b95800eeSThierry Reding 
1209b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1210b95800eeSThierry Reding 	drm_crtc_wait_one_vblank(&dc->base);
1211b95800eeSThierry Reding 
1212b95800eeSThierry Reding 	value = tegra_dc_readl(dc, DC_COM_CRC_CHECKSUM);
1213b95800eeSThierry Reding 	seq_printf(s, "%08x\n", value);
1214b95800eeSThierry Reding 
1215b95800eeSThierry Reding 	tegra_dc_writel(dc, 0, DC_COM_CRC_CONTROL);
1216b95800eeSThierry Reding 
1217b95800eeSThierry Reding unlock:
1218b95800eeSThierry Reding 	drm_modeset_unlock(&dc->base.mutex);
1219b95800eeSThierry Reding 	return err;
1220b95800eeSThierry Reding }
1221b95800eeSThierry Reding 
1222b95800eeSThierry Reding static int tegra_dc_show_stats(struct seq_file *s, void *data)
1223b95800eeSThierry Reding {
1224b95800eeSThierry Reding 	struct drm_info_node *node = s->private;
1225b95800eeSThierry Reding 	struct tegra_dc *dc = node->info_ent->data;
1226b95800eeSThierry Reding 
1227b95800eeSThierry Reding 	seq_printf(s, "frames: %lu\n", dc->stats.frames);
1228b95800eeSThierry Reding 	seq_printf(s, "vblank: %lu\n", dc->stats.vblank);
1229b95800eeSThierry Reding 	seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
1230b95800eeSThierry Reding 	seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
1231b95800eeSThierry Reding 
1232b95800eeSThierry Reding 	return 0;
1233b95800eeSThierry Reding }
1234b95800eeSThierry Reding 
1235b95800eeSThierry Reding static struct drm_info_list debugfs_files[] = {
1236b95800eeSThierry Reding 	{ "regs", tegra_dc_show_regs, 0, NULL },
1237b95800eeSThierry Reding 	{ "crc", tegra_dc_show_crc, 0, NULL },
1238b95800eeSThierry Reding 	{ "stats", tegra_dc_show_stats, 0, NULL },
1239b95800eeSThierry Reding };
1240b95800eeSThierry Reding 
1241b95800eeSThierry Reding static int tegra_dc_late_register(struct drm_crtc *crtc)
1242b95800eeSThierry Reding {
1243b95800eeSThierry Reding 	unsigned int i, count = ARRAY_SIZE(debugfs_files);
1244b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
1245b95800eeSThierry Reding 	struct dentry *root = crtc->debugfs_entry;
1246b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1247b95800eeSThierry Reding 	int err;
1248b95800eeSThierry Reding 
1249b95800eeSThierry Reding 	dc->debugfs_files = kmemdup(debugfs_files, sizeof(debugfs_files),
1250b95800eeSThierry Reding 				    GFP_KERNEL);
1251b95800eeSThierry Reding 	if (!dc->debugfs_files)
1252b95800eeSThierry Reding 		return -ENOMEM;
1253b95800eeSThierry Reding 
1254b95800eeSThierry Reding 	for (i = 0; i < count; i++)
1255b95800eeSThierry Reding 		dc->debugfs_files[i].data = dc;
1256b95800eeSThierry Reding 
1257b95800eeSThierry Reding 	err = drm_debugfs_create_files(dc->debugfs_files, count, root, minor);
1258b95800eeSThierry Reding 	if (err < 0)
1259b95800eeSThierry Reding 		goto free;
1260b95800eeSThierry Reding 
1261b95800eeSThierry Reding 	return 0;
1262b95800eeSThierry Reding 
1263b95800eeSThierry Reding free:
1264b95800eeSThierry Reding 	kfree(dc->debugfs_files);
1265b95800eeSThierry Reding 	dc->debugfs_files = NULL;
1266b95800eeSThierry Reding 
1267b95800eeSThierry Reding 	return err;
1268b95800eeSThierry Reding }
1269b95800eeSThierry Reding 
1270b95800eeSThierry Reding static void tegra_dc_early_unregister(struct drm_crtc *crtc)
1271b95800eeSThierry Reding {
1272b95800eeSThierry Reding 	unsigned int count = ARRAY_SIZE(debugfs_files);
1273b95800eeSThierry Reding 	struct drm_minor *minor = crtc->dev->primary;
1274b95800eeSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1275b95800eeSThierry Reding 
1276b95800eeSThierry Reding 	drm_debugfs_remove_files(dc->debugfs_files, count, minor);
1277b95800eeSThierry Reding 	kfree(dc->debugfs_files);
1278b95800eeSThierry Reding 	dc->debugfs_files = NULL;
1279b95800eeSThierry Reding }
1280b95800eeSThierry Reding 
1281c49c81e2SThierry Reding static u32 tegra_dc_get_vblank_counter(struct drm_crtc *crtc)
1282c49c81e2SThierry Reding {
1283c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1284c49c81e2SThierry Reding 
128547307954SThierry Reding 	/* XXX vblank syncpoints don't work with nvdisplay yet */
128647307954SThierry Reding 	if (dc->syncpt && !dc->soc->has_nvdisplay)
1287c49c81e2SThierry Reding 		return host1x_syncpt_read(dc->syncpt);
1288c49c81e2SThierry Reding 
1289c49c81e2SThierry Reding 	/* fallback to software emulated VBLANK counter */
1290c49c81e2SThierry Reding 	return drm_crtc_vblank_count(&dc->base);
1291c49c81e2SThierry Reding }
1292c49c81e2SThierry Reding 
1293c49c81e2SThierry Reding static int tegra_dc_enable_vblank(struct drm_crtc *crtc)
1294c49c81e2SThierry Reding {
1295c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1296363541e8SThierry Reding 	u32 value;
1297c49c81e2SThierry Reding 
1298c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1299c49c81e2SThierry Reding 	value |= VBLANK_INT;
1300c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1301c49c81e2SThierry Reding 
1302c49c81e2SThierry Reding 	return 0;
1303c49c81e2SThierry Reding }
1304c49c81e2SThierry Reding 
1305c49c81e2SThierry Reding static void tegra_dc_disable_vblank(struct drm_crtc *crtc)
1306c49c81e2SThierry Reding {
1307c49c81e2SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1308363541e8SThierry Reding 	u32 value;
1309c49c81e2SThierry Reding 
1310c49c81e2SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_INT_MASK);
1311c49c81e2SThierry Reding 	value &= ~VBLANK_INT;
1312c49c81e2SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
1313c49c81e2SThierry Reding }
1314c49c81e2SThierry Reding 
1315dee8268fSThierry Reding static const struct drm_crtc_funcs tegra_crtc_funcs = {
13161503ca47SThierry Reding 	.page_flip = drm_atomic_helper_page_flip,
131774f48791SThierry Reding 	.set_config = drm_atomic_helper_set_config,
1318f002abc1SThierry Reding 	.destroy = tegra_dc_destroy,
1319ca915b10SThierry Reding 	.reset = tegra_crtc_reset,
1320ca915b10SThierry Reding 	.atomic_duplicate_state = tegra_crtc_atomic_duplicate_state,
1321ca915b10SThierry Reding 	.atomic_destroy_state = tegra_crtc_atomic_destroy_state,
1322b95800eeSThierry Reding 	.late_register = tegra_dc_late_register,
1323b95800eeSThierry Reding 	.early_unregister = tegra_dc_early_unregister,
132410437d9bSShawn Guo 	.get_vblank_counter = tegra_dc_get_vblank_counter,
132510437d9bSShawn Guo 	.enable_vblank = tegra_dc_enable_vblank,
132610437d9bSShawn Guo 	.disable_vblank = tegra_dc_disable_vblank,
1327dee8268fSThierry Reding };
1328dee8268fSThierry Reding 
1329dee8268fSThierry Reding static int tegra_dc_set_timings(struct tegra_dc *dc,
1330dee8268fSThierry Reding 				struct drm_display_mode *mode)
1331dee8268fSThierry Reding {
13320444c0ffSThierry Reding 	unsigned int h_ref_to_sync = 1;
13330444c0ffSThierry Reding 	unsigned int v_ref_to_sync = 1;
1334dee8268fSThierry Reding 	unsigned long value;
1335dee8268fSThierry Reding 
133647307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
1337dee8268fSThierry Reding 		tegra_dc_writel(dc, 0x0, DC_DISP_DISP_TIMING_OPTIONS);
1338dee8268fSThierry Reding 
1339dee8268fSThierry Reding 		value = (v_ref_to_sync << 16) | h_ref_to_sync;
1340dee8268fSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_REF_TO_SYNC);
134147307954SThierry Reding 	}
1342dee8268fSThierry Reding 
1343dee8268fSThierry Reding 	value = ((mode->vsync_end - mode->vsync_start) << 16) |
1344dee8268fSThierry Reding 		((mode->hsync_end - mode->hsync_start) <<  0);
1345dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_SYNC_WIDTH);
1346dee8268fSThierry Reding 
1347dee8268fSThierry Reding 	value = ((mode->vtotal - mode->vsync_end) << 16) |
1348dee8268fSThierry Reding 		((mode->htotal - mode->hsync_end) <<  0);
1349dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_BACK_PORCH);
1350dee8268fSThierry Reding 
1351dee8268fSThierry Reding 	value = ((mode->vsync_start - mode->vdisplay) << 16) |
1352dee8268fSThierry Reding 		((mode->hsync_start - mode->hdisplay) <<  0);
1353dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_FRONT_PORCH);
1354dee8268fSThierry Reding 
1355dee8268fSThierry Reding 	value = (mode->vdisplay << 16) | mode->hdisplay;
1356dee8268fSThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_ACTIVE);
1357dee8268fSThierry Reding 
1358dee8268fSThierry Reding 	return 0;
1359dee8268fSThierry Reding }
1360dee8268fSThierry Reding 
13619d910b60SThierry Reding /**
13629d910b60SThierry Reding  * tegra_dc_state_setup_clock - check clock settings and store them in atomic
13639d910b60SThierry Reding  *     state
13649d910b60SThierry Reding  * @dc: display controller
13659d910b60SThierry Reding  * @crtc_state: CRTC atomic state
13669d910b60SThierry Reding  * @clk: parent clock for display controller
13679d910b60SThierry Reding  * @pclk: pixel clock
13689d910b60SThierry Reding  * @div: shift clock divider
13699d910b60SThierry Reding  *
13709d910b60SThierry Reding  * Returns:
13719d910b60SThierry Reding  * 0 on success or a negative error-code on failure.
13729d910b60SThierry Reding  */
1373ca915b10SThierry Reding int tegra_dc_state_setup_clock(struct tegra_dc *dc,
1374ca915b10SThierry Reding 			       struct drm_crtc_state *crtc_state,
1375ca915b10SThierry Reding 			       struct clk *clk, unsigned long pclk,
1376ca915b10SThierry Reding 			       unsigned int div)
1377ca915b10SThierry Reding {
1378ca915b10SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc_state);
1379ca915b10SThierry Reding 
1380d2982748SThierry Reding 	if (!clk_has_parent(dc->clk, clk))
1381d2982748SThierry Reding 		return -EINVAL;
1382d2982748SThierry Reding 
1383ca915b10SThierry Reding 	state->clk = clk;
1384ca915b10SThierry Reding 	state->pclk = pclk;
1385ca915b10SThierry Reding 	state->div = div;
1386ca915b10SThierry Reding 
1387ca915b10SThierry Reding 	return 0;
1388ca915b10SThierry Reding }
1389ca915b10SThierry Reding 
139076d59ed0SThierry Reding static void tegra_dc_commit_state(struct tegra_dc *dc,
139176d59ed0SThierry Reding 				  struct tegra_dc_state *state)
139276d59ed0SThierry Reding {
139376d59ed0SThierry Reding 	u32 value;
139476d59ed0SThierry Reding 	int err;
139576d59ed0SThierry Reding 
139676d59ed0SThierry Reding 	err = clk_set_parent(dc->clk, state->clk);
139776d59ed0SThierry Reding 	if (err < 0)
139876d59ed0SThierry Reding 		dev_err(dc->dev, "failed to set parent clock: %d\n", err);
139976d59ed0SThierry Reding 
140076d59ed0SThierry Reding 	/*
140176d59ed0SThierry Reding 	 * Outputs may not want to change the parent clock rate. This is only
140276d59ed0SThierry Reding 	 * relevant to Tegra20 where only a single display PLL is available.
140376d59ed0SThierry Reding 	 * Since that PLL would typically be used for HDMI, an internal LVDS
140476d59ed0SThierry Reding 	 * panel would need to be driven by some other clock such as PLL_P
140576d59ed0SThierry Reding 	 * which is shared with other peripherals. Changing the clock rate
140676d59ed0SThierry Reding 	 * should therefore be avoided.
140776d59ed0SThierry Reding 	 */
140876d59ed0SThierry Reding 	if (state->pclk > 0) {
140976d59ed0SThierry Reding 		err = clk_set_rate(state->clk, state->pclk);
141076d59ed0SThierry Reding 		if (err < 0)
141176d59ed0SThierry Reding 			dev_err(dc->dev,
141276d59ed0SThierry Reding 				"failed to set clock rate to %lu Hz\n",
141376d59ed0SThierry Reding 				state->pclk);
141476d59ed0SThierry Reding 	}
141576d59ed0SThierry Reding 
141676d59ed0SThierry Reding 	DRM_DEBUG_KMS("rate: %lu, div: %u\n", clk_get_rate(dc->clk),
141776d59ed0SThierry Reding 		      state->div);
141876d59ed0SThierry Reding 	DRM_DEBUG_KMS("pclk: %lu\n", state->pclk);
141976d59ed0SThierry Reding 
142047307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
142176d59ed0SThierry Reding 		value = SHIFT_CLK_DIVIDER(state->div) | PIXEL_CLK_DIVIDER_PCD1;
142276d59ed0SThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_CLOCK_CONTROL);
142347307954SThierry Reding 	}
142439e08affSThierry Reding 
142539e08affSThierry Reding 	err = clk_set_rate(dc->clk, state->pclk);
142639e08affSThierry Reding 	if (err < 0)
142739e08affSThierry Reding 		dev_err(dc->dev, "failed to set clock %pC to %lu Hz: %d\n",
142839e08affSThierry Reding 			dc->clk, state->pclk, err);
142976d59ed0SThierry Reding }
143076d59ed0SThierry Reding 
1431003fc848SThierry Reding static void tegra_dc_stop(struct tegra_dc *dc)
1432003fc848SThierry Reding {
1433003fc848SThierry Reding 	u32 value;
1434003fc848SThierry Reding 
1435003fc848SThierry Reding 	/* stop the display controller */
1436003fc848SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
1437003fc848SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
1438003fc848SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
1439003fc848SThierry Reding 
1440003fc848SThierry Reding 	tegra_dc_commit(dc);
1441003fc848SThierry Reding }
1442003fc848SThierry Reding 
1443003fc848SThierry Reding static bool tegra_dc_idle(struct tegra_dc *dc)
1444003fc848SThierry Reding {
1445003fc848SThierry Reding 	u32 value;
1446003fc848SThierry Reding 
1447003fc848SThierry Reding 	value = tegra_dc_readl_active(dc, DC_CMD_DISPLAY_COMMAND);
1448003fc848SThierry Reding 
1449003fc848SThierry Reding 	return (value & DISP_CTRL_MODE_MASK) == 0;
1450003fc848SThierry Reding }
1451003fc848SThierry Reding 
1452003fc848SThierry Reding static int tegra_dc_wait_idle(struct tegra_dc *dc, unsigned long timeout)
1453003fc848SThierry Reding {
1454003fc848SThierry Reding 	timeout = jiffies + msecs_to_jiffies(timeout);
1455003fc848SThierry Reding 
1456003fc848SThierry Reding 	while (time_before(jiffies, timeout)) {
1457003fc848SThierry Reding 		if (tegra_dc_idle(dc))
1458003fc848SThierry Reding 			return 0;
1459003fc848SThierry Reding 
1460003fc848SThierry Reding 		usleep_range(1000, 2000);
1461003fc848SThierry Reding 	}
1462003fc848SThierry Reding 
1463003fc848SThierry Reding 	dev_dbg(dc->dev, "timeout waiting for DC to become idle\n");
1464003fc848SThierry Reding 	return -ETIMEDOUT;
1465003fc848SThierry Reding }
1466003fc848SThierry Reding 
146764581714SLaurent Pinchart static void tegra_crtc_atomic_disable(struct drm_crtc *crtc,
146864581714SLaurent Pinchart 				      struct drm_crtc_state *old_state)
1469003fc848SThierry Reding {
1470003fc848SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1471003fc848SThierry Reding 	u32 value;
1472003fc848SThierry Reding 
1473003fc848SThierry Reding 	if (!tegra_dc_idle(dc)) {
1474003fc848SThierry Reding 		tegra_dc_stop(dc);
1475003fc848SThierry Reding 
1476003fc848SThierry Reding 		/*
1477003fc848SThierry Reding 		 * Ignore the return value, there isn't anything useful to do
1478003fc848SThierry Reding 		 * in case this fails.
1479003fc848SThierry Reding 		 */
1480003fc848SThierry Reding 		tegra_dc_wait_idle(dc, 100);
1481003fc848SThierry Reding 	}
1482003fc848SThierry Reding 
1483003fc848SThierry Reding 	/*
1484003fc848SThierry Reding 	 * This should really be part of the RGB encoder driver, but clearing
1485003fc848SThierry Reding 	 * these bits has the side-effect of stopping the display controller.
1486003fc848SThierry Reding 	 * When that happens no VBLANK interrupts will be raised. At the same
1487003fc848SThierry Reding 	 * time the encoder is disabled before the display controller, so the
1488003fc848SThierry Reding 	 * above code is always going to timeout waiting for the controller
1489003fc848SThierry Reding 	 * to go idle.
1490003fc848SThierry Reding 	 *
1491003fc848SThierry Reding 	 * Given the close coupling between the RGB encoder and the display
1492003fc848SThierry Reding 	 * controller doing it here is still kind of okay. None of the other
1493003fc848SThierry Reding 	 * encoder drivers require these bits to be cleared.
1494003fc848SThierry Reding 	 *
1495003fc848SThierry Reding 	 * XXX: Perhaps given that the display controller is switched off at
1496003fc848SThierry Reding 	 * this point anyway maybe clearing these bits isn't even useful for
1497003fc848SThierry Reding 	 * the RGB encoder?
1498003fc848SThierry Reding 	 */
1499003fc848SThierry Reding 	if (dc->rgb) {
1500003fc848SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
1501003fc848SThierry Reding 		value &= ~(PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
1502003fc848SThierry Reding 			   PW4_ENABLE | PM0_ENABLE | PM1_ENABLE);
1503003fc848SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
1504003fc848SThierry Reding 	}
1505003fc848SThierry Reding 
1506003fc848SThierry Reding 	tegra_dc_stats_reset(&dc->stats);
1507003fc848SThierry Reding 	drm_crtc_vblank_off(crtc);
150833a8eb8dSThierry Reding 
15099d99ab6eSThierry Reding 	spin_lock_irq(&crtc->dev->event_lock);
15109d99ab6eSThierry Reding 
15119d99ab6eSThierry Reding 	if (crtc->state->event) {
15129d99ab6eSThierry Reding 		drm_crtc_send_vblank_event(crtc, crtc->state->event);
15139d99ab6eSThierry Reding 		crtc->state->event = NULL;
15149d99ab6eSThierry Reding 	}
15159d99ab6eSThierry Reding 
15169d99ab6eSThierry Reding 	spin_unlock_irq(&crtc->dev->event_lock);
15179d99ab6eSThierry Reding 
151833a8eb8dSThierry Reding 	pm_runtime_put_sync(dc->dev);
1519003fc848SThierry Reding }
1520003fc848SThierry Reding 
15210b20a0f8SLaurent Pinchart static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
15220b20a0f8SLaurent Pinchart 				     struct drm_crtc_state *old_state)
1523dee8268fSThierry Reding {
15244aa3df71SThierry Reding 	struct drm_display_mode *mode = &crtc->state->adjusted_mode;
152576d59ed0SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc->state);
1526dee8268fSThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
1527dbb3f2f7SThierry Reding 	u32 value;
1528dee8268fSThierry Reding 
152933a8eb8dSThierry Reding 	pm_runtime_get_sync(dc->dev);
153033a8eb8dSThierry Reding 
153133a8eb8dSThierry Reding 	/* initialize display controller */
153233a8eb8dSThierry Reding 	if (dc->syncpt) {
153347307954SThierry Reding 		u32 syncpt = host1x_syncpt_id(dc->syncpt), enable;
153447307954SThierry Reding 
153547307954SThierry Reding 		if (dc->soc->has_nvdisplay)
153647307954SThierry Reding 			enable = 1 << 31;
153747307954SThierry Reding 		else
153847307954SThierry Reding 			enable = 1 << 8;
153933a8eb8dSThierry Reding 
154033a8eb8dSThierry Reding 		value = SYNCPT_CNTRL_NO_STALL;
154133a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_GENERAL_INCR_SYNCPT_CNTRL);
154233a8eb8dSThierry Reding 
154347307954SThierry Reding 		value = enable | syncpt;
154433a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_CONT_SYNCPT_VSYNC);
154533a8eb8dSThierry Reding 	}
154633a8eb8dSThierry Reding 
154747307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
154847307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
154947307954SThierry Reding 			DSC_OBUF_UF_INT;
155047307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
155147307954SThierry Reding 
155247307954SThierry Reding 		value = DSC_TO_UF_INT | DSC_BBUF_UF_INT | DSC_RBUF_UF_INT |
155347307954SThierry Reding 			DSC_OBUF_UF_INT | SD3_BUCKET_WALK_DONE_INT |
155447307954SThierry Reding 			HEAD_UF_INT | MSF_INT | REG_TMOUT_INT |
155547307954SThierry Reding 			REGION_CRC_INT | V_PULSE2_INT | V_PULSE3_INT |
155647307954SThierry Reding 			VBLANK_INT | FRAME_END_INT;
155747307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
155847307954SThierry Reding 
155947307954SThierry Reding 		value = SD3_BUCKET_WALK_DONE_INT | HEAD_UF_INT | VBLANK_INT |
156047307954SThierry Reding 			FRAME_END_INT;
156147307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
156247307954SThierry Reding 
156347307954SThierry Reding 		value = HEAD_UF_INT | REG_TMOUT_INT | FRAME_END_INT;
156447307954SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
156547307954SThierry Reding 
156647307954SThierry Reding 		tegra_dc_writel(dc, READ_MUX, DC_CMD_STATE_ACCESS);
156747307954SThierry Reding 	} else {
156833a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
156933a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
157033a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_TYPE);
157133a8eb8dSThierry Reding 
157233a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
157333a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
157433a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_POLARITY);
157533a8eb8dSThierry Reding 
157633a8eb8dSThierry Reding 		/* initialize timer */
157733a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(0x20) |
157833a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(0x20) | WINDOW_C_THRESHOLD(0x20);
157933a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY);
158033a8eb8dSThierry Reding 
158133a8eb8dSThierry Reding 		value = CURSOR_THRESHOLD(0) | WINDOW_A_THRESHOLD(1) |
158233a8eb8dSThierry Reding 			WINDOW_B_THRESHOLD(1) | WINDOW_C_THRESHOLD(1);
158333a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_DISP_MEM_HIGH_PRIORITY_TIMER);
158433a8eb8dSThierry Reding 
158533a8eb8dSThierry Reding 		value = VBLANK_INT | WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
158633a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
158733a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_ENABLE);
158833a8eb8dSThierry Reding 
158933a8eb8dSThierry Reding 		value = WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT |
159033a8eb8dSThierry Reding 			WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT;
159133a8eb8dSThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_INT_MASK);
159247307954SThierry Reding 	}
159333a8eb8dSThierry Reding 
15947116e9a8SThierry Reding 	if (dc->soc->supports_background_color)
15957116e9a8SThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BLEND_BACKGROUND_COLOR);
15967116e9a8SThierry Reding 	else
159733a8eb8dSThierry Reding 		tegra_dc_writel(dc, 0, DC_DISP_BORDER_COLOR);
159833a8eb8dSThierry Reding 
159933a8eb8dSThierry Reding 	/* apply PLL and pixel clock changes */
160076d59ed0SThierry Reding 	tegra_dc_commit_state(dc, state);
160176d59ed0SThierry Reding 
1602dee8268fSThierry Reding 	/* program display mode */
1603dee8268fSThierry Reding 	tegra_dc_set_timings(dc, mode);
1604dee8268fSThierry Reding 
16058620fc62SThierry Reding 	/* interlacing isn't supported yet, so disable it */
16068620fc62SThierry Reding 	if (dc->soc->supports_interlacing) {
16078620fc62SThierry Reding 		value = tegra_dc_readl(dc, DC_DISP_INTERLACE_CONTROL);
16088620fc62SThierry Reding 		value &= ~INTERLACE_ENABLE;
16098620fc62SThierry Reding 		tegra_dc_writel(dc, value, DC_DISP_INTERLACE_CONTROL);
16108620fc62SThierry Reding 	}
1611666cb873SThierry Reding 
1612666cb873SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_DISPLAY_COMMAND);
1613666cb873SThierry Reding 	value &= ~DISP_CTRL_MODE_MASK;
1614666cb873SThierry Reding 	value |= DISP_CTRL_MODE_C_DISPLAY;
1615666cb873SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_DISPLAY_COMMAND);
1616666cb873SThierry Reding 
161747307954SThierry Reding 	if (!dc->soc->has_nvdisplay) {
1618666cb873SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_DISPLAY_POWER_CONTROL);
1619666cb873SThierry Reding 		value |= PW0_ENABLE | PW1_ENABLE | PW2_ENABLE | PW3_ENABLE |
1620666cb873SThierry Reding 			 PW4_ENABLE | PM0_ENABLE | PM1_ENABLE;
1621666cb873SThierry Reding 		tegra_dc_writel(dc, value, DC_CMD_DISPLAY_POWER_CONTROL);
162247307954SThierry Reding 	}
162347307954SThierry Reding 
162447307954SThierry Reding 	/* enable underflow reporting and display red for missing pixels */
162547307954SThierry Reding 	if (dc->soc->has_nvdisplay) {
162647307954SThierry Reding 		value = UNDERFLOW_MODE_RED | UNDERFLOW_REPORT_ENABLE;
162747307954SThierry Reding 		tegra_dc_writel(dc, value, DC_COM_RG_UNDERFLOW);
162847307954SThierry Reding 	}
1629666cb873SThierry Reding 
1630666cb873SThierry Reding 	tegra_dc_commit(dc);
1631dee8268fSThierry Reding 
16328ff64c17SThierry Reding 	drm_crtc_vblank_on(crtc);
1633dee8268fSThierry Reding }
1634dee8268fSThierry Reding 
16354aa3df71SThierry Reding static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
16364aa3df71SThierry Reding 				   struct drm_crtc_state *state)
16374aa3df71SThierry Reding {
1638c4755fb9SThierry Reding 	struct tegra_atomic_state *s = to_tegra_atomic_state(state->state);
1639c4755fb9SThierry Reding 	struct tegra_dc_state *tegra = to_dc_state(state);
1640c4755fb9SThierry Reding 
1641c4755fb9SThierry Reding 	/*
1642c4755fb9SThierry Reding 	 * The display hub display clock needs to be fed by the display clock
1643c4755fb9SThierry Reding 	 * with the highest frequency to ensure proper functioning of all the
1644c4755fb9SThierry Reding 	 * displays.
1645c4755fb9SThierry Reding 	 *
1646c4755fb9SThierry Reding 	 * Note that this isn't used before Tegra186, but it doesn't hurt and
1647c4755fb9SThierry Reding 	 * conditionalizing it would make the code less clean.
1648c4755fb9SThierry Reding 	 */
1649c4755fb9SThierry Reding 	if (state->active) {
1650c4755fb9SThierry Reding 		if (!s->clk_disp || tegra->pclk > s->rate) {
1651c4755fb9SThierry Reding 			s->dc = to_tegra_dc(crtc);
1652c4755fb9SThierry Reding 			s->clk_disp = s->dc->clk;
1653c4755fb9SThierry Reding 			s->rate = tegra->pclk;
1654c4755fb9SThierry Reding 		}
1655c4755fb9SThierry Reding 	}
1656c4755fb9SThierry Reding 
16574aa3df71SThierry Reding 	return 0;
16584aa3df71SThierry Reding }
16594aa3df71SThierry Reding 
1660613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_begin(struct drm_crtc *crtc,
1661613d2b27SMaarten Lankhorst 				    struct drm_crtc_state *old_crtc_state)
16624aa3df71SThierry Reding {
16639d99ab6eSThierry Reding 	unsigned long flags;
16641503ca47SThierry Reding 
16651503ca47SThierry Reding 	if (crtc->state->event) {
16669d99ab6eSThierry Reding 		spin_lock_irqsave(&crtc->dev->event_lock, flags);
16671503ca47SThierry Reding 
16689d99ab6eSThierry Reding 		if (drm_crtc_vblank_get(crtc) != 0)
16699d99ab6eSThierry Reding 			drm_crtc_send_vblank_event(crtc, crtc->state->event);
16709d99ab6eSThierry Reding 		else
16719d99ab6eSThierry Reding 			drm_crtc_arm_vblank_event(crtc, crtc->state->event);
16721503ca47SThierry Reding 
16739d99ab6eSThierry Reding 		spin_unlock_irqrestore(&crtc->dev->event_lock, flags);
16749d99ab6eSThierry Reding 
16751503ca47SThierry Reding 		crtc->state->event = NULL;
16761503ca47SThierry Reding 	}
16774aa3df71SThierry Reding }
16784aa3df71SThierry Reding 
1679613d2b27SMaarten Lankhorst static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
1680613d2b27SMaarten Lankhorst 				    struct drm_crtc_state *old_crtc_state)
16814aa3df71SThierry Reding {
168247802b09SThierry Reding 	struct tegra_dc_state *state = to_dc_state(crtc->state);
168347802b09SThierry Reding 	struct tegra_dc *dc = to_tegra_dc(crtc);
168447307954SThierry Reding 	u32 value;
168547802b09SThierry Reding 
168647307954SThierry Reding 	value = state->planes << 8 | GENERAL_UPDATE;
168747307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
168847307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
168947307954SThierry Reding 
169047307954SThierry Reding 	value = state->planes | GENERAL_ACT_REQ;
169147307954SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_STATE_CONTROL);
169247307954SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
16934aa3df71SThierry Reding }
16944aa3df71SThierry Reding 
1695dee8268fSThierry Reding static const struct drm_crtc_helper_funcs tegra_crtc_helper_funcs = {
16964aa3df71SThierry Reding 	.atomic_check = tegra_crtc_atomic_check,
16974aa3df71SThierry Reding 	.atomic_begin = tegra_crtc_atomic_begin,
16984aa3df71SThierry Reding 	.atomic_flush = tegra_crtc_atomic_flush,
16990b20a0f8SLaurent Pinchart 	.atomic_enable = tegra_crtc_atomic_enable,
170064581714SLaurent Pinchart 	.atomic_disable = tegra_crtc_atomic_disable,
1701dee8268fSThierry Reding };
1702dee8268fSThierry Reding 
1703dee8268fSThierry Reding static irqreturn_t tegra_dc_irq(int irq, void *data)
1704dee8268fSThierry Reding {
1705dee8268fSThierry Reding 	struct tegra_dc *dc = data;
1706dee8268fSThierry Reding 	unsigned long status;
1707dee8268fSThierry Reding 
1708dee8268fSThierry Reding 	status = tegra_dc_readl(dc, DC_CMD_INT_STATUS);
1709dee8268fSThierry Reding 	tegra_dc_writel(dc, status, DC_CMD_INT_STATUS);
1710dee8268fSThierry Reding 
1711dee8268fSThierry Reding 	if (status & FRAME_END_INT) {
1712dee8268fSThierry Reding 		/*
1713dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): frame end\n", __func__);
1714dee8268fSThierry Reding 		*/
1715791ddb1eSThierry Reding 		dc->stats.frames++;
1716dee8268fSThierry Reding 	}
1717dee8268fSThierry Reding 
1718dee8268fSThierry Reding 	if (status & VBLANK_INT) {
1719dee8268fSThierry Reding 		/*
1720dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
1721dee8268fSThierry Reding 		*/
1722ed7dae58SThierry Reding 		drm_crtc_handle_vblank(&dc->base);
1723791ddb1eSThierry Reding 		dc->stats.vblank++;
1724dee8268fSThierry Reding 	}
1725dee8268fSThierry Reding 
1726dee8268fSThierry Reding 	if (status & (WIN_A_UF_INT | WIN_B_UF_INT | WIN_C_UF_INT)) {
1727dee8268fSThierry Reding 		/*
1728dee8268fSThierry Reding 		dev_dbg(dc->dev, "%s(): underflow\n", __func__);
1729dee8268fSThierry Reding 		*/
1730791ddb1eSThierry Reding 		dc->stats.underflow++;
1731791ddb1eSThierry Reding 	}
1732791ddb1eSThierry Reding 
1733791ddb1eSThierry Reding 	if (status & (WIN_A_OF_INT | WIN_B_OF_INT | WIN_C_OF_INT)) {
1734791ddb1eSThierry Reding 		/*
1735791ddb1eSThierry Reding 		dev_dbg(dc->dev, "%s(): overflow\n", __func__);
1736791ddb1eSThierry Reding 		*/
1737791ddb1eSThierry Reding 		dc->stats.overflow++;
1738dee8268fSThierry Reding 	}
1739dee8268fSThierry Reding 
174047307954SThierry Reding 	if (status & HEAD_UF_INT) {
174147307954SThierry Reding 		dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
174247307954SThierry Reding 		dc->stats.underflow++;
174347307954SThierry Reding 	}
174447307954SThierry Reding 
1745dee8268fSThierry Reding 	return IRQ_HANDLED;
1746dee8268fSThierry Reding }
1747dee8268fSThierry Reding 
1748dee8268fSThierry Reding static int tegra_dc_init(struct host1x_client *client)
1749dee8268fSThierry Reding {
17509910f5c4SThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->parent);
1751*bc8828bdSThierry Reding 	struct iommu_group *group = iommu_group_get(client->dev);
17522bcdcbfaSThierry Reding 	unsigned long flags = HOST1X_SYNCPT_CLIENT_MANAGED;
1753dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
1754d1f3e1e0SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
1755c7679306SThierry Reding 	struct drm_plane *primary = NULL;
1756c7679306SThierry Reding 	struct drm_plane *cursor = NULL;
1757dee8268fSThierry Reding 	int err;
1758dee8268fSThierry Reding 
1759617dd7ccSThierry Reding 	dc->syncpt = host1x_syncpt_request(client, flags);
17602bcdcbfaSThierry Reding 	if (!dc->syncpt)
17612bcdcbfaSThierry Reding 		dev_warn(dc->dev, "failed to allocate syncpoint\n");
17622bcdcbfaSThierry Reding 
1763*bc8828bdSThierry Reding 	if (group && tegra->domain) {
1764*bc8828bdSThierry Reding 		if (group != tegra->group) {
1765*bc8828bdSThierry Reding 			err = iommu_attach_group(tegra->domain, group);
1766df06b759SThierry Reding 			if (err < 0) {
1767*bc8828bdSThierry Reding 				dev_err(dc->dev,
1768*bc8828bdSThierry Reding 					"failed to attach to domain: %d\n",
1769df06b759SThierry Reding 					err);
1770df06b759SThierry Reding 				return err;
1771df06b759SThierry Reding 			}
1772df06b759SThierry Reding 
1773*bc8828bdSThierry Reding 			tegra->group = group;
1774*bc8828bdSThierry Reding 		}
1775*bc8828bdSThierry Reding 
1776df06b759SThierry Reding 		dc->domain = tegra->domain;
1777df06b759SThierry Reding 	}
1778df06b759SThierry Reding 
177947307954SThierry Reding 	if (dc->soc->wgrps)
178047307954SThierry Reding 		primary = tegra_dc_add_shared_planes(drm, dc);
178147307954SThierry Reding 	else
178247307954SThierry Reding 		primary = tegra_dc_add_planes(drm, dc);
178347307954SThierry Reding 
1784c7679306SThierry Reding 	if (IS_ERR(primary)) {
1785c7679306SThierry Reding 		err = PTR_ERR(primary);
1786c7679306SThierry Reding 		goto cleanup;
1787c7679306SThierry Reding 	}
1788c7679306SThierry Reding 
1789c7679306SThierry Reding 	if (dc->soc->supports_cursor) {
1790c7679306SThierry Reding 		cursor = tegra_dc_cursor_plane_create(drm, dc);
1791c7679306SThierry Reding 		if (IS_ERR(cursor)) {
1792c7679306SThierry Reding 			err = PTR_ERR(cursor);
1793c7679306SThierry Reding 			goto cleanup;
1794c7679306SThierry Reding 		}
1795c7679306SThierry Reding 	}
1796c7679306SThierry Reding 
1797c7679306SThierry Reding 	err = drm_crtc_init_with_planes(drm, &dc->base, primary, cursor,
1798f9882876SVille Syrjälä 					&tegra_crtc_funcs, NULL);
1799c7679306SThierry Reding 	if (err < 0)
1800c7679306SThierry Reding 		goto cleanup;
1801c7679306SThierry Reding 
1802dee8268fSThierry Reding 	drm_crtc_helper_add(&dc->base, &tegra_crtc_helper_funcs);
1803dee8268fSThierry Reding 
1804d1f3e1e0SThierry Reding 	/*
1805d1f3e1e0SThierry Reding 	 * Keep track of the minimum pitch alignment across all display
1806d1f3e1e0SThierry Reding 	 * controllers.
1807d1f3e1e0SThierry Reding 	 */
1808d1f3e1e0SThierry Reding 	if (dc->soc->pitch_align > tegra->pitch_align)
1809d1f3e1e0SThierry Reding 		tegra->pitch_align = dc->soc->pitch_align;
1810d1f3e1e0SThierry Reding 
18119910f5c4SThierry Reding 	err = tegra_dc_rgb_init(drm, dc);
1812dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
1813dee8268fSThierry Reding 		dev_err(dc->dev, "failed to initialize RGB output: %d\n", err);
1814c7679306SThierry Reding 		goto cleanup;
1815dee8268fSThierry Reding 	}
1816dee8268fSThierry Reding 
1817dee8268fSThierry Reding 	err = devm_request_irq(dc->dev, dc->irq, tegra_dc_irq, 0,
1818dee8268fSThierry Reding 			       dev_name(dc->dev), dc);
1819dee8268fSThierry Reding 	if (err < 0) {
1820dee8268fSThierry Reding 		dev_err(dc->dev, "failed to request IRQ#%u: %d\n", dc->irq,
1821dee8268fSThierry Reding 			err);
1822c7679306SThierry Reding 		goto cleanup;
1823dee8268fSThierry Reding 	}
1824dee8268fSThierry Reding 
1825dee8268fSThierry Reding 	return 0;
1826c7679306SThierry Reding 
1827c7679306SThierry Reding cleanup:
182847307954SThierry Reding 	if (!IS_ERR_OR_NULL(cursor))
1829c7679306SThierry Reding 		drm_plane_cleanup(cursor);
1830c7679306SThierry Reding 
183147307954SThierry Reding 	if (!IS_ERR(primary))
1832c7679306SThierry Reding 		drm_plane_cleanup(primary);
1833c7679306SThierry Reding 
1834*bc8828bdSThierry Reding 	if (group && tegra->domain) {
1835*bc8828bdSThierry Reding 		iommu_detach_group(tegra->domain, group);
1836c7679306SThierry Reding 		dc->domain = NULL;
1837c7679306SThierry Reding 	}
1838c7679306SThierry Reding 
1839c7679306SThierry Reding 	return err;
1840dee8268fSThierry Reding }
1841dee8268fSThierry Reding 
1842dee8268fSThierry Reding static int tegra_dc_exit(struct host1x_client *client)
1843dee8268fSThierry Reding {
1844*bc8828bdSThierry Reding 	struct iommu_group *group = iommu_group_get(client->dev);
1845dee8268fSThierry Reding 	struct tegra_dc *dc = host1x_client_to_dc(client);
1846dee8268fSThierry Reding 	int err;
1847dee8268fSThierry Reding 
1848dee8268fSThierry Reding 	devm_free_irq(dc->dev, dc->irq, dc);
1849dee8268fSThierry Reding 
1850dee8268fSThierry Reding 	err = tegra_dc_rgb_exit(dc);
1851dee8268fSThierry Reding 	if (err) {
1852dee8268fSThierry Reding 		dev_err(dc->dev, "failed to shutdown RGB output: %d\n", err);
1853dee8268fSThierry Reding 		return err;
1854dee8268fSThierry Reding 	}
1855dee8268fSThierry Reding 
1856*bc8828bdSThierry Reding 	if (group && dc->domain) {
1857*bc8828bdSThierry Reding 		iommu_detach_group(dc->domain, group);
1858df06b759SThierry Reding 		dc->domain = NULL;
1859df06b759SThierry Reding 	}
1860df06b759SThierry Reding 
18612bcdcbfaSThierry Reding 	host1x_syncpt_free(dc->syncpt);
18622bcdcbfaSThierry Reding 
1863dee8268fSThierry Reding 	return 0;
1864dee8268fSThierry Reding }
1865dee8268fSThierry Reding 
1866dee8268fSThierry Reding static const struct host1x_client_ops dc_client_ops = {
1867dee8268fSThierry Reding 	.init = tegra_dc_init,
1868dee8268fSThierry Reding 	.exit = tegra_dc_exit,
1869dee8268fSThierry Reding };
1870dee8268fSThierry Reding 
18718620fc62SThierry Reding static const struct tegra_dc_soc_info tegra20_dc_soc_info = {
18727116e9a8SThierry Reding 	.supports_background_color = false,
18738620fc62SThierry Reding 	.supports_interlacing = false,
1874e687651bSThierry Reding 	.supports_cursor = false,
1875c134f019SThierry Reding 	.supports_block_linear = false,
1876ab7d3f58SThierry Reding 	.supports_blending = false,
1877d1f3e1e0SThierry Reding 	.pitch_align = 8,
18789c012700SThierry Reding 	.has_powergate = false,
18796ac1571bSDmitry Osipenko 	.broken_reset = true,
188047307954SThierry Reding 	.has_nvdisplay = false,
1881511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
1882511c7023SThierry Reding 	.primary_formats = tegra20_primary_formats,
1883511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
1884511c7023SThierry Reding 	.overlay_formats = tegra20_overlay_formats,
18858620fc62SThierry Reding };
18868620fc62SThierry Reding 
18878620fc62SThierry Reding static const struct tegra_dc_soc_info tegra30_dc_soc_info = {
18887116e9a8SThierry Reding 	.supports_background_color = false,
18898620fc62SThierry Reding 	.supports_interlacing = false,
1890e687651bSThierry Reding 	.supports_cursor = false,
1891c134f019SThierry Reding 	.supports_block_linear = false,
1892ab7d3f58SThierry Reding 	.supports_blending = false,
1893d1f3e1e0SThierry Reding 	.pitch_align = 8,
18949c012700SThierry Reding 	.has_powergate = false,
18956ac1571bSDmitry Osipenko 	.broken_reset = false,
189647307954SThierry Reding 	.has_nvdisplay = false,
1897511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra20_primary_formats),
1898511c7023SThierry Reding 	.primary_formats = tegra20_primary_formats,
1899511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra20_overlay_formats),
1900511c7023SThierry Reding 	.overlay_formats = tegra20_overlay_formats,
1901d1f3e1e0SThierry Reding };
1902d1f3e1e0SThierry Reding 
1903d1f3e1e0SThierry Reding static const struct tegra_dc_soc_info tegra114_dc_soc_info = {
19047116e9a8SThierry Reding 	.supports_background_color = false,
1905d1f3e1e0SThierry Reding 	.supports_interlacing = false,
1906d1f3e1e0SThierry Reding 	.supports_cursor = false,
1907d1f3e1e0SThierry Reding 	.supports_block_linear = false,
1908ab7d3f58SThierry Reding 	.supports_blending = false,
1909d1f3e1e0SThierry Reding 	.pitch_align = 64,
19109c012700SThierry Reding 	.has_powergate = true,
19116ac1571bSDmitry Osipenko 	.broken_reset = false,
191247307954SThierry Reding 	.has_nvdisplay = false,
1913511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
1914511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
1915511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
1916511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
19178620fc62SThierry Reding };
19188620fc62SThierry Reding 
19198620fc62SThierry Reding static const struct tegra_dc_soc_info tegra124_dc_soc_info = {
19207116e9a8SThierry Reding 	.supports_background_color = true,
19218620fc62SThierry Reding 	.supports_interlacing = true,
1922e687651bSThierry Reding 	.supports_cursor = true,
1923c134f019SThierry Reding 	.supports_block_linear = true,
1924ab7d3f58SThierry Reding 	.supports_blending = true,
1925d1f3e1e0SThierry Reding 	.pitch_align = 64,
19269c012700SThierry Reding 	.has_powergate = true,
19276ac1571bSDmitry Osipenko 	.broken_reset = false,
192847307954SThierry Reding 	.has_nvdisplay = false,
1929511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra124_primary_formats),
1930511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
1931511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra124_overlay_formats),
1932511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
19338620fc62SThierry Reding };
19348620fc62SThierry Reding 
19355b4f516fSThierry Reding static const struct tegra_dc_soc_info tegra210_dc_soc_info = {
19367116e9a8SThierry Reding 	.supports_background_color = true,
19375b4f516fSThierry Reding 	.supports_interlacing = true,
19385b4f516fSThierry Reding 	.supports_cursor = true,
19395b4f516fSThierry Reding 	.supports_block_linear = true,
1940ab7d3f58SThierry Reding 	.supports_blending = true,
19415b4f516fSThierry Reding 	.pitch_align = 64,
19425b4f516fSThierry Reding 	.has_powergate = true,
19436ac1571bSDmitry Osipenko 	.broken_reset = false,
194447307954SThierry Reding 	.has_nvdisplay = false,
1945511c7023SThierry Reding 	.num_primary_formats = ARRAY_SIZE(tegra114_primary_formats),
1946511c7023SThierry Reding 	.primary_formats = tegra114_primary_formats,
1947511c7023SThierry Reding 	.num_overlay_formats = ARRAY_SIZE(tegra114_overlay_formats),
1948511c7023SThierry Reding 	.overlay_formats = tegra114_overlay_formats,
194947307954SThierry Reding };
195047307954SThierry Reding 
195147307954SThierry Reding static const struct tegra_windowgroup_soc tegra186_dc_wgrps[] = {
195247307954SThierry Reding 	{
195347307954SThierry Reding 		.index = 0,
195447307954SThierry Reding 		.dc = 0,
195547307954SThierry Reding 		.windows = (const unsigned int[]) { 0 },
195647307954SThierry Reding 		.num_windows = 1,
195747307954SThierry Reding 	}, {
195847307954SThierry Reding 		.index = 1,
195947307954SThierry Reding 		.dc = 1,
196047307954SThierry Reding 		.windows = (const unsigned int[]) { 1 },
196147307954SThierry Reding 		.num_windows = 1,
196247307954SThierry Reding 	}, {
196347307954SThierry Reding 		.index = 2,
196447307954SThierry Reding 		.dc = 1,
196547307954SThierry Reding 		.windows = (const unsigned int[]) { 2 },
196647307954SThierry Reding 		.num_windows = 1,
196747307954SThierry Reding 	}, {
196847307954SThierry Reding 		.index = 3,
196947307954SThierry Reding 		.dc = 2,
197047307954SThierry Reding 		.windows = (const unsigned int[]) { 3 },
197147307954SThierry Reding 		.num_windows = 1,
197247307954SThierry Reding 	}, {
197347307954SThierry Reding 		.index = 4,
197447307954SThierry Reding 		.dc = 2,
197547307954SThierry Reding 		.windows = (const unsigned int[]) { 4 },
197647307954SThierry Reding 		.num_windows = 1,
197747307954SThierry Reding 	}, {
197847307954SThierry Reding 		.index = 5,
197947307954SThierry Reding 		.dc = 2,
198047307954SThierry Reding 		.windows = (const unsigned int[]) { 5 },
198147307954SThierry Reding 		.num_windows = 1,
198247307954SThierry Reding 	},
198347307954SThierry Reding };
198447307954SThierry Reding 
198547307954SThierry Reding static const struct tegra_dc_soc_info tegra186_dc_soc_info = {
198647307954SThierry Reding 	.supports_background_color = true,
198747307954SThierry Reding 	.supports_interlacing = true,
198847307954SThierry Reding 	.supports_cursor = true,
198947307954SThierry Reding 	.supports_block_linear = true,
1990ab7d3f58SThierry Reding 	.supports_blending = true,
199147307954SThierry Reding 	.pitch_align = 64,
199247307954SThierry Reding 	.has_powergate = false,
199347307954SThierry Reding 	.broken_reset = false,
199447307954SThierry Reding 	.has_nvdisplay = true,
199547307954SThierry Reding 	.wgrps = tegra186_dc_wgrps,
199647307954SThierry Reding 	.num_wgrps = ARRAY_SIZE(tegra186_dc_wgrps),
19975b4f516fSThierry Reding };
19985b4f516fSThierry Reding 
19998620fc62SThierry Reding static const struct of_device_id tegra_dc_of_match[] = {
20008620fc62SThierry Reding 	{
200147307954SThierry Reding 		.compatible = "nvidia,tegra186-dc",
200247307954SThierry Reding 		.data = &tegra186_dc_soc_info,
200347307954SThierry Reding 	}, {
20045b4f516fSThierry Reding 		.compatible = "nvidia,tegra210-dc",
20055b4f516fSThierry Reding 		.data = &tegra210_dc_soc_info,
20065b4f516fSThierry Reding 	}, {
20078620fc62SThierry Reding 		.compatible = "nvidia,tegra124-dc",
20088620fc62SThierry Reding 		.data = &tegra124_dc_soc_info,
20098620fc62SThierry Reding 	}, {
20109c012700SThierry Reding 		.compatible = "nvidia,tegra114-dc",
20119c012700SThierry Reding 		.data = &tegra114_dc_soc_info,
20129c012700SThierry Reding 	}, {
20138620fc62SThierry Reding 		.compatible = "nvidia,tegra30-dc",
20148620fc62SThierry Reding 		.data = &tegra30_dc_soc_info,
20158620fc62SThierry Reding 	}, {
20168620fc62SThierry Reding 		.compatible = "nvidia,tegra20-dc",
20178620fc62SThierry Reding 		.data = &tegra20_dc_soc_info,
20188620fc62SThierry Reding 	}, {
20198620fc62SThierry Reding 		/* sentinel */
20208620fc62SThierry Reding 	}
20218620fc62SThierry Reding };
2022ef70728cSStephen Warren MODULE_DEVICE_TABLE(of, tegra_dc_of_match);
20238620fc62SThierry Reding 
202413411dddSThierry Reding static int tegra_dc_parse_dt(struct tegra_dc *dc)
202513411dddSThierry Reding {
202613411dddSThierry Reding 	struct device_node *np;
202713411dddSThierry Reding 	u32 value = 0;
202813411dddSThierry Reding 	int err;
202913411dddSThierry Reding 
203013411dddSThierry Reding 	err = of_property_read_u32(dc->dev->of_node, "nvidia,head", &value);
203113411dddSThierry Reding 	if (err < 0) {
203213411dddSThierry Reding 		dev_err(dc->dev, "missing \"nvidia,head\" property\n");
203313411dddSThierry Reding 
203413411dddSThierry Reding 		/*
203513411dddSThierry Reding 		 * If the nvidia,head property isn't present, try to find the
203613411dddSThierry Reding 		 * correct head number by looking up the position of this
203713411dddSThierry Reding 		 * display controller's node within the device tree. Assuming
203813411dddSThierry Reding 		 * that the nodes are ordered properly in the DTS file and
203913411dddSThierry Reding 		 * that the translation into a flattened device tree blob
204013411dddSThierry Reding 		 * preserves that ordering this will actually yield the right
204113411dddSThierry Reding 		 * head number.
204213411dddSThierry Reding 		 *
204313411dddSThierry Reding 		 * If those assumptions don't hold, this will still work for
204413411dddSThierry Reding 		 * cases where only a single display controller is used.
204513411dddSThierry Reding 		 */
204613411dddSThierry Reding 		for_each_matching_node(np, tegra_dc_of_match) {
2047cf6b1744SJulia Lawall 			if (np == dc->dev->of_node) {
2048cf6b1744SJulia Lawall 				of_node_put(np);
204913411dddSThierry Reding 				break;
2050cf6b1744SJulia Lawall 			}
205113411dddSThierry Reding 
205213411dddSThierry Reding 			value++;
205313411dddSThierry Reding 		}
205413411dddSThierry Reding 	}
205513411dddSThierry Reding 
205613411dddSThierry Reding 	dc->pipe = value;
205713411dddSThierry Reding 
205813411dddSThierry Reding 	return 0;
205913411dddSThierry Reding }
206013411dddSThierry Reding 
2061dee8268fSThierry Reding static int tegra_dc_probe(struct platform_device *pdev)
2062dee8268fSThierry Reding {
2063dee8268fSThierry Reding 	struct resource *regs;
2064dee8268fSThierry Reding 	struct tegra_dc *dc;
2065dee8268fSThierry Reding 	int err;
2066dee8268fSThierry Reding 
2067dee8268fSThierry Reding 	dc = devm_kzalloc(&pdev->dev, sizeof(*dc), GFP_KERNEL);
2068dee8268fSThierry Reding 	if (!dc)
2069dee8268fSThierry Reding 		return -ENOMEM;
2070dee8268fSThierry Reding 
2071b9ff7aeaSThierry Reding 	dc->soc = of_device_get_match_data(&pdev->dev);
20728620fc62SThierry Reding 
2073dee8268fSThierry Reding 	INIT_LIST_HEAD(&dc->list);
2074dee8268fSThierry Reding 	dc->dev = &pdev->dev;
2075dee8268fSThierry Reding 
207613411dddSThierry Reding 	err = tegra_dc_parse_dt(dc);
207713411dddSThierry Reding 	if (err < 0)
207813411dddSThierry Reding 		return err;
207913411dddSThierry Reding 
2080dee8268fSThierry Reding 	dc->clk = devm_clk_get(&pdev->dev, NULL);
2081dee8268fSThierry Reding 	if (IS_ERR(dc->clk)) {
2082dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to get clock\n");
2083dee8268fSThierry Reding 		return PTR_ERR(dc->clk);
2084dee8268fSThierry Reding 	}
2085dee8268fSThierry Reding 
2086ca48080aSStephen Warren 	dc->rst = devm_reset_control_get(&pdev->dev, "dc");
2087ca48080aSStephen Warren 	if (IS_ERR(dc->rst)) {
2088ca48080aSStephen Warren 		dev_err(&pdev->dev, "failed to get reset\n");
2089ca48080aSStephen Warren 		return PTR_ERR(dc->rst);
2090ca48080aSStephen Warren 	}
2091ca48080aSStephen Warren 
2092a2f2f740SThierry Reding 	/* assert reset and disable clock */
2093a2f2f740SThierry Reding 	if (!dc->soc->broken_reset) {
2094a2f2f740SThierry Reding 		err = clk_prepare_enable(dc->clk);
2095a2f2f740SThierry Reding 		if (err < 0)
2096a2f2f740SThierry Reding 			return err;
2097a2f2f740SThierry Reding 
2098a2f2f740SThierry Reding 		usleep_range(2000, 4000);
2099a2f2f740SThierry Reding 
2100a2f2f740SThierry Reding 		err = reset_control_assert(dc->rst);
2101a2f2f740SThierry Reding 		if (err < 0)
2102a2f2f740SThierry Reding 			return err;
2103a2f2f740SThierry Reding 
2104a2f2f740SThierry Reding 		usleep_range(2000, 4000);
2105a2f2f740SThierry Reding 
2106a2f2f740SThierry Reding 		clk_disable_unprepare(dc->clk);
2107a2f2f740SThierry Reding 	}
210833a8eb8dSThierry Reding 
21099c012700SThierry Reding 	if (dc->soc->has_powergate) {
21109c012700SThierry Reding 		if (dc->pipe == 0)
21119c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DIS;
21129c012700SThierry Reding 		else
21139c012700SThierry Reding 			dc->powergate = TEGRA_POWERGATE_DISB;
21149c012700SThierry Reding 
211533a8eb8dSThierry Reding 		tegra_powergate_power_off(dc->powergate);
21169c012700SThierry Reding 	}
2117dee8268fSThierry Reding 
2118dee8268fSThierry Reding 	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2119dee8268fSThierry Reding 	dc->regs = devm_ioremap_resource(&pdev->dev, regs);
2120dee8268fSThierry Reding 	if (IS_ERR(dc->regs))
2121dee8268fSThierry Reding 		return PTR_ERR(dc->regs);
2122dee8268fSThierry Reding 
2123dee8268fSThierry Reding 	dc->irq = platform_get_irq(pdev, 0);
2124dee8268fSThierry Reding 	if (dc->irq < 0) {
2125dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to get IRQ\n");
2126dee8268fSThierry Reding 		return -ENXIO;
2127dee8268fSThierry Reding 	}
2128dee8268fSThierry Reding 
2129dee8268fSThierry Reding 	err = tegra_dc_rgb_probe(dc);
2130dee8268fSThierry Reding 	if (err < 0 && err != -ENODEV) {
2131dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to probe RGB output: %d\n", err);
2132dee8268fSThierry Reding 		return err;
2133dee8268fSThierry Reding 	}
2134dee8268fSThierry Reding 
213533a8eb8dSThierry Reding 	platform_set_drvdata(pdev, dc);
213633a8eb8dSThierry Reding 	pm_runtime_enable(&pdev->dev);
213733a8eb8dSThierry Reding 
213833a8eb8dSThierry Reding 	INIT_LIST_HEAD(&dc->client.list);
213933a8eb8dSThierry Reding 	dc->client.ops = &dc_client_ops;
214033a8eb8dSThierry Reding 	dc->client.dev = &pdev->dev;
214133a8eb8dSThierry Reding 
2142dee8268fSThierry Reding 	err = host1x_client_register(&dc->client);
2143dee8268fSThierry Reding 	if (err < 0) {
2144dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
2145dee8268fSThierry Reding 			err);
2146dee8268fSThierry Reding 		return err;
2147dee8268fSThierry Reding 	}
2148dee8268fSThierry Reding 
2149dee8268fSThierry Reding 	return 0;
2150dee8268fSThierry Reding }
2151dee8268fSThierry Reding 
2152dee8268fSThierry Reding static int tegra_dc_remove(struct platform_device *pdev)
2153dee8268fSThierry Reding {
2154dee8268fSThierry Reding 	struct tegra_dc *dc = platform_get_drvdata(pdev);
2155dee8268fSThierry Reding 	int err;
2156dee8268fSThierry Reding 
2157dee8268fSThierry Reding 	err = host1x_client_unregister(&dc->client);
2158dee8268fSThierry Reding 	if (err < 0) {
2159dee8268fSThierry Reding 		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
2160dee8268fSThierry Reding 			err);
2161dee8268fSThierry Reding 		return err;
2162dee8268fSThierry Reding 	}
2163dee8268fSThierry Reding 
216459d29c0eSThierry Reding 	err = tegra_dc_rgb_remove(dc);
216559d29c0eSThierry Reding 	if (err < 0) {
216659d29c0eSThierry Reding 		dev_err(&pdev->dev, "failed to remove RGB output: %d\n", err);
216759d29c0eSThierry Reding 		return err;
216859d29c0eSThierry Reding 	}
216959d29c0eSThierry Reding 
217033a8eb8dSThierry Reding 	pm_runtime_disable(&pdev->dev);
217133a8eb8dSThierry Reding 
217233a8eb8dSThierry Reding 	return 0;
217333a8eb8dSThierry Reding }
217433a8eb8dSThierry Reding 
217533a8eb8dSThierry Reding #ifdef CONFIG_PM
217633a8eb8dSThierry Reding static int tegra_dc_suspend(struct device *dev)
217733a8eb8dSThierry Reding {
217833a8eb8dSThierry Reding 	struct tegra_dc *dc = dev_get_drvdata(dev);
217933a8eb8dSThierry Reding 	int err;
218033a8eb8dSThierry Reding 
21816ac1571bSDmitry Osipenko 	if (!dc->soc->broken_reset) {
218233a8eb8dSThierry Reding 		err = reset_control_assert(dc->rst);
218333a8eb8dSThierry Reding 		if (err < 0) {
218433a8eb8dSThierry Reding 			dev_err(dev, "failed to assert reset: %d\n", err);
218533a8eb8dSThierry Reding 			return err;
218633a8eb8dSThierry Reding 		}
21876ac1571bSDmitry Osipenko 	}
21889c012700SThierry Reding 
21899c012700SThierry Reding 	if (dc->soc->has_powergate)
21909c012700SThierry Reding 		tegra_powergate_power_off(dc->powergate);
21919c012700SThierry Reding 
2192dee8268fSThierry Reding 	clk_disable_unprepare(dc->clk);
2193dee8268fSThierry Reding 
2194dee8268fSThierry Reding 	return 0;
2195dee8268fSThierry Reding }
2196dee8268fSThierry Reding 
219733a8eb8dSThierry Reding static int tegra_dc_resume(struct device *dev)
219833a8eb8dSThierry Reding {
219933a8eb8dSThierry Reding 	struct tegra_dc *dc = dev_get_drvdata(dev);
220033a8eb8dSThierry Reding 	int err;
220133a8eb8dSThierry Reding 
220233a8eb8dSThierry Reding 	if (dc->soc->has_powergate) {
220333a8eb8dSThierry Reding 		err = tegra_powergate_sequence_power_up(dc->powergate, dc->clk,
220433a8eb8dSThierry Reding 							dc->rst);
220533a8eb8dSThierry Reding 		if (err < 0) {
220633a8eb8dSThierry Reding 			dev_err(dev, "failed to power partition: %d\n", err);
220733a8eb8dSThierry Reding 			return err;
220833a8eb8dSThierry Reding 		}
220933a8eb8dSThierry Reding 	} else {
221033a8eb8dSThierry Reding 		err = clk_prepare_enable(dc->clk);
221133a8eb8dSThierry Reding 		if (err < 0) {
221233a8eb8dSThierry Reding 			dev_err(dev, "failed to enable clock: %d\n", err);
221333a8eb8dSThierry Reding 			return err;
221433a8eb8dSThierry Reding 		}
221533a8eb8dSThierry Reding 
22166ac1571bSDmitry Osipenko 		if (!dc->soc->broken_reset) {
221733a8eb8dSThierry Reding 			err = reset_control_deassert(dc->rst);
221833a8eb8dSThierry Reding 			if (err < 0) {
22196ac1571bSDmitry Osipenko 				dev_err(dev,
22206ac1571bSDmitry Osipenko 					"failed to deassert reset: %d\n", err);
222133a8eb8dSThierry Reding 				return err;
222233a8eb8dSThierry Reding 			}
222333a8eb8dSThierry Reding 		}
22246ac1571bSDmitry Osipenko 	}
222533a8eb8dSThierry Reding 
222633a8eb8dSThierry Reding 	return 0;
222733a8eb8dSThierry Reding }
222833a8eb8dSThierry Reding #endif
222933a8eb8dSThierry Reding 
223033a8eb8dSThierry Reding static const struct dev_pm_ops tegra_dc_pm_ops = {
223133a8eb8dSThierry Reding 	SET_RUNTIME_PM_OPS(tegra_dc_suspend, tegra_dc_resume, NULL)
223233a8eb8dSThierry Reding };
223333a8eb8dSThierry Reding 
2234dee8268fSThierry Reding struct platform_driver tegra_dc_driver = {
2235dee8268fSThierry Reding 	.driver = {
2236dee8268fSThierry Reding 		.name = "tegra-dc",
2237dee8268fSThierry Reding 		.of_match_table = tegra_dc_of_match,
223833a8eb8dSThierry Reding 		.pm = &tegra_dc_pm_ops,
2239dee8268fSThierry Reding 	},
2240dee8268fSThierry Reding 	.probe = tegra_dc_probe,
2241dee8268fSThierry Reding 	.remove = tegra_dc_remove,
2242dee8268fSThierry Reding };
2243