xref: /openbmc/linux/drivers/gpu/drm/tegra/hub.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2c4755fb9SThierry Reding /*
3c4755fb9SThierry Reding  * Copyright (C) 2017 NVIDIA CORPORATION.  All rights reserved.
4c4755fb9SThierry Reding  */
5c4755fb9SThierry Reding 
6c4755fb9SThierry Reding #include <linux/clk.h>
7eb1df694SSam Ravnborg #include <linux/delay.h>
87ac1a36aSRobin Murphy #include <linux/dma-mapping.h>
9c4755fb9SThierry Reding #include <linux/host1x.h>
10c4755fb9SThierry Reding #include <linux/module.h>
11c4755fb9SThierry Reding #include <linux/of.h>
12c4755fb9SThierry Reding #include <linux/of_graph.h>
13*722d4f06SRob Herring #include <linux/of_platform.h>
14c4755fb9SThierry Reding #include <linux/platform_device.h>
15c4755fb9SThierry Reding #include <linux/pm_runtime.h>
16c4755fb9SThierry Reding #include <linux/reset.h>
17c4755fb9SThierry Reding 
18c4755fb9SThierry Reding #include <drm/drm_atomic.h>
19c4755fb9SThierry Reding #include <drm/drm_atomic_helper.h>
2090bb087fSVille Syrjälä #include <drm/drm_blend.h>
21eb1df694SSam Ravnborg #include <drm/drm_fourcc.h>
22720cf96dSVille Syrjälä #include <drm/drm_framebuffer.h>
23fcd70cd3SDaniel Vetter #include <drm/drm_probe_helper.h>
24c4755fb9SThierry Reding 
25c4755fb9SThierry Reding #include "drm.h"
26c4755fb9SThierry Reding #include "dc.h"
27c4755fb9SThierry Reding #include "plane.h"
28c4755fb9SThierry Reding 
29ecc583e2SThierry Reding #define NFB 24
30ecc583e2SThierry Reding 
31c4755fb9SThierry Reding static const u32 tegra_shared_plane_formats[] = {
32511c7023SThierry Reding 	DRM_FORMAT_ARGB1555,
33c4755fb9SThierry Reding 	DRM_FORMAT_RGB565,
34511c7023SThierry Reding 	DRM_FORMAT_RGBA5551,
35511c7023SThierry Reding 	DRM_FORMAT_ARGB8888,
36511c7023SThierry Reding 	DRM_FORMAT_ABGR8888,
37511c7023SThierry Reding 	/* new on Tegra114 */
38511c7023SThierry Reding 	DRM_FORMAT_ABGR4444,
39511c7023SThierry Reding 	DRM_FORMAT_ABGR1555,
40511c7023SThierry Reding 	DRM_FORMAT_BGRA5551,
41511c7023SThierry Reding 	DRM_FORMAT_XRGB1555,
42511c7023SThierry Reding 	DRM_FORMAT_RGBX5551,
43511c7023SThierry Reding 	DRM_FORMAT_XBGR1555,
44511c7023SThierry Reding 	DRM_FORMAT_BGRX5551,
45511c7023SThierry Reding 	DRM_FORMAT_BGR565,
46511c7023SThierry Reding 	DRM_FORMAT_XRGB8888,
47511c7023SThierry Reding 	DRM_FORMAT_XBGR8888,
48511c7023SThierry Reding 	/* planar formats */
49511c7023SThierry Reding 	DRM_FORMAT_UYVY,
50511c7023SThierry Reding 	DRM_FORMAT_YUYV,
51511c7023SThierry Reding 	DRM_FORMAT_YUV420,
52511c7023SThierry Reding 	DRM_FORMAT_YUV422,
53c4755fb9SThierry Reding };
54c4755fb9SThierry Reding 
55e90124cbSThierry Reding static const u64 tegra_shared_plane_modifiers[] = {
56e90124cbSThierry Reding 	DRM_FORMAT_MOD_LINEAR,
57e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
58e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
59e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
60e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
61e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
62e90124cbSThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
637b6f8467SThierry Reding 	/*
647b6f8467SThierry Reding 	 * The GPU sector layout is only supported on Tegra194, but these will
657b6f8467SThierry Reding 	 * be filtered out later on by ->format_mod_supported() on SoCs where
667b6f8467SThierry Reding 	 * it isn't supported.
677b6f8467SThierry Reding 	 */
687b6f8467SThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
697b6f8467SThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
707b6f8467SThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
717b6f8467SThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
727b6f8467SThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
737b6f8467SThierry Reding 	DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
747b6f8467SThierry Reding 	/* sentinel */
75e90124cbSThierry Reding 	DRM_FORMAT_MOD_INVALID
76e90124cbSThierry Reding };
77e90124cbSThierry Reding 
tegra_plane_offset(struct tegra_plane * plane,unsigned int offset)781087fac1SThierry Reding static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
79c4755fb9SThierry Reding 					      unsigned int offset)
80c4755fb9SThierry Reding {
81c4755fb9SThierry Reding 	if (offset >= 0x500 && offset <= 0x581) {
82c4755fb9SThierry Reding 		offset = 0x000 + (offset - 0x500);
831087fac1SThierry Reding 		return plane->offset + offset;
84c4755fb9SThierry Reding 	}
85c4755fb9SThierry Reding 
86c4755fb9SThierry Reding 	if (offset >= 0x700 && offset <= 0x73c) {
87c4755fb9SThierry Reding 		offset = 0x180 + (offset - 0x700);
881087fac1SThierry Reding 		return plane->offset + offset;
89c4755fb9SThierry Reding 	}
90c4755fb9SThierry Reding 
91c4755fb9SThierry Reding 	if (offset >= 0x800 && offset <= 0x83e) {
92c4755fb9SThierry Reding 		offset = 0x1c0 + (offset - 0x800);
931087fac1SThierry Reding 		return plane->offset + offset;
94c4755fb9SThierry Reding 	}
95c4755fb9SThierry Reding 
96c4755fb9SThierry Reding 	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
97c4755fb9SThierry Reding 
981087fac1SThierry Reding 	return plane->offset + offset;
99c4755fb9SThierry Reding }
100c4755fb9SThierry Reding 
tegra_plane_readl(struct tegra_plane * plane,unsigned int offset)1011087fac1SThierry Reding static inline u32 tegra_plane_readl(struct tegra_plane *plane,
102c4755fb9SThierry Reding 				    unsigned int offset)
103c4755fb9SThierry Reding {
104c4755fb9SThierry Reding 	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
105c4755fb9SThierry Reding }
106c4755fb9SThierry Reding 
tegra_plane_writel(struct tegra_plane * plane,u32 value,unsigned int offset)1071087fac1SThierry Reding static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
1081087fac1SThierry Reding 				      unsigned int offset)
109c4755fb9SThierry Reding {
110c4755fb9SThierry Reding 	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
111c4755fb9SThierry Reding }
112c4755fb9SThierry Reding 
tegra_windowgroup_enable(struct tegra_windowgroup * wgrp)113c4755fb9SThierry Reding static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
114c4755fb9SThierry Reding {
115fd67e9c6SThierry Reding 	int err = 0;
116fd67e9c6SThierry Reding 
117c4755fb9SThierry Reding 	mutex_lock(&wgrp->lock);
118c4755fb9SThierry Reding 
119c4755fb9SThierry Reding 	if (wgrp->usecount == 0) {
120fd67e9c6SThierry Reding 		err = host1x_client_resume(wgrp->parent);
121fd67e9c6SThierry Reding 		if (err < 0) {
122fd67e9c6SThierry Reding 			dev_err(wgrp->parent->dev, "failed to resume: %d\n", err);
123fd67e9c6SThierry Reding 			goto unlock;
124fd67e9c6SThierry Reding 		}
125fd67e9c6SThierry Reding 
126c4755fb9SThierry Reding 		reset_control_deassert(wgrp->rst);
127c4755fb9SThierry Reding 	}
128c4755fb9SThierry Reding 
129c4755fb9SThierry Reding 	wgrp->usecount++;
130c4755fb9SThierry Reding 
131fd67e9c6SThierry Reding unlock:
132fd67e9c6SThierry Reding 	mutex_unlock(&wgrp->lock);
133fd67e9c6SThierry Reding 	return err;
134c4755fb9SThierry Reding }
135c4755fb9SThierry Reding 
tegra_windowgroup_disable(struct tegra_windowgroup * wgrp)136c4755fb9SThierry Reding static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
137c4755fb9SThierry Reding {
138c4755fb9SThierry Reding 	int err;
139c4755fb9SThierry Reding 
140c4755fb9SThierry Reding 	mutex_lock(&wgrp->lock);
141c4755fb9SThierry Reding 
142c4755fb9SThierry Reding 	if (wgrp->usecount == 1) {
143c4755fb9SThierry Reding 		err = reset_control_assert(wgrp->rst);
144c4755fb9SThierry Reding 		if (err < 0) {
145c4755fb9SThierry Reding 			pr_err("failed to assert reset for window group %u\n",
146c4755fb9SThierry Reding 			       wgrp->index);
147c4755fb9SThierry Reding 		}
148c4755fb9SThierry Reding 
149fd67e9c6SThierry Reding 		host1x_client_suspend(wgrp->parent);
150c4755fb9SThierry Reding 	}
151c4755fb9SThierry Reding 
152c4755fb9SThierry Reding 	wgrp->usecount--;
153c4755fb9SThierry Reding 	mutex_unlock(&wgrp->lock);
154c4755fb9SThierry Reding }
155c4755fb9SThierry Reding 
tegra_display_hub_prepare(struct tegra_display_hub * hub)156c4755fb9SThierry Reding int tegra_display_hub_prepare(struct tegra_display_hub *hub)
157c4755fb9SThierry Reding {
158c4755fb9SThierry Reding 	unsigned int i;
159c4755fb9SThierry Reding 
160c4755fb9SThierry Reding 	/*
161c4755fb9SThierry Reding 	 * XXX Enabling/disabling windowgroups needs to happen when the owner
162c4755fb9SThierry Reding 	 * display controller is disabled. There's currently no good point at
163c4755fb9SThierry Reding 	 * which this could be executed, so unconditionally enable all window
164c4755fb9SThierry Reding 	 * groups for now.
165c4755fb9SThierry Reding 	 */
166c4755fb9SThierry Reding 	for (i = 0; i < hub->soc->num_wgrps; i++) {
167c4755fb9SThierry Reding 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
168c4755fb9SThierry Reding 
169ef4e417eSNicolin Chen 		/* Skip orphaned window group whose parent DC is disabled */
170ef4e417eSNicolin Chen 		if (wgrp->parent)
171c4755fb9SThierry Reding 			tegra_windowgroup_enable(wgrp);
172c4755fb9SThierry Reding 	}
173c4755fb9SThierry Reding 
174c4755fb9SThierry Reding 	return 0;
175c4755fb9SThierry Reding }
176c4755fb9SThierry Reding 
tegra_display_hub_cleanup(struct tegra_display_hub * hub)177c4755fb9SThierry Reding void tegra_display_hub_cleanup(struct tegra_display_hub *hub)
178c4755fb9SThierry Reding {
179c4755fb9SThierry Reding 	unsigned int i;
180c4755fb9SThierry Reding 
181c4755fb9SThierry Reding 	/*
182c4755fb9SThierry Reding 	 * XXX Remove this once window groups can be more fine-grainedly
183c4755fb9SThierry Reding 	 * enabled and disabled.
184c4755fb9SThierry Reding 	 */
185c4755fb9SThierry Reding 	for (i = 0; i < hub->soc->num_wgrps; i++) {
186c4755fb9SThierry Reding 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
187c4755fb9SThierry Reding 
188ef4e417eSNicolin Chen 		/* Skip orphaned window group whose parent DC is disabled */
189ef4e417eSNicolin Chen 		if (wgrp->parent)
190c4755fb9SThierry Reding 			tegra_windowgroup_disable(wgrp);
191c4755fb9SThierry Reding 	}
192c4755fb9SThierry Reding }
193c4755fb9SThierry Reding 
tegra_shared_plane_update(struct tegra_plane * plane)1941087fac1SThierry Reding static void tegra_shared_plane_update(struct tegra_plane *plane)
195c4755fb9SThierry Reding {
196c4755fb9SThierry Reding 	struct tegra_dc *dc = plane->dc;
197c4755fb9SThierry Reding 	unsigned long timeout;
198c4755fb9SThierry Reding 	u32 mask, value;
199c4755fb9SThierry Reding 
200c4755fb9SThierry Reding 	mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index;
201c4755fb9SThierry Reding 	tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
202c4755fb9SThierry Reding 
203c4755fb9SThierry Reding 	timeout = jiffies + msecs_to_jiffies(1000);
204c4755fb9SThierry Reding 
205c4755fb9SThierry Reding 	while (time_before(jiffies, timeout)) {
206c4755fb9SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
207c4755fb9SThierry Reding 		if ((value & mask) == 0)
208c4755fb9SThierry Reding 			break;
209c4755fb9SThierry Reding 
210c4755fb9SThierry Reding 		usleep_range(100, 400);
211c4755fb9SThierry Reding 	}
212c4755fb9SThierry Reding }
213c4755fb9SThierry Reding 
tegra_shared_plane_activate(struct tegra_plane * plane)2141087fac1SThierry Reding static void tegra_shared_plane_activate(struct tegra_plane *plane)
215c4755fb9SThierry Reding {
216c4755fb9SThierry Reding 	struct tegra_dc *dc = plane->dc;
217c4755fb9SThierry Reding 	unsigned long timeout;
218c4755fb9SThierry Reding 	u32 mask, value;
219c4755fb9SThierry Reding 
220c4755fb9SThierry Reding 	mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index;
221c4755fb9SThierry Reding 	tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
222c4755fb9SThierry Reding 
223c4755fb9SThierry Reding 	timeout = jiffies + msecs_to_jiffies(1000);
224c4755fb9SThierry Reding 
225c4755fb9SThierry Reding 	while (time_before(jiffies, timeout)) {
226c4755fb9SThierry Reding 		value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
227c4755fb9SThierry Reding 		if ((value & mask) == 0)
228c4755fb9SThierry Reding 			break;
229c4755fb9SThierry Reding 
230c4755fb9SThierry Reding 		usleep_range(100, 400);
231c4755fb9SThierry Reding 	}
232c4755fb9SThierry Reding }
233c4755fb9SThierry Reding 
234c4755fb9SThierry Reding static unsigned int
tegra_shared_plane_get_owner(struct tegra_plane * plane,struct tegra_dc * dc)2351087fac1SThierry Reding tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc)
236c4755fb9SThierry Reding {
237c4755fb9SThierry Reding 	unsigned int offset =
238c4755fb9SThierry Reding 		tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
239c4755fb9SThierry Reding 
240c4755fb9SThierry Reding 	return tegra_dc_readl(dc, offset) & OWNER_MASK;
241c4755fb9SThierry Reding }
242c4755fb9SThierry Reding 
tegra_dc_owns_shared_plane(struct tegra_dc * dc,struct tegra_plane * plane)243c4755fb9SThierry Reding static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc,
2441087fac1SThierry Reding 				       struct tegra_plane *plane)
245c4755fb9SThierry Reding {
246c4755fb9SThierry Reding 	struct device *dev = dc->dev;
247c4755fb9SThierry Reding 
248c4755fb9SThierry Reding 	if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) {
249c4755fb9SThierry Reding 		if (plane->dc == dc)
250c4755fb9SThierry Reding 			return true;
251c4755fb9SThierry Reding 
252c4755fb9SThierry Reding 		dev_WARN(dev, "head %u owns window %u but is not attached\n",
2531087fac1SThierry Reding 			 dc->pipe, plane->index);
254c4755fb9SThierry Reding 	}
255c4755fb9SThierry Reding 
256c4755fb9SThierry Reding 	return false;
257c4755fb9SThierry Reding }
258c4755fb9SThierry Reding 
tegra_shared_plane_set_owner(struct tegra_plane * plane,struct tegra_dc * new)2591087fac1SThierry Reding static int tegra_shared_plane_set_owner(struct tegra_plane *plane,
260c4755fb9SThierry Reding 					struct tegra_dc *new)
261c4755fb9SThierry Reding {
262c4755fb9SThierry Reding 	unsigned int offset =
263c4755fb9SThierry Reding 		tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
264c4755fb9SThierry Reding 	struct tegra_dc *old = plane->dc, *dc = new ? new : old;
265c4755fb9SThierry Reding 	struct device *dev = new ? new->dev : old->dev;
2661087fac1SThierry Reding 	unsigned int owner, index = plane->index;
267c4755fb9SThierry Reding 	u32 value;
268c4755fb9SThierry Reding 
269c4755fb9SThierry Reding 	value = tegra_dc_readl(dc, offset);
270c4755fb9SThierry Reding 	owner = value & OWNER_MASK;
271c4755fb9SThierry Reding 
272c4755fb9SThierry Reding 	if (new && (owner != OWNER_MASK && owner != new->pipe)) {
273c4755fb9SThierry Reding 		dev_WARN(dev, "window %u owned by head %u\n", index, owner);
274c4755fb9SThierry Reding 		return -EBUSY;
275c4755fb9SThierry Reding 	}
276c4755fb9SThierry Reding 
277c4755fb9SThierry Reding 	/*
278c4755fb9SThierry Reding 	 * This seems to happen whenever the head has been disabled with one
279c4755fb9SThierry Reding 	 * or more windows being active. This is harmless because we'll just
280c4755fb9SThierry Reding 	 * reassign the window to the new head anyway.
281c4755fb9SThierry Reding 	 */
282c4755fb9SThierry Reding 	if (old && owner == OWNER_MASK)
283c4755fb9SThierry Reding 		dev_dbg(dev, "window %u not owned by head %u but %u\n", index,
284c4755fb9SThierry Reding 			old->pipe, owner);
285c4755fb9SThierry Reding 
286c4755fb9SThierry Reding 	value &= ~OWNER_MASK;
287c4755fb9SThierry Reding 
288c4755fb9SThierry Reding 	if (new)
289c4755fb9SThierry Reding 		value |= OWNER(new->pipe);
290c4755fb9SThierry Reding 	else
291c4755fb9SThierry Reding 		value |= OWNER_MASK;
292c4755fb9SThierry Reding 
293c4755fb9SThierry Reding 	tegra_dc_writel(dc, value, offset);
294c4755fb9SThierry Reding 
295c4755fb9SThierry Reding 	plane->dc = new;
296c4755fb9SThierry Reding 
297c4755fb9SThierry Reding 	return 0;
298c4755fb9SThierry Reding }
299c4755fb9SThierry Reding 
tegra_shared_plane_setup_scaler(struct tegra_plane * plane)300ecc583e2SThierry Reding static void tegra_shared_plane_setup_scaler(struct tegra_plane *plane)
301ecc583e2SThierry Reding {
302ecc583e2SThierry Reding 	static const unsigned int coeffs[192] = {
303ecc583e2SThierry Reding 		0x00000000, 0x3c70e400, 0x3bb037e4, 0x0c51cc9c,
304ecc583e2SThierry Reding 		0x00100001, 0x3bf0dbfa, 0x3d00f406, 0x3fe003ff,
305ecc583e2SThierry Reding 		0x00300002, 0x3b80cbf5, 0x3da1040d, 0x3fb003fe,
306ecc583e2SThierry Reding 		0x00400002, 0x3b20bff1, 0x3e511015, 0x3f9003fc,
307ecc583e2SThierry Reding 		0x00500002, 0x3ad0b3ed, 0x3f21201d, 0x3f5003fb,
308ecc583e2SThierry Reding 		0x00500003, 0x3aa0a3e9, 0x3ff13026, 0x3f2007f9,
309ecc583e2SThierry Reding 		0x00500403, 0x3a7097e6, 0x00e1402f, 0x3ee007f7,
310ecc583e2SThierry Reding 		0x00500403, 0x3a608be4, 0x01d14c38, 0x3ea00bf6,
311ecc583e2SThierry Reding 		0x00500403, 0x3a507fe2, 0x02e15c42, 0x3e500ff4,
312ecc583e2SThierry Reding 		0x00500402, 0x3a6073e1, 0x03f16c4d, 0x3e000ff2,
313ecc583e2SThierry Reding 		0x00400402, 0x3a706be0, 0x05117858, 0x3db013f0,
314ecc583e2SThierry Reding 		0x00300402, 0x3a905fe0, 0x06318863, 0x3d6017ee,
315ecc583e2SThierry Reding 		0x00300402, 0x3ab057e0, 0x0771986e, 0x3d001beb,
316ecc583e2SThierry Reding 		0x00200001, 0x3af04fe1, 0x08a1a47a, 0x3cb023e9,
317ecc583e2SThierry Reding 		0x00100001, 0x3b2047e2, 0x09e1b485, 0x3c6027e7,
318ecc583e2SThierry Reding 		0x00100000, 0x3b703fe2, 0x0b11c091, 0x3c002fe6,
319ecc583e2SThierry Reding 		0x3f203800, 0x0391103f, 0x3ff0a014, 0x0811606c,
320ecc583e2SThierry Reding 		0x3f2037ff, 0x0351083c, 0x03e11842, 0x3f203c00,
321ecc583e2SThierry Reding 		0x3f302fff, 0x03010439, 0x04311c45, 0x3f104401,
322ecc583e2SThierry Reding 		0x3f302fff, 0x02c0fc35, 0x04812448, 0x3f104802,
323ecc583e2SThierry Reding 		0x3f4027ff, 0x0270f832, 0x04c1284b, 0x3f205003,
324ecc583e2SThierry Reding 		0x3f4023ff, 0x0230f030, 0x0511304e, 0x3f205403,
325ecc583e2SThierry Reding 		0x3f601fff, 0x01f0e82d, 0x05613451, 0x3f205c04,
326ecc583e2SThierry Reding 		0x3f701bfe, 0x01b0e02a, 0x05a13c54, 0x3f306006,
327ecc583e2SThierry Reding 		0x3f7017fe, 0x0170d827, 0x05f14057, 0x3f406807,
328ecc583e2SThierry Reding 		0x3f8017ff, 0x0140d424, 0x0641445a, 0x3f406c08,
329ecc583e2SThierry Reding 		0x3fa013ff, 0x0100cc22, 0x0681485d, 0x3f507409,
330ecc583e2SThierry Reding 		0x3fa00fff, 0x00d0c41f, 0x06d14c60, 0x3f607c0b,
331ecc583e2SThierry Reding 		0x3fc00fff, 0x0090bc1c, 0x07115063, 0x3f80840c,
332ecc583e2SThierry Reding 		0x3fd00bff, 0x0070b41a, 0x07515465, 0x3f908c0e,
333ecc583e2SThierry Reding 		0x3fe007ff, 0x0040b018, 0x07915868, 0x3fb0900f,
334ecc583e2SThierry Reding 		0x3ff00400, 0x0010a816, 0x07d15c6a, 0x3fd09811,
335ecc583e2SThierry Reding 		0x00a04c0e, 0x0460f442, 0x0240a827, 0x05c15859,
336ecc583e2SThierry Reding 		0x0090440d, 0x0440f040, 0x0480fc43, 0x00b05010,
337ecc583e2SThierry Reding 		0x0080400c, 0x0410ec3e, 0x04910044, 0x00d05411,
338ecc583e2SThierry Reding 		0x0070380b, 0x03f0e83d, 0x04b10846, 0x00e05812,
339ecc583e2SThierry Reding 		0x0060340a, 0x03d0e43b, 0x04d10c48, 0x00f06013,
340ecc583e2SThierry Reding 		0x00503009, 0x03b0e039, 0x04e11449, 0x01106415,
341ecc583e2SThierry Reding 		0x00402c08, 0x0390d838, 0x05011c4b, 0x01206c16,
342ecc583e2SThierry Reding 		0x00302807, 0x0370d436, 0x0511204c, 0x01407018,
343ecc583e2SThierry Reding 		0x00302406, 0x0340d034, 0x0531244e, 0x01507419,
344ecc583e2SThierry Reding 		0x00202005, 0x0320cc32, 0x05412c50, 0x01707c1b,
345ecc583e2SThierry Reding 		0x00101c04, 0x0300c431, 0x05613451, 0x0180801d,
346ecc583e2SThierry Reding 		0x00101803, 0x02e0c02f, 0x05713853, 0x01a0881e,
347ecc583e2SThierry Reding 		0x00101002, 0x02b0bc2d, 0x05814054, 0x01c08c20,
348ecc583e2SThierry Reding 		0x00000c02, 0x02a0b82c, 0x05914455, 0x01e09421,
349ecc583e2SThierry Reding 		0x00000801, 0x0280b02a, 0x05a14c57, 0x02009c23,
350ecc583e2SThierry Reding 		0x00000400, 0x0260ac28, 0x05b15458, 0x0220a025,
351ecc583e2SThierry Reding 	};
352ecc583e2SThierry Reding 	unsigned int ratio, row, column;
353ecc583e2SThierry Reding 
354ecc583e2SThierry Reding 	for (ratio = 0; ratio <= 2; ratio++) {
355ecc583e2SThierry Reding 		for (row = 0; row <= 15; row++) {
356ecc583e2SThierry Reding 			for (column = 0; column <= 3; column++) {
357ecc583e2SThierry Reding 				unsigned int index = (ratio << 6) + (row << 2) + column;
358ecc583e2SThierry Reding 				u32 value;
359ecc583e2SThierry Reding 
360ecc583e2SThierry Reding 				value = COEFF_INDEX(index) | COEFF_DATA(coeffs[index]);
361ecc583e2SThierry Reding 				tegra_plane_writel(plane, value,
362ecc583e2SThierry Reding 						   DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_COEFF);
363ecc583e2SThierry Reding 			}
364ecc583e2SThierry Reding 		}
365ecc583e2SThierry Reding 	}
366ecc583e2SThierry Reding }
367ecc583e2SThierry Reding 
tegra_dc_assign_shared_plane(struct tegra_dc * dc,struct tegra_plane * plane)368c4755fb9SThierry Reding static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
3691087fac1SThierry Reding 					 struct tegra_plane *plane)
370c4755fb9SThierry Reding {
371c4755fb9SThierry Reding 	u32 value;
372c4755fb9SThierry Reding 	int err;
373c4755fb9SThierry Reding 
374c4755fb9SThierry Reding 	if (!tegra_dc_owns_shared_plane(dc, plane)) {
375c4755fb9SThierry Reding 		err = tegra_shared_plane_set_owner(plane, dc);
376c4755fb9SThierry Reding 		if (err < 0)
377c4755fb9SThierry Reding 			return;
378c4755fb9SThierry Reding 	}
379c4755fb9SThierry Reding 
380c4755fb9SThierry Reding 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
381c4755fb9SThierry Reding 	value |= MODE_FOUR_LINES;
382c4755fb9SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
383c4755fb9SThierry Reding 
384c4755fb9SThierry Reding 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
385c4755fb9SThierry Reding 	value = SLOTS(1);
386c4755fb9SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
387c4755fb9SThierry Reding 
388c4755fb9SThierry Reding 	/* disable watermark */
389c4755fb9SThierry Reding 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
390c4755fb9SThierry Reding 	value &= ~LATENCY_CTL_MODE_ENABLE;
391c4755fb9SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
392c4755fb9SThierry Reding 
393c4755fb9SThierry Reding 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
394c4755fb9SThierry Reding 	value |= WATERMARK_MASK;
395c4755fb9SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
396c4755fb9SThierry Reding 
397c4755fb9SThierry Reding 	/* pipe meter */
398c4755fb9SThierry Reding 	value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
399c4755fb9SThierry Reding 	value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0);
400c4755fb9SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
401c4755fb9SThierry Reding 
402c4755fb9SThierry Reding 	/* mempool entries */
403c4755fb9SThierry Reding 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
404c4755fb9SThierry Reding 	value = MEMPOOL_ENTRIES(0x331);
405c4755fb9SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
406c4755fb9SThierry Reding 
407c4755fb9SThierry Reding 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP);
408c4755fb9SThierry Reding 	value &= ~THREAD_NUM_MASK;
409c4755fb9SThierry Reding 	value |= THREAD_NUM(plane->base.index);
410c4755fb9SThierry Reding 	value |= THREAD_GROUP_ENABLE;
411c4755fb9SThierry Reding 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP);
412c4755fb9SThierry Reding 
413ecc583e2SThierry Reding 	tegra_shared_plane_setup_scaler(plane);
414ecc583e2SThierry Reding 
415c4755fb9SThierry Reding 	tegra_shared_plane_update(plane);
416c4755fb9SThierry Reding 	tegra_shared_plane_activate(plane);
417c4755fb9SThierry Reding }
418c4755fb9SThierry Reding 
tegra_dc_remove_shared_plane(struct tegra_dc * dc,struct tegra_plane * plane)419c4755fb9SThierry Reding static void tegra_dc_remove_shared_plane(struct tegra_dc *dc,
4201087fac1SThierry Reding 					 struct tegra_plane *plane)
421c4755fb9SThierry Reding {
422c4755fb9SThierry Reding 	tegra_shared_plane_set_owner(plane, NULL);
423c4755fb9SThierry Reding }
424c4755fb9SThierry Reding 
tegra_shared_plane_atomic_check(struct drm_plane * plane,struct drm_atomic_state * state)425c4755fb9SThierry Reding static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
4267c11b99aSMaxime Ripard 					   struct drm_atomic_state *state)
427c4755fb9SThierry Reding {
4287c11b99aSMaxime Ripard 	struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
4297c11b99aSMaxime Ripard 										 plane);
430ba5c1649SMaxime Ripard 	struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
431c4755fb9SThierry Reding 	struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane);
432c4755fb9SThierry Reding 	struct tegra_bo_tiling *tiling = &plane_state->tiling;
433ba5c1649SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
434c4755fb9SThierry Reding 	int err;
435c4755fb9SThierry Reding 
436c4755fb9SThierry Reding 	/* no need for further checks if the plane is being disabled */
437ba5c1649SMaxime Ripard 	if (!new_plane_state->crtc || !new_plane_state->fb)
438c4755fb9SThierry Reding 		return 0;
439c4755fb9SThierry Reding 
440ba5c1649SMaxime Ripard 	err = tegra_plane_format(new_plane_state->fb->format->format,
441c4755fb9SThierry Reding 				 &plane_state->format,
442c4755fb9SThierry Reding 				 &plane_state->swap);
443c4755fb9SThierry Reding 	if (err < 0)
444c4755fb9SThierry Reding 		return err;
445c4755fb9SThierry Reding 
446ba5c1649SMaxime Ripard 	err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
447c4755fb9SThierry Reding 	if (err < 0)
448c4755fb9SThierry Reding 		return err;
449c4755fb9SThierry Reding 
450c4755fb9SThierry Reding 	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
451c4755fb9SThierry Reding 	    !dc->soc->supports_block_linear) {
452c4755fb9SThierry Reding 		DRM_ERROR("hardware doesn't support block linear mode\n");
453c4755fb9SThierry Reding 		return -EINVAL;
454c4755fb9SThierry Reding 	}
455c4755fb9SThierry Reding 
4567b6f8467SThierry Reding 	if (tiling->sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU &&
4577b6f8467SThierry Reding 	    !dc->soc->supports_sector_layout) {
4587b6f8467SThierry Reding 		DRM_ERROR("hardware doesn't support GPU sector layout\n");
4597b6f8467SThierry Reding 		return -EINVAL;
4607b6f8467SThierry Reding 	}
4617b6f8467SThierry Reding 
462c4755fb9SThierry Reding 	/*
463c4755fb9SThierry Reding 	 * Tegra doesn't support different strides for U and V planes so we
464c4755fb9SThierry Reding 	 * error out if the user tries to display a framebuffer with such a
465c4755fb9SThierry Reding 	 * configuration.
466c4755fb9SThierry Reding 	 */
467ba5c1649SMaxime Ripard 	if (new_plane_state->fb->format->num_planes > 2) {
468ba5c1649SMaxime Ripard 		if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
469c4755fb9SThierry Reding 			DRM_ERROR("unsupported UV-plane configuration\n");
470c4755fb9SThierry Reding 			return -EINVAL;
471c4755fb9SThierry Reding 		}
472c4755fb9SThierry Reding 	}
473c4755fb9SThierry Reding 
474c4755fb9SThierry Reding 	/* XXX scaling is not yet supported, add a check here */
475c4755fb9SThierry Reding 
476ba5c1649SMaxime Ripard 	err = tegra_plane_state_add(&tegra->base, new_plane_state);
477c4755fb9SThierry Reding 	if (err < 0)
478c4755fb9SThierry Reding 		return err;
479c4755fb9SThierry Reding 
480c4755fb9SThierry Reding 	return 0;
481c4755fb9SThierry Reding }
482c4755fb9SThierry Reding 
tegra_shared_plane_atomic_disable(struct drm_plane * plane,struct drm_atomic_state * state)483c4755fb9SThierry Reding static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
484977697e2SMaxime Ripard 					      struct drm_atomic_state *state)
485c4755fb9SThierry Reding {
486977697e2SMaxime Ripard 	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
487977697e2SMaxime Ripard 									   plane);
4881087fac1SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
4897cf77b27SThierry Reding 	struct tegra_dc *dc;
490c4755fb9SThierry Reding 	u32 value;
491fd67e9c6SThierry Reding 	int err;
492c4755fb9SThierry Reding 
493c4755fb9SThierry Reding 	/* rien ne va plus */
494c4755fb9SThierry Reding 	if (!old_state || !old_state->crtc)
495c4755fb9SThierry Reding 		return;
496c4755fb9SThierry Reding 
4977cf77b27SThierry Reding 	dc = to_tegra_dc(old_state->crtc);
4987cf77b27SThierry Reding 
499fd67e9c6SThierry Reding 	err = host1x_client_resume(&dc->client);
500fd67e9c6SThierry Reding 	if (err < 0) {
501fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to resume: %d\n", err);
502fd67e9c6SThierry Reding 		return;
503fd67e9c6SThierry Reding 	}
504fd67e9c6SThierry Reding 
505c4755fb9SThierry Reding 	/*
506c4755fb9SThierry Reding 	 * XXX Legacy helpers seem to sometimes call ->atomic_disable() even
507c4755fb9SThierry Reding 	 * on planes that are already disabled. Make sure we fallback to the
508c4755fb9SThierry Reding 	 * head for this particular state instead of crashing.
509c4755fb9SThierry Reding 	 */
510c4755fb9SThierry Reding 	if (WARN_ON(p->dc == NULL))
511c4755fb9SThierry Reding 		p->dc = dc;
512c4755fb9SThierry Reding 
513c4755fb9SThierry Reding 	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
514c4755fb9SThierry Reding 	value &= ~WIN_ENABLE;
515c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
516c4755fb9SThierry Reding 
517c4755fb9SThierry Reding 	tegra_dc_remove_shared_plane(dc, p);
518c4755fb9SThierry Reding 
519fd67e9c6SThierry Reding 	host1x_client_suspend(&dc->client);
520c4755fb9SThierry Reding }
521c4755fb9SThierry Reding 
compute_phase_incr(fixed20_12 in,unsigned int out)522ecc583e2SThierry Reding static inline u32 compute_phase_incr(fixed20_12 in, unsigned int out)
523ecc583e2SThierry Reding {
524ecc583e2SThierry Reding 	u64 tmp, tmp1, tmp2;
525ecc583e2SThierry Reding 
526ecc583e2SThierry Reding 	tmp = (u64)dfixed_trunc(in);
527ecc583e2SThierry Reding 	tmp2 = (u64)out;
528ecc583e2SThierry Reding 	tmp1 = (tmp << NFB) + (tmp2 >> 1);
529ecc583e2SThierry Reding 	do_div(tmp1, tmp2);
530ecc583e2SThierry Reding 
531ecc583e2SThierry Reding 	return lower_32_bits(tmp1);
532ecc583e2SThierry Reding }
533ecc583e2SThierry Reding 
tegra_shared_plane_atomic_update(struct drm_plane * plane,struct drm_atomic_state * state)534c4755fb9SThierry Reding static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
535977697e2SMaxime Ripard 					     struct drm_atomic_state *state)
536c4755fb9SThierry Reding {
53737418bf1SMaxime Ripard 	struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
53837418bf1SMaxime Ripard 									   plane);
53941016fe1SMaxime Ripard 	struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
540e05162c0SMaxime Ripard 	struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
541e05162c0SMaxime Ripard 	unsigned int zpos = new_state->normalized_zpos;
542e05162c0SMaxime Ripard 	struct drm_framebuffer *fb = new_state->fb;
5431087fac1SThierry Reding 	struct tegra_plane *p = to_tegra_plane(plane);
544ecc583e2SThierry Reding 	u32 value, min_width, bypass = 0;
545e16efff4SThierry Reding 	dma_addr_t base, addr_flag = 0;
546a649b133SThierry Reding 	unsigned int bpc, planes;
547a649b133SThierry Reding 	bool yuv;
548fd67e9c6SThierry Reding 	int err;
549c4755fb9SThierry Reding 
550c4755fb9SThierry Reding 	/* rien ne va plus */
551e05162c0SMaxime Ripard 	if (!new_state->crtc || !new_state->fb)
552c4755fb9SThierry Reding 		return;
553c4755fb9SThierry Reding 
554e05162c0SMaxime Ripard 	if (!new_state->visible) {
555977697e2SMaxime Ripard 		tegra_shared_plane_atomic_disable(plane, state);
556c4755fb9SThierry Reding 		return;
557c4755fb9SThierry Reding 	}
558c4755fb9SThierry Reding 
559fd67e9c6SThierry Reding 	err = host1x_client_resume(&dc->client);
560fd67e9c6SThierry Reding 	if (err < 0) {
561fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to resume: %d\n", err);
562fd67e9c6SThierry Reding 		return;
563fd67e9c6SThierry Reding 	}
564c4755fb9SThierry Reding 
565a649b133SThierry Reding 	yuv = tegra_plane_format_is_yuv(tegra_plane_state->format, &planes, &bpc);
566e16efff4SThierry Reding 
567c4755fb9SThierry Reding 	tegra_dc_assign_shared_plane(dc, p);
568c4755fb9SThierry Reding 
569c4755fb9SThierry Reding 	tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
570c4755fb9SThierry Reding 
571c4755fb9SThierry Reding 	/* blending */
572c4755fb9SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
573c4755fb9SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
574c4755fb9SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
575c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT);
576c4755fb9SThierry Reding 
577c4755fb9SThierry Reding 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
578c4755fb9SThierry Reding 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
579c4755fb9SThierry Reding 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
580c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT);
581c4755fb9SThierry Reding 
582ab7d3f58SThierry Reding 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos);
583c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
584c4755fb9SThierry Reding 
585ecc583e2SThierry Reding 	/* scaling */
586ecc583e2SThierry Reding 	min_width = min(new_state->src_w >> 16, new_state->crtc_w);
587ecc583e2SThierry Reding 
588ecc583e2SThierry Reding 	value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPC);
589ecc583e2SThierry Reding 
590ecc583e2SThierry Reding 	if (min_width < MAX_PIXELS_5TAP444(value)) {
591ecc583e2SThierry Reding 		value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
592ecc583e2SThierry Reding 	} else {
593ecc583e2SThierry Reding 		value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPE);
594ecc583e2SThierry Reding 
595ecc583e2SThierry Reding 		if (min_width < MAX_PIXELS_2TAP444(value))
596ecc583e2SThierry Reding 			value = HORIZONTAL_TAPS_2 | VERTICAL_TAPS_2;
597ecc583e2SThierry Reding 		else
598ecc583e2SThierry Reding 			dev_err(dc->dev, "invalid minimum width: %u\n", min_width);
599ecc583e2SThierry Reding 	}
600ecc583e2SThierry Reding 
601c4755fb9SThierry Reding 	value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
602c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
603c4755fb9SThierry Reding 
604ecc583e2SThierry Reding 	if (new_state->src_w != new_state->crtc_w << 16) {
605ecc583e2SThierry Reding 		fixed20_12 width = dfixed_init(new_state->src_w >> 16);
606ecc583e2SThierry Reding 		u32 incr = compute_phase_incr(width, new_state->crtc_w) & ~0x1;
607ecc583e2SThierry Reding 		u32 init = (1 << (NFB - 1)) + (incr >> 1);
608ecc583e2SThierry Reding 
609ecc583e2SThierry Reding 		tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_HPHASE_INCR);
610ecc583e2SThierry Reding 		tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_H_START_PHASE);
611ecc583e2SThierry Reding 	} else {
612ecc583e2SThierry Reding 		bypass |= INPUT_SCALER_HBYPASS;
613ecc583e2SThierry Reding 	}
614ecc583e2SThierry Reding 
615ecc583e2SThierry Reding 	if (new_state->src_h != new_state->crtc_h << 16) {
616ecc583e2SThierry Reding 		fixed20_12 height = dfixed_init(new_state->src_h >> 16);
617ecc583e2SThierry Reding 		u32 incr = compute_phase_incr(height, new_state->crtc_h) & ~0x1;
618ecc583e2SThierry Reding 		u32 init = (1 << (NFB - 1)) + (incr >> 1);
619ecc583e2SThierry Reding 
620ecc583e2SThierry Reding 		tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_VPHASE_INCR);
621ecc583e2SThierry Reding 		tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_V_START_PHASE);
622ecc583e2SThierry Reding 	} else {
623ecc583e2SThierry Reding 		bypass |= INPUT_SCALER_VBYPASS;
624ecc583e2SThierry Reding 	}
625ecc583e2SThierry Reding 
626ecc583e2SThierry Reding 	tegra_plane_writel(p, bypass, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
627c4755fb9SThierry Reding 
628c4755fb9SThierry Reding 	/* disable compression */
629c4755fb9SThierry Reding 	tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
630c4755fb9SThierry Reding 
6317b6f8467SThierry Reding #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
6327b6f8467SThierry Reding 	/*
6337b6f8467SThierry Reding 	 * Physical address bit 39 in Tegra194 is used as a switch for special
6347b6f8467SThierry Reding 	 * logic that swizzles the memory using either the legacy Tegra or the
6357b6f8467SThierry Reding 	 * dGPU sector layout.
6367b6f8467SThierry Reding 	 */
6377b6f8467SThierry Reding 	if (tegra_plane_state->tiling.sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU)
638e16efff4SThierry Reding 		addr_flag = BIT_ULL(39);
6397b6f8467SThierry Reding #endif
6407b6f8467SThierry Reding 
641e16efff4SThierry Reding 	base = tegra_plane_state->iova[0] + fb->offsets[0];
642e16efff4SThierry Reding 	base |= addr_flag;
643e16efff4SThierry Reding 
64441016fe1SMaxime Ripard 	tegra_plane_writel(p, tegra_plane_state->format, DC_WIN_COLOR_DEPTH);
645c4755fb9SThierry Reding 	tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
646c4755fb9SThierry Reding 
647e05162c0SMaxime Ripard 	value = V_POSITION(new_state->crtc_y) |
648e05162c0SMaxime Ripard 		H_POSITION(new_state->crtc_x);
649c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_POSITION);
650c4755fb9SThierry Reding 
651e05162c0SMaxime Ripard 	value = V_SIZE(new_state->crtc_h) | H_SIZE(new_state->crtc_w);
652c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_SIZE);
653c4755fb9SThierry Reding 
654c4755fb9SThierry Reding 	value = WIN_ENABLE | COLOR_EXPAND;
655c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
656c4755fb9SThierry Reding 
657ecc583e2SThierry Reding 	value = V_SIZE(new_state->src_h >> 16) | H_SIZE(new_state->src_w >> 16);
658c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
659c4755fb9SThierry Reding 
660c4755fb9SThierry Reding 	tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);
661c4755fb9SThierry Reding 	tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR);
662c4755fb9SThierry Reding 
663c4755fb9SThierry Reding 	value = PITCH(fb->pitches[0]);
664c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
665c4755fb9SThierry Reding 
666a649b133SThierry Reding 	if (yuv && planes > 1) {
667e16efff4SThierry Reding 		base = tegra_plane_state->iova[1] + fb->offsets[1];
668e16efff4SThierry Reding 		base |= addr_flag;
669e16efff4SThierry Reding 
670e16efff4SThierry Reding 		tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI_U);
671e16efff4SThierry Reding 		tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR_U);
672e16efff4SThierry Reding 
673a649b133SThierry Reding 		if (planes > 2) {
674e16efff4SThierry Reding 			base = tegra_plane_state->iova[2] + fb->offsets[2];
675e16efff4SThierry Reding 			base |= addr_flag;
676e16efff4SThierry Reding 
677e16efff4SThierry Reding 			tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI_V);
678e16efff4SThierry Reding 			tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR_V);
679a649b133SThierry Reding 		}
680e16efff4SThierry Reding 
681a649b133SThierry Reding 		value = PITCH_U(fb->pitches[1]);
682a649b133SThierry Reding 
683a649b133SThierry Reding 		if (planes > 2)
684a649b133SThierry Reding 			value |= PITCH_V(fb->pitches[2]);
685a649b133SThierry Reding 
686e16efff4SThierry Reding 		tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE_UV);
687e16efff4SThierry Reding 	} else {
688e16efff4SThierry Reding 		tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_U);
689e16efff4SThierry Reding 		tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_HI_U);
690e16efff4SThierry Reding 		tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_V);
691e16efff4SThierry Reding 		tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_HI_V);
692e16efff4SThierry Reding 		tegra_plane_writel(p, 0, DC_WIN_PLANAR_STORAGE_UV);
693e16efff4SThierry Reding 	}
694e16efff4SThierry Reding 
695e16efff4SThierry Reding 	value = CLAMP_BEFORE_BLEND | INPUT_RANGE_FULL;
696e16efff4SThierry Reding 
697e16efff4SThierry Reding 	if (yuv) {
698e16efff4SThierry Reding 		if (bpc < 12)
699e16efff4SThierry Reding 			value |= DEGAMMA_YUV8_10;
700e16efff4SThierry Reding 		else
701e16efff4SThierry Reding 			value |= DEGAMMA_YUV12;
702e16efff4SThierry Reding 
703e16efff4SThierry Reding 		/* XXX parameterize */
704e16efff4SThierry Reding 		value |= COLOR_SPACE_YUV_2020;
705e16efff4SThierry Reding 	} else {
706e16efff4SThierry Reding 		if (!tegra_plane_format_is_indexed(tegra_plane_state->format))
707e16efff4SThierry Reding 			value |= DEGAMMA_SRGB;
708e16efff4SThierry Reding 	}
709e16efff4SThierry Reding 
710c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
711c4755fb9SThierry Reding 
712e05162c0SMaxime Ripard 	value = OFFSET_X(new_state->src_y >> 16) |
713e05162c0SMaxime Ripard 		OFFSET_Y(new_state->src_x >> 16);
714c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT);
715c4755fb9SThierry Reding 
716c4755fb9SThierry Reding 	if (dc->soc->supports_block_linear) {
71741016fe1SMaxime Ripard 		unsigned long height = tegra_plane_state->tiling.value;
718c4755fb9SThierry Reding 
719c4755fb9SThierry Reding 		/* XXX */
72041016fe1SMaxime Ripard 		switch (tegra_plane_state->tiling.mode) {
721c4755fb9SThierry Reding 		case TEGRA_BO_TILING_MODE_PITCH:
722c4755fb9SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) |
723c4755fb9SThierry Reding 				DC_WINBUF_SURFACE_KIND_PITCH;
724c4755fb9SThierry Reding 			break;
725c4755fb9SThierry Reding 
726c4755fb9SThierry Reding 		/* XXX not supported on Tegra186 and later */
727c4755fb9SThierry Reding 		case TEGRA_BO_TILING_MODE_TILED:
728c4755fb9SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_TILED;
729c4755fb9SThierry Reding 			break;
730c4755fb9SThierry Reding 
731c4755fb9SThierry Reding 		case TEGRA_BO_TILING_MODE_BLOCK:
732c4755fb9SThierry Reding 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
733c4755fb9SThierry Reding 				DC_WINBUF_SURFACE_KIND_BLOCK;
734c4755fb9SThierry Reding 			break;
735c4755fb9SThierry Reding 		}
736c4755fb9SThierry Reding 
737c4755fb9SThierry Reding 		tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
738c4755fb9SThierry Reding 	}
739c4755fb9SThierry Reding 
740c4755fb9SThierry Reding 	/* disable gamut CSC */
741c4755fb9SThierry Reding 	value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL);
742c4755fb9SThierry Reding 	value &= ~CONTROL_CSC_ENABLE;
743c4755fb9SThierry Reding 	tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
744c4755fb9SThierry Reding 
745fd67e9c6SThierry Reding 	host1x_client_suspend(&dc->client);
746c4755fb9SThierry Reding }
747c4755fb9SThierry Reding 
748c4755fb9SThierry Reding static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
7492e8d8749SThierry Reding 	.prepare_fb = tegra_plane_prepare_fb,
7502e8d8749SThierry Reding 	.cleanup_fb = tegra_plane_cleanup_fb,
751c4755fb9SThierry Reding 	.atomic_check = tegra_shared_plane_atomic_check,
752c4755fb9SThierry Reding 	.atomic_update = tegra_shared_plane_atomic_update,
753c4755fb9SThierry Reding 	.atomic_disable = tegra_shared_plane_atomic_disable,
754c4755fb9SThierry Reding };
755c4755fb9SThierry Reding 
tegra_shared_plane_create(struct drm_device * drm,struct tegra_dc * dc,unsigned int wgrp,unsigned int index)756c4755fb9SThierry Reding struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
757c4755fb9SThierry Reding 					    struct tegra_dc *dc,
758c4755fb9SThierry Reding 					    unsigned int wgrp,
759c4755fb9SThierry Reding 					    unsigned int index)
760c4755fb9SThierry Reding {
761c4755fb9SThierry Reding 	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
762c4755fb9SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
763c4755fb9SThierry Reding 	struct tegra_display_hub *hub = tegra->hub;
764c4755fb9SThierry Reding 	struct tegra_shared_plane *plane;
76505d1adfeSThierry Reding 	unsigned int possible_crtcs;
766c4755fb9SThierry Reding 	unsigned int num_formats;
767e90124cbSThierry Reding 	const u64 *modifiers;
768c4755fb9SThierry Reding 	struct drm_plane *p;
769c4755fb9SThierry Reding 	const u32 *formats;
770c4755fb9SThierry Reding 	int err;
771c4755fb9SThierry Reding 
772c4755fb9SThierry Reding 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
773c4755fb9SThierry Reding 	if (!plane)
774c4755fb9SThierry Reding 		return ERR_PTR(-ENOMEM);
775c4755fb9SThierry Reding 
776c4755fb9SThierry Reding 	plane->base.offset = 0x0a00 + 0x0300 * index;
777c4755fb9SThierry Reding 	plane->base.index = index;
778c4755fb9SThierry Reding 
779c4755fb9SThierry Reding 	plane->wgrp = &hub->wgrps[wgrp];
780fd67e9c6SThierry Reding 	plane->wgrp->parent = &dc->client;
781c4755fb9SThierry Reding 
782c4755fb9SThierry Reding 	p = &plane->base.base;
783c4755fb9SThierry Reding 
78405d1adfeSThierry Reding 	/* planes can be assigned to arbitrary CRTCs */
78505d1adfeSThierry Reding 	possible_crtcs = BIT(tegra->num_crtcs) - 1;
78605d1adfeSThierry Reding 
787c4755fb9SThierry Reding 	num_formats = ARRAY_SIZE(tegra_shared_plane_formats);
788c4755fb9SThierry Reding 	formats = tegra_shared_plane_formats;
789e90124cbSThierry Reding 	modifiers = tegra_shared_plane_modifiers;
790c4755fb9SThierry Reding 
791c4755fb9SThierry Reding 	err = drm_universal_plane_init(drm, p, possible_crtcs,
792c4755fb9SThierry Reding 				       &tegra_plane_funcs, formats,
793e90124cbSThierry Reding 				       num_formats, modifiers, type, NULL);
794c4755fb9SThierry Reding 	if (err < 0) {
795c4755fb9SThierry Reding 		kfree(plane);
796c4755fb9SThierry Reding 		return ERR_PTR(err);
797c4755fb9SThierry Reding 	}
798c4755fb9SThierry Reding 
799c4755fb9SThierry Reding 	drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
800ab7d3f58SThierry Reding 	drm_plane_create_zpos_property(p, 0, 0, 255);
801c4755fb9SThierry Reding 
802c4755fb9SThierry Reding 	return p;
803c4755fb9SThierry Reding }
804c4755fb9SThierry Reding 
8050281c414SThierry Reding static struct drm_private_state *
tegra_display_hub_duplicate_state(struct drm_private_obj * obj)8060281c414SThierry Reding tegra_display_hub_duplicate_state(struct drm_private_obj *obj)
8070281c414SThierry Reding {
8080281c414SThierry Reding 	struct tegra_display_hub_state *state;
8090281c414SThierry Reding 
8100281c414SThierry Reding 	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
8110281c414SThierry Reding 	if (!state)
8120281c414SThierry Reding 		return NULL;
8130281c414SThierry Reding 
8140281c414SThierry Reding 	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
8150281c414SThierry Reding 
8160281c414SThierry Reding 	return &state->base;
8170281c414SThierry Reding }
8180281c414SThierry Reding 
tegra_display_hub_destroy_state(struct drm_private_obj * obj,struct drm_private_state * state)8190281c414SThierry Reding static void tegra_display_hub_destroy_state(struct drm_private_obj *obj,
8200281c414SThierry Reding 					    struct drm_private_state *state)
8210281c414SThierry Reding {
8220281c414SThierry Reding 	struct tegra_display_hub_state *hub_state =
8230281c414SThierry Reding 		to_tegra_display_hub_state(state);
8240281c414SThierry Reding 
8250281c414SThierry Reding 	kfree(hub_state);
8260281c414SThierry Reding }
8270281c414SThierry Reding 
8280281c414SThierry Reding static const struct drm_private_state_funcs tegra_display_hub_state_funcs = {
8290281c414SThierry Reding 	.atomic_duplicate_state = tegra_display_hub_duplicate_state,
8300281c414SThierry Reding 	.atomic_destroy_state = tegra_display_hub_destroy_state,
8310281c414SThierry Reding };
8320281c414SThierry Reding 
8330281c414SThierry Reding static struct tegra_display_hub_state *
tegra_display_hub_get_state(struct tegra_display_hub * hub,struct drm_atomic_state * state)8340281c414SThierry Reding tegra_display_hub_get_state(struct tegra_display_hub *hub,
8350281c414SThierry Reding 			    struct drm_atomic_state *state)
8360281c414SThierry Reding {
8370281c414SThierry Reding 	struct drm_private_state *priv;
8380281c414SThierry Reding 
8390281c414SThierry Reding 	priv = drm_atomic_get_private_obj_state(state, &hub->base);
8400281c414SThierry Reding 	if (IS_ERR(priv))
8410281c414SThierry Reding 		return ERR_CAST(priv);
8420281c414SThierry Reding 
8430281c414SThierry Reding 	return to_tegra_display_hub_state(priv);
8440281c414SThierry Reding }
8450281c414SThierry Reding 
tegra_display_hub_atomic_check(struct drm_device * drm,struct drm_atomic_state * state)8460281c414SThierry Reding int tegra_display_hub_atomic_check(struct drm_device *drm,
8470281c414SThierry Reding 				   struct drm_atomic_state *state)
8480281c414SThierry Reding {
8490281c414SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
8500281c414SThierry Reding 	struct tegra_display_hub_state *hub_state;
8510281c414SThierry Reding 	struct drm_crtc_state *old, *new;
8520281c414SThierry Reding 	struct drm_crtc *crtc;
8530281c414SThierry Reding 	unsigned int i;
8540281c414SThierry Reding 
8550281c414SThierry Reding 	if (!tegra->hub)
8560281c414SThierry Reding 		return 0;
8570281c414SThierry Reding 
8580281c414SThierry Reding 	hub_state = tegra_display_hub_get_state(tegra->hub, state);
8590281c414SThierry Reding 	if (IS_ERR(hub_state))
8600281c414SThierry Reding 		return PTR_ERR(hub_state);
8610281c414SThierry Reding 
8620281c414SThierry Reding 	/*
8630281c414SThierry Reding 	 * The display hub display clock needs to be fed by the display clock
8640281c414SThierry Reding 	 * with the highest frequency to ensure proper functioning of all the
8650281c414SThierry Reding 	 * displays.
8660281c414SThierry Reding 	 *
8670281c414SThierry Reding 	 * Note that this isn't used before Tegra186, but it doesn't hurt and
8680281c414SThierry Reding 	 * conditionalizing it would make the code less clean.
8690281c414SThierry Reding 	 */
8700281c414SThierry Reding 	for_each_oldnew_crtc_in_state(state, crtc, old, new, i) {
8710281c414SThierry Reding 		struct tegra_dc_state *dc = to_dc_state(new);
8720281c414SThierry Reding 
8730281c414SThierry Reding 		if (new->active) {
8740281c414SThierry Reding 			if (!hub_state->clk || dc->pclk > hub_state->rate) {
8750281c414SThierry Reding 				hub_state->dc = to_tegra_dc(dc->base.crtc);
8760281c414SThierry Reding 				hub_state->clk = hub_state->dc->clk;
8770281c414SThierry Reding 				hub_state->rate = dc->pclk;
8780281c414SThierry Reding 			}
8790281c414SThierry Reding 		}
8800281c414SThierry Reding 	}
8810281c414SThierry Reding 
8820281c414SThierry Reding 	return 0;
8830281c414SThierry Reding }
8840281c414SThierry Reding 
tegra_display_hub_update(struct tegra_dc * dc)885c4755fb9SThierry Reding static void tegra_display_hub_update(struct tegra_dc *dc)
886c4755fb9SThierry Reding {
887c4755fb9SThierry Reding 	u32 value;
888fd67e9c6SThierry Reding 	int err;
889c4755fb9SThierry Reding 
890fd67e9c6SThierry Reding 	err = host1x_client_resume(&dc->client);
891fd67e9c6SThierry Reding 	if (err < 0) {
892fd67e9c6SThierry Reding 		dev_err(dc->dev, "failed to resume: %d\n", err);
893fd67e9c6SThierry Reding 		return;
894fd67e9c6SThierry Reding 	}
895c4755fb9SThierry Reding 
896c4755fb9SThierry Reding 	value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
897c4755fb9SThierry Reding 	value &= ~LATENCY_EVENT;
898c4755fb9SThierry Reding 	tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL);
899c4755fb9SThierry Reding 
900c4755fb9SThierry Reding 	value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
901c4755fb9SThierry Reding 	value = CURS_SLOTS(1) | WGRP_SLOTS(1);
902c4755fb9SThierry Reding 	tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
903c4755fb9SThierry Reding 
904c4755fb9SThierry Reding 	tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL);
905c4755fb9SThierry Reding 	tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
906c4755fb9SThierry Reding 	tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
907c4755fb9SThierry Reding 	tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
908c4755fb9SThierry Reding 
909fd67e9c6SThierry Reding 	host1x_client_suspend(&dc->client);
910c4755fb9SThierry Reding }
911c4755fb9SThierry Reding 
tegra_display_hub_atomic_commit(struct drm_device * drm,struct drm_atomic_state * state)912c4755fb9SThierry Reding void tegra_display_hub_atomic_commit(struct drm_device *drm,
913c4755fb9SThierry Reding 				     struct drm_atomic_state *state)
914c4755fb9SThierry Reding {
915c4755fb9SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
916c4755fb9SThierry Reding 	struct tegra_display_hub *hub = tegra->hub;
9170281c414SThierry Reding 	struct tegra_display_hub_state *hub_state;
918c4755fb9SThierry Reding 	struct device *dev = hub->client.dev;
919c4755fb9SThierry Reding 	int err;
920c4755fb9SThierry Reding 
921b1a3dc0bSStefan Schake 	hub_state = to_tegra_display_hub_state(hub->base.state);
9220281c414SThierry Reding 
9230281c414SThierry Reding 	if (hub_state->clk) {
9240281c414SThierry Reding 		err = clk_set_rate(hub_state->clk, hub_state->rate);
925c4755fb9SThierry Reding 		if (err < 0)
926c4755fb9SThierry Reding 			dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
9270281c414SThierry Reding 				hub_state->clk, hub_state->rate);
928c4755fb9SThierry Reding 
9290281c414SThierry Reding 		err = clk_set_parent(hub->clk_disp, hub_state->clk);
930c4755fb9SThierry Reding 		if (err < 0)
931c4755fb9SThierry Reding 			dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
9320281c414SThierry Reding 				hub->clk_disp, hub_state->clk, err);
933c4755fb9SThierry Reding 	}
934c4755fb9SThierry Reding 
9350281c414SThierry Reding 	if (hub_state->dc)
9360281c414SThierry Reding 		tegra_display_hub_update(hub_state->dc);
937c4755fb9SThierry Reding }
938c4755fb9SThierry Reding 
tegra_display_hub_init(struct host1x_client * client)939c4755fb9SThierry Reding static int tegra_display_hub_init(struct host1x_client *client)
940c4755fb9SThierry Reding {
941c4755fb9SThierry Reding 	struct tegra_display_hub *hub = to_tegra_display_hub(client);
942608f43adSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
943c4755fb9SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
9440281c414SThierry Reding 	struct tegra_display_hub_state *state;
9450281c414SThierry Reding 
9460281c414SThierry Reding 	state = kzalloc(sizeof(*state), GFP_KERNEL);
9470281c414SThierry Reding 	if (!state)
9480281c414SThierry Reding 		return -ENOMEM;
9490281c414SThierry Reding 
950b962a120SRob Clark 	drm_atomic_private_obj_init(drm, &hub->base, &state->base,
9510281c414SThierry Reding 				    &tegra_display_hub_state_funcs);
952c4755fb9SThierry Reding 
953c4755fb9SThierry Reding 	tegra->hub = hub;
954c4755fb9SThierry Reding 
955c4755fb9SThierry Reding 	return 0;
956c4755fb9SThierry Reding }
957c4755fb9SThierry Reding 
tegra_display_hub_exit(struct host1x_client * client)958c4755fb9SThierry Reding static int tegra_display_hub_exit(struct host1x_client *client)
959c4755fb9SThierry Reding {
960608f43adSThierry Reding 	struct drm_device *drm = dev_get_drvdata(client->host);
961c4755fb9SThierry Reding 	struct tegra_drm *tegra = drm->dev_private;
962c4755fb9SThierry Reding 
9630281c414SThierry Reding 	drm_atomic_private_obj_fini(&tegra->hub->base);
964c4755fb9SThierry Reding 	tegra->hub = NULL;
965c4755fb9SThierry Reding 
966c4755fb9SThierry Reding 	return 0;
967c4755fb9SThierry Reding }
968c4755fb9SThierry Reding 
tegra_display_hub_runtime_suspend(struct host1x_client * client)969fd67e9c6SThierry Reding static int tegra_display_hub_runtime_suspend(struct host1x_client *client)
970fd67e9c6SThierry Reding {
971fd67e9c6SThierry Reding 	struct tegra_display_hub *hub = to_tegra_display_hub(client);
972fd67e9c6SThierry Reding 	struct device *dev = client->dev;
973fd67e9c6SThierry Reding 	unsigned int i = hub->num_heads;
974fd67e9c6SThierry Reding 	int err;
975fd67e9c6SThierry Reding 
976fd67e9c6SThierry Reding 	err = reset_control_assert(hub->rst);
977fd67e9c6SThierry Reding 	if (err < 0)
978fd67e9c6SThierry Reding 		return err;
979fd67e9c6SThierry Reding 
980fd67e9c6SThierry Reding 	while (i--)
981fd67e9c6SThierry Reding 		clk_disable_unprepare(hub->clk_heads[i]);
982fd67e9c6SThierry Reding 
983fd67e9c6SThierry Reding 	clk_disable_unprepare(hub->clk_hub);
984fd67e9c6SThierry Reding 	clk_disable_unprepare(hub->clk_dsc);
985fd67e9c6SThierry Reding 	clk_disable_unprepare(hub->clk_disp);
986fd67e9c6SThierry Reding 
987fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
988fd67e9c6SThierry Reding 
989fd67e9c6SThierry Reding 	return 0;
990fd67e9c6SThierry Reding }
991fd67e9c6SThierry Reding 
tegra_display_hub_runtime_resume(struct host1x_client * client)992fd67e9c6SThierry Reding static int tegra_display_hub_runtime_resume(struct host1x_client *client)
993fd67e9c6SThierry Reding {
994fd67e9c6SThierry Reding 	struct tegra_display_hub *hub = to_tegra_display_hub(client);
995fd67e9c6SThierry Reding 	struct device *dev = client->dev;
996fd67e9c6SThierry Reding 	unsigned int i;
997fd67e9c6SThierry Reding 	int err;
998fd67e9c6SThierry Reding 
999dcdfe271SQinglang Miao 	err = pm_runtime_resume_and_get(dev);
1000fd67e9c6SThierry Reding 	if (err < 0) {
1001fd67e9c6SThierry Reding 		dev_err(dev, "failed to get runtime PM: %d\n", err);
1002fd67e9c6SThierry Reding 		return err;
1003fd67e9c6SThierry Reding 	}
1004fd67e9c6SThierry Reding 
1005fd67e9c6SThierry Reding 	err = clk_prepare_enable(hub->clk_disp);
1006fd67e9c6SThierry Reding 	if (err < 0)
1007fd67e9c6SThierry Reding 		goto put_rpm;
1008fd67e9c6SThierry Reding 
1009fd67e9c6SThierry Reding 	err = clk_prepare_enable(hub->clk_dsc);
1010fd67e9c6SThierry Reding 	if (err < 0)
1011fd67e9c6SThierry Reding 		goto disable_disp;
1012fd67e9c6SThierry Reding 
1013fd67e9c6SThierry Reding 	err = clk_prepare_enable(hub->clk_hub);
1014fd67e9c6SThierry Reding 	if (err < 0)
1015fd67e9c6SThierry Reding 		goto disable_dsc;
1016fd67e9c6SThierry Reding 
1017fd67e9c6SThierry Reding 	for (i = 0; i < hub->num_heads; i++) {
1018fd67e9c6SThierry Reding 		err = clk_prepare_enable(hub->clk_heads[i]);
1019fd67e9c6SThierry Reding 		if (err < 0)
1020fd67e9c6SThierry Reding 			goto disable_heads;
1021fd67e9c6SThierry Reding 	}
1022fd67e9c6SThierry Reding 
1023fd67e9c6SThierry Reding 	err = reset_control_deassert(hub->rst);
1024fd67e9c6SThierry Reding 	if (err < 0)
1025fd67e9c6SThierry Reding 		goto disable_heads;
1026fd67e9c6SThierry Reding 
1027fd67e9c6SThierry Reding 	return 0;
1028fd67e9c6SThierry Reding 
1029fd67e9c6SThierry Reding disable_heads:
1030fd67e9c6SThierry Reding 	while (i--)
1031fd67e9c6SThierry Reding 		clk_disable_unprepare(hub->clk_heads[i]);
1032fd67e9c6SThierry Reding 
1033fd67e9c6SThierry Reding 	clk_disable_unprepare(hub->clk_hub);
1034fd67e9c6SThierry Reding disable_dsc:
1035fd67e9c6SThierry Reding 	clk_disable_unprepare(hub->clk_dsc);
1036fd67e9c6SThierry Reding disable_disp:
1037fd67e9c6SThierry Reding 	clk_disable_unprepare(hub->clk_disp);
1038fd67e9c6SThierry Reding put_rpm:
1039fd67e9c6SThierry Reding 	pm_runtime_put_sync(dev);
1040fd67e9c6SThierry Reding 	return err;
1041fd67e9c6SThierry Reding }
1042fd67e9c6SThierry Reding 
1043c4755fb9SThierry Reding static const struct host1x_client_ops tegra_display_hub_ops = {
1044c4755fb9SThierry Reding 	.init = tegra_display_hub_init,
1045c4755fb9SThierry Reding 	.exit = tegra_display_hub_exit,
1046fd67e9c6SThierry Reding 	.suspend = tegra_display_hub_runtime_suspend,
1047fd67e9c6SThierry Reding 	.resume = tegra_display_hub_runtime_resume,
1048c4755fb9SThierry Reding };
1049c4755fb9SThierry Reding 
tegra_display_hub_probe(struct platform_device * pdev)1050c4755fb9SThierry Reding static int tegra_display_hub_probe(struct platform_device *pdev)
1051c4755fb9SThierry Reding {
105286044e74SThierry Reding 	u64 dma_mask = dma_get_mask(pdev->dev.parent);
10530cffbde2SThierry Reding 	struct device_node *child = NULL;
1054c4755fb9SThierry Reding 	struct tegra_display_hub *hub;
10550cffbde2SThierry Reding 	struct clk *clk;
1056c4755fb9SThierry Reding 	unsigned int i;
1057c4755fb9SThierry Reding 	int err;
1058c4755fb9SThierry Reding 
105986044e74SThierry Reding 	err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask);
106086044e74SThierry Reding 	if (err < 0) {
106186044e74SThierry Reding 		dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err);
106286044e74SThierry Reding 		return err;
106386044e74SThierry Reding 	}
106486044e74SThierry Reding 
1065c4755fb9SThierry Reding 	hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
1066c4755fb9SThierry Reding 	if (!hub)
1067c4755fb9SThierry Reding 		return -ENOMEM;
1068c4755fb9SThierry Reding 
1069c4755fb9SThierry Reding 	hub->soc = of_device_get_match_data(&pdev->dev);
1070c4755fb9SThierry Reding 
1071c4755fb9SThierry Reding 	hub->clk_disp = devm_clk_get(&pdev->dev, "disp");
1072c4755fb9SThierry Reding 	if (IS_ERR(hub->clk_disp)) {
1073c4755fb9SThierry Reding 		err = PTR_ERR(hub->clk_disp);
1074c4755fb9SThierry Reding 		return err;
1075c4755fb9SThierry Reding 	}
1076c4755fb9SThierry Reding 
10775725daaaSThierry Reding 	if (hub->soc->supports_dsc) {
1078c4755fb9SThierry Reding 		hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc");
1079c4755fb9SThierry Reding 		if (IS_ERR(hub->clk_dsc)) {
1080c4755fb9SThierry Reding 			err = PTR_ERR(hub->clk_dsc);
1081c4755fb9SThierry Reding 			return err;
1082c4755fb9SThierry Reding 		}
10835725daaaSThierry Reding 	}
1084c4755fb9SThierry Reding 
1085c4755fb9SThierry Reding 	hub->clk_hub = devm_clk_get(&pdev->dev, "hub");
1086c4755fb9SThierry Reding 	if (IS_ERR(hub->clk_hub)) {
1087c4755fb9SThierry Reding 		err = PTR_ERR(hub->clk_hub);
1088c4755fb9SThierry Reding 		return err;
1089c4755fb9SThierry Reding 	}
1090c4755fb9SThierry Reding 
1091c4755fb9SThierry Reding 	hub->rst = devm_reset_control_get(&pdev->dev, "misc");
1092c4755fb9SThierry Reding 	if (IS_ERR(hub->rst)) {
1093c4755fb9SThierry Reding 		err = PTR_ERR(hub->rst);
1094c4755fb9SThierry Reding 		return err;
1095c4755fb9SThierry Reding 	}
1096c4755fb9SThierry Reding 
1097c4755fb9SThierry Reding 	hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps,
1098c4755fb9SThierry Reding 				  sizeof(*hub->wgrps), GFP_KERNEL);
1099c4755fb9SThierry Reding 	if (!hub->wgrps)
1100c4755fb9SThierry Reding 		return -ENOMEM;
1101c4755fb9SThierry Reding 
1102c4755fb9SThierry Reding 	for (i = 0; i < hub->soc->num_wgrps; i++) {
1103c4755fb9SThierry Reding 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
1104c4755fb9SThierry Reding 		char id[8];
1105c4755fb9SThierry Reding 
1106c4755fb9SThierry Reding 		snprintf(id, sizeof(id), "wgrp%u", i);
1107c4755fb9SThierry Reding 		mutex_init(&wgrp->lock);
1108c4755fb9SThierry Reding 		wgrp->usecount = 0;
1109c4755fb9SThierry Reding 		wgrp->index = i;
1110c4755fb9SThierry Reding 
1111c4755fb9SThierry Reding 		wgrp->rst = devm_reset_control_get(&pdev->dev, id);
1112c4755fb9SThierry Reding 		if (IS_ERR(wgrp->rst))
1113c4755fb9SThierry Reding 			return PTR_ERR(wgrp->rst);
1114c4755fb9SThierry Reding 
1115c4755fb9SThierry Reding 		err = reset_control_assert(wgrp->rst);
1116c4755fb9SThierry Reding 		if (err < 0)
1117c4755fb9SThierry Reding 			return err;
1118c4755fb9SThierry Reding 	}
1119c4755fb9SThierry Reding 
11200cffbde2SThierry Reding 	hub->num_heads = of_get_child_count(pdev->dev.of_node);
11210cffbde2SThierry Reding 
11220cffbde2SThierry Reding 	hub->clk_heads = devm_kcalloc(&pdev->dev, hub->num_heads, sizeof(clk),
11230cffbde2SThierry Reding 				      GFP_KERNEL);
11240cffbde2SThierry Reding 	if (!hub->clk_heads)
11250cffbde2SThierry Reding 		return -ENOMEM;
11260cffbde2SThierry Reding 
11270cffbde2SThierry Reding 	for (i = 0; i < hub->num_heads; i++) {
11280cffbde2SThierry Reding 		child = of_get_next_child(pdev->dev.of_node, child);
11290cffbde2SThierry Reding 		if (!child) {
11300cffbde2SThierry Reding 			dev_err(&pdev->dev, "failed to find node for head %u\n",
11310cffbde2SThierry Reding 				i);
11320cffbde2SThierry Reding 			return -ENODEV;
11330cffbde2SThierry Reding 		}
11340cffbde2SThierry Reding 
11350cffbde2SThierry Reding 		clk = devm_get_clk_from_child(&pdev->dev, child, "dc");
11360cffbde2SThierry Reding 		if (IS_ERR(clk)) {
11370cffbde2SThierry Reding 			dev_err(&pdev->dev, "failed to get clock for head %u\n",
11380cffbde2SThierry Reding 				i);
11390cffbde2SThierry Reding 			of_node_put(child);
11400cffbde2SThierry Reding 			return PTR_ERR(clk);
11410cffbde2SThierry Reding 		}
11420cffbde2SThierry Reding 
11430cffbde2SThierry Reding 		hub->clk_heads[i] = clk;
11440cffbde2SThierry Reding 	}
11450cffbde2SThierry Reding 
11460cffbde2SThierry Reding 	of_node_put(child);
11470cffbde2SThierry Reding 
1148c4755fb9SThierry Reding 	/* XXX: enable clock across reset? */
1149c4755fb9SThierry Reding 	err = reset_control_assert(hub->rst);
1150c4755fb9SThierry Reding 	if (err < 0)
1151c4755fb9SThierry Reding 		return err;
1152c4755fb9SThierry Reding 
1153c4755fb9SThierry Reding 	platform_set_drvdata(pdev, hub);
1154c4755fb9SThierry Reding 	pm_runtime_enable(&pdev->dev);
1155c4755fb9SThierry Reding 
1156c4755fb9SThierry Reding 	INIT_LIST_HEAD(&hub->client.list);
1157c4755fb9SThierry Reding 	hub->client.ops = &tegra_display_hub_ops;
1158c4755fb9SThierry Reding 	hub->client.dev = &pdev->dev;
1159c4755fb9SThierry Reding 
1160c4755fb9SThierry Reding 	err = host1x_client_register(&hub->client);
1161c4755fb9SThierry Reding 	if (err < 0)
1162c4755fb9SThierry Reding 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
1163c4755fb9SThierry Reding 			err);
1164c4755fb9SThierry Reding 
1165a101e3daSThierry Reding 	err = devm_of_platform_populate(&pdev->dev);
1166a101e3daSThierry Reding 	if (err < 0)
1167a101e3daSThierry Reding 		goto unregister;
1168a101e3daSThierry Reding 
1169a101e3daSThierry Reding 	return err;
1170a101e3daSThierry Reding 
1171a101e3daSThierry Reding unregister:
1172a101e3daSThierry Reding 	host1x_client_unregister(&hub->client);
1173a101e3daSThierry Reding 	pm_runtime_disable(&pdev->dev);
1174c4755fb9SThierry Reding 	return err;
1175c4755fb9SThierry Reding }
1176c4755fb9SThierry Reding 
tegra_display_hub_remove(struct platform_device * pdev)1177f9998eefSUwe Kleine-König static void tegra_display_hub_remove(struct platform_device *pdev)
1178c4755fb9SThierry Reding {
1179c4755fb9SThierry Reding 	struct tegra_display_hub *hub = platform_get_drvdata(pdev);
1180fd67e9c6SThierry Reding 	unsigned int i;
1181c4755fb9SThierry Reding 
11821d83d1a2SUwe Kleine-König 	host1x_client_unregister(&hub->client);
1183c4755fb9SThierry Reding 
1184fd67e9c6SThierry Reding 	for (i = 0; i < hub->soc->num_wgrps; i++) {
1185fd67e9c6SThierry Reding 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
1186fd67e9c6SThierry Reding 
1187fd67e9c6SThierry Reding 		mutex_destroy(&wgrp->lock);
1188fd67e9c6SThierry Reding 	}
1189fd67e9c6SThierry Reding 
1190c4755fb9SThierry Reding 	pm_runtime_disable(&pdev->dev);
1191c4755fb9SThierry Reding }
1192c4755fb9SThierry Reding 
1193c4755fb9SThierry Reding static const struct tegra_display_hub_soc tegra186_display_hub = {
1194c4755fb9SThierry Reding 	.num_wgrps = 6,
11955725daaaSThierry Reding 	.supports_dsc = true,
11965725daaaSThierry Reding };
11975725daaaSThierry Reding 
11985725daaaSThierry Reding static const struct tegra_display_hub_soc tegra194_display_hub = {
11995725daaaSThierry Reding 	.num_wgrps = 6,
12005725daaaSThierry Reding 	.supports_dsc = false,
1201c4755fb9SThierry Reding };
1202c4755fb9SThierry Reding 
1203c4755fb9SThierry Reding static const struct of_device_id tegra_display_hub_of_match[] = {
1204c4755fb9SThierry Reding 	{
12055725daaaSThierry Reding 		.compatible = "nvidia,tegra194-display",
12065725daaaSThierry Reding 		.data = &tegra194_display_hub
12075725daaaSThierry Reding 	}, {
1208c4755fb9SThierry Reding 		.compatible = "nvidia,tegra186-display",
1209c4755fb9SThierry Reding 		.data = &tegra186_display_hub
1210c4755fb9SThierry Reding 	}, {
1211c4755fb9SThierry Reding 		/* sentinel */
1212c4755fb9SThierry Reding 	}
1213c4755fb9SThierry Reding };
1214c4755fb9SThierry Reding MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match);
1215c4755fb9SThierry Reding 
1216c4755fb9SThierry Reding struct platform_driver tegra_display_hub_driver = {
1217c4755fb9SThierry Reding 	.driver = {
1218c4755fb9SThierry Reding 		.name = "tegra-display-hub",
1219c4755fb9SThierry Reding 		.of_match_table = tegra_display_hub_of_match,
1220c4755fb9SThierry Reding 	},
1221c4755fb9SThierry Reding 	.probe = tegra_display_hub_probe,
1222f9998eefSUwe Kleine-König 	.remove_new = tegra_display_hub_remove,
1223c4755fb9SThierry Reding };
1224