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