xref: /openbmc/linux/drivers/gpu/drm/tegra/hub.c (revision 1087fac18b8e3ec8fadf1595bbc46dce7ff08a81)
1 /*
2  * Copyright (C) 2017 NVIDIA CORPORATION.  All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 
9 #include <linux/clk.h>
10 #include <linux/host1x.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/of_device.h>
14 #include <linux/of_graph.h>
15 #include <linux/platform_device.h>
16 #include <linux/pm_runtime.h>
17 #include <linux/reset.h>
18 
19 #include <drm/drmP.h>
20 #include <drm/drm_atomic.h>
21 #include <drm/drm_atomic_helper.h>
22 #include <drm/drm_crtc_helper.h>
23 
24 #include "drm.h"
25 #include "dc.h"
26 #include "plane.h"
27 
28 static const u32 tegra_shared_plane_formats[] = {
29 	DRM_FORMAT_ARGB1555,
30 	DRM_FORMAT_RGB565,
31 	DRM_FORMAT_RGBA5551,
32 	DRM_FORMAT_ARGB8888,
33 	DRM_FORMAT_ABGR8888,
34 	/* new on Tegra114 */
35 	DRM_FORMAT_ABGR4444,
36 	DRM_FORMAT_ABGR1555,
37 	DRM_FORMAT_BGRA5551,
38 	DRM_FORMAT_XRGB1555,
39 	DRM_FORMAT_RGBX5551,
40 	DRM_FORMAT_XBGR1555,
41 	DRM_FORMAT_BGRX5551,
42 	DRM_FORMAT_BGR565,
43 	DRM_FORMAT_XRGB8888,
44 	DRM_FORMAT_XBGR8888,
45 	/* planar formats */
46 	DRM_FORMAT_UYVY,
47 	DRM_FORMAT_YUYV,
48 	DRM_FORMAT_YUV420,
49 	DRM_FORMAT_YUV422,
50 };
51 
52 static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
53 					      unsigned int offset)
54 {
55 	if (offset >= 0x500 && offset <= 0x581) {
56 		offset = 0x000 + (offset - 0x500);
57 		return plane->offset + offset;
58 	}
59 
60 	if (offset >= 0x700 && offset <= 0x73c) {
61 		offset = 0x180 + (offset - 0x700);
62 		return plane->offset + offset;
63 	}
64 
65 	if (offset >= 0x800 && offset <= 0x83e) {
66 		offset = 0x1c0 + (offset - 0x800);
67 		return plane->offset + offset;
68 	}
69 
70 	dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
71 
72 	return plane->offset + offset;
73 }
74 
75 static inline u32 tegra_plane_readl(struct tegra_plane *plane,
76 				    unsigned int offset)
77 {
78 	return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
79 }
80 
81 static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
82 				      unsigned int offset)
83 {
84 	tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
85 }
86 
87 static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
88 {
89 	mutex_lock(&wgrp->lock);
90 
91 	if (wgrp->usecount == 0) {
92 		pm_runtime_get_sync(wgrp->parent);
93 		reset_control_deassert(wgrp->rst);
94 	}
95 
96 	wgrp->usecount++;
97 	mutex_unlock(&wgrp->lock);
98 
99 	return 0;
100 }
101 
102 static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
103 {
104 	int err;
105 
106 	mutex_lock(&wgrp->lock);
107 
108 	if (wgrp->usecount == 1) {
109 		err = reset_control_assert(wgrp->rst);
110 		if (err < 0) {
111 			pr_err("failed to assert reset for window group %u\n",
112 			       wgrp->index);
113 		}
114 
115 		pm_runtime_put(wgrp->parent);
116 	}
117 
118 	wgrp->usecount--;
119 	mutex_unlock(&wgrp->lock);
120 }
121 
122 int tegra_display_hub_prepare(struct tegra_display_hub *hub)
123 {
124 	unsigned int i;
125 
126 	/*
127 	 * XXX Enabling/disabling windowgroups needs to happen when the owner
128 	 * display controller is disabled. There's currently no good point at
129 	 * which this could be executed, so unconditionally enable all window
130 	 * groups for now.
131 	 */
132 	for (i = 0; i < hub->soc->num_wgrps; i++) {
133 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
134 
135 		tegra_windowgroup_enable(wgrp);
136 	}
137 
138 	return 0;
139 }
140 
141 void tegra_display_hub_cleanup(struct tegra_display_hub *hub)
142 {
143 	unsigned int i;
144 
145 	/*
146 	 * XXX Remove this once window groups can be more fine-grainedly
147 	 * enabled and disabled.
148 	 */
149 	for (i = 0; i < hub->soc->num_wgrps; i++) {
150 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
151 
152 		tegra_windowgroup_disable(wgrp);
153 	}
154 }
155 
156 static void tegra_shared_plane_update(struct tegra_plane *plane)
157 {
158 	struct tegra_dc *dc = plane->dc;
159 	unsigned long timeout;
160 	u32 mask, value;
161 
162 	mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index;
163 	tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
164 
165 	timeout = jiffies + msecs_to_jiffies(1000);
166 
167 	while (time_before(jiffies, timeout)) {
168 		value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
169 		if ((value & mask) == 0)
170 			break;
171 
172 		usleep_range(100, 400);
173 	}
174 }
175 
176 static void tegra_shared_plane_activate(struct tegra_plane *plane)
177 {
178 	struct tegra_dc *dc = plane->dc;
179 	unsigned long timeout;
180 	u32 mask, value;
181 
182 	mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index;
183 	tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
184 
185 	timeout = jiffies + msecs_to_jiffies(1000);
186 
187 	while (time_before(jiffies, timeout)) {
188 		value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
189 		if ((value & mask) == 0)
190 			break;
191 
192 		usleep_range(100, 400);
193 	}
194 }
195 
196 static unsigned int
197 tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc)
198 {
199 	unsigned int offset =
200 		tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
201 
202 	return tegra_dc_readl(dc, offset) & OWNER_MASK;
203 }
204 
205 static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc,
206 				       struct tegra_plane *plane)
207 {
208 	struct device *dev = dc->dev;
209 
210 	if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) {
211 		if (plane->dc == dc)
212 			return true;
213 
214 		dev_WARN(dev, "head %u owns window %u but is not attached\n",
215 			 dc->pipe, plane->index);
216 	}
217 
218 	return false;
219 }
220 
221 static int tegra_shared_plane_set_owner(struct tegra_plane *plane,
222 					struct tegra_dc *new)
223 {
224 	unsigned int offset =
225 		tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
226 	struct tegra_dc *old = plane->dc, *dc = new ? new : old;
227 	struct device *dev = new ? new->dev : old->dev;
228 	unsigned int owner, index = plane->index;
229 	u32 value;
230 
231 	value = tegra_dc_readl(dc, offset);
232 	owner = value & OWNER_MASK;
233 
234 	if (new && (owner != OWNER_MASK && owner != new->pipe)) {
235 		dev_WARN(dev, "window %u owned by head %u\n", index, owner);
236 		return -EBUSY;
237 	}
238 
239 	/*
240 	 * This seems to happen whenever the head has been disabled with one
241 	 * or more windows being active. This is harmless because we'll just
242 	 * reassign the window to the new head anyway.
243 	 */
244 	if (old && owner == OWNER_MASK)
245 		dev_dbg(dev, "window %u not owned by head %u but %u\n", index,
246 			old->pipe, owner);
247 
248 	value &= ~OWNER_MASK;
249 
250 	if (new)
251 		value |= OWNER(new->pipe);
252 	else
253 		value |= OWNER_MASK;
254 
255 	tegra_dc_writel(dc, value, offset);
256 
257 	plane->dc = new;
258 
259 	return 0;
260 }
261 
262 static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
263 					 struct tegra_plane *plane)
264 {
265 	u32 value;
266 	int err;
267 
268 	if (!tegra_dc_owns_shared_plane(dc, plane)) {
269 		err = tegra_shared_plane_set_owner(plane, dc);
270 		if (err < 0)
271 			return;
272 	}
273 
274 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
275 	value |= MODE_FOUR_LINES;
276 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
277 
278 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
279 	value = SLOTS(1);
280 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
281 
282 	/* disable watermark */
283 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
284 	value &= ~LATENCY_CTL_MODE_ENABLE;
285 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
286 
287 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
288 	value |= WATERMARK_MASK;
289 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
290 
291 	/* pipe meter */
292 	value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
293 	value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0);
294 	tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
295 
296 	/* mempool entries */
297 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
298 	value = MEMPOOL_ENTRIES(0x331);
299 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
300 
301 	value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP);
302 	value &= ~THREAD_NUM_MASK;
303 	value |= THREAD_NUM(plane->base.index);
304 	value |= THREAD_GROUP_ENABLE;
305 	tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP);
306 
307 	tegra_shared_plane_update(plane);
308 	tegra_shared_plane_activate(plane);
309 }
310 
311 static void tegra_dc_remove_shared_plane(struct tegra_dc *dc,
312 					 struct tegra_plane *plane)
313 {
314 	tegra_shared_plane_set_owner(plane, NULL);
315 }
316 
317 static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
318 					   struct drm_plane_state *state)
319 {
320 	struct tegra_plane_state *plane_state = to_tegra_plane_state(state);
321 	struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane);
322 	struct tegra_bo_tiling *tiling = &plane_state->tiling;
323 	struct tegra_dc *dc = to_tegra_dc(state->crtc);
324 	int err;
325 
326 	/* no need for further checks if the plane is being disabled */
327 	if (!state->crtc || !state->fb)
328 		return 0;
329 
330 	err = tegra_plane_format(state->fb->format->format,
331 				 &plane_state->format,
332 				 &plane_state->swap);
333 	if (err < 0)
334 		return err;
335 
336 	err = tegra_fb_get_tiling(state->fb, tiling);
337 	if (err < 0)
338 		return err;
339 
340 	if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
341 	    !dc->soc->supports_block_linear) {
342 		DRM_ERROR("hardware doesn't support block linear mode\n");
343 		return -EINVAL;
344 	}
345 
346 	/*
347 	 * Tegra doesn't support different strides for U and V planes so we
348 	 * error out if the user tries to display a framebuffer with such a
349 	 * configuration.
350 	 */
351 	if (state->fb->format->num_planes > 2) {
352 		if (state->fb->pitches[2] != state->fb->pitches[1]) {
353 			DRM_ERROR("unsupported UV-plane configuration\n");
354 			return -EINVAL;
355 		}
356 	}
357 
358 	/* XXX scaling is not yet supported, add a check here */
359 
360 	err = tegra_plane_state_add(&tegra->base, state);
361 	if (err < 0)
362 		return err;
363 
364 	return 0;
365 }
366 
367 static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
368 					      struct drm_plane_state *old_state)
369 {
370 	struct tegra_dc *dc = to_tegra_dc(old_state->crtc);
371 	struct tegra_plane *p = to_tegra_plane(plane);
372 	u32 value;
373 
374 	/* rien ne va plus */
375 	if (!old_state || !old_state->crtc)
376 		return;
377 
378 	/*
379 	 * XXX Legacy helpers seem to sometimes call ->atomic_disable() even
380 	 * on planes that are already disabled. Make sure we fallback to the
381 	 * head for this particular state instead of crashing.
382 	 */
383 	if (WARN_ON(p->dc == NULL))
384 		p->dc = dc;
385 
386 	pm_runtime_get_sync(dc->dev);
387 
388 	value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
389 	value &= ~WIN_ENABLE;
390 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
391 
392 	tegra_dc_remove_shared_plane(dc, p);
393 
394 	pm_runtime_put(dc->dev);
395 }
396 
397 static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
398 					     struct drm_plane_state *old_state)
399 {
400 	struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
401 	struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
402 	struct drm_framebuffer *fb = plane->state->fb;
403 	struct tegra_plane *p = to_tegra_plane(plane);
404 	struct tegra_bo *bo;
405 	dma_addr_t base;
406 	u32 value;
407 
408 	/* rien ne va plus */
409 	if (!plane->state->crtc || !plane->state->fb)
410 		return;
411 
412 	if (!plane->state->visible) {
413 		tegra_shared_plane_atomic_disable(plane, old_state);
414 		return;
415 	}
416 
417 	pm_runtime_get_sync(dc->dev);
418 
419 	tegra_dc_assign_shared_plane(dc, p);
420 
421 	tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
422 
423 	/* blending */
424 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
425 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
426 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
427 	tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT);
428 
429 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
430 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
431 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
432 	tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT);
433 
434 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->depth);
435 	tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
436 
437 	/* bypass scaling */
438 	value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
439 	tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
440 
441 	value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS;
442 	tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
443 
444 	/* disable compression */
445 	tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
446 
447 	bo = tegra_fb_get_plane(fb, 0);
448 	base = bo->paddr;
449 
450 	tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH);
451 	tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
452 
453 	value = V_POSITION(plane->state->crtc_y) |
454 		H_POSITION(plane->state->crtc_x);
455 	tegra_plane_writel(p, value, DC_WIN_POSITION);
456 
457 	value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
458 	tegra_plane_writel(p, value, DC_WIN_SIZE);
459 
460 	value = WIN_ENABLE | COLOR_EXPAND;
461 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
462 
463 	value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
464 	tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
465 
466 	tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);
467 	tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR);
468 
469 	value = PITCH(fb->pitches[0]);
470 	tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
471 
472 	value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL;
473 	tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
474 
475 	value = OFFSET_X(plane->state->src_y >> 16) |
476 		OFFSET_Y(plane->state->src_x >> 16);
477 	tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT);
478 
479 	if (dc->soc->supports_block_linear) {
480 		unsigned long height = state->tiling.value;
481 
482 		/* XXX */
483 		switch (state->tiling.mode) {
484 		case TEGRA_BO_TILING_MODE_PITCH:
485 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) |
486 				DC_WINBUF_SURFACE_KIND_PITCH;
487 			break;
488 
489 		/* XXX not supported on Tegra186 and later */
490 		case TEGRA_BO_TILING_MODE_TILED:
491 			value = DC_WINBUF_SURFACE_KIND_TILED;
492 			break;
493 
494 		case TEGRA_BO_TILING_MODE_BLOCK:
495 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
496 				DC_WINBUF_SURFACE_KIND_BLOCK;
497 			break;
498 		}
499 
500 		tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
501 	}
502 
503 	/* disable gamut CSC */
504 	value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL);
505 	value &= ~CONTROL_CSC_ENABLE;
506 	tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
507 
508 	pm_runtime_put(dc->dev);
509 }
510 
511 static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
512 	.atomic_check = tegra_shared_plane_atomic_check,
513 	.atomic_update = tegra_shared_plane_atomic_update,
514 	.atomic_disable = tegra_shared_plane_atomic_disable,
515 };
516 
517 struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
518 					    struct tegra_dc *dc,
519 					    unsigned int wgrp,
520 					    unsigned int index)
521 {
522 	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
523 	struct tegra_drm *tegra = drm->dev_private;
524 	struct tegra_display_hub *hub = tegra->hub;
525 	/* planes can be assigned to arbitrary CRTCs */
526 	unsigned int possible_crtcs = 0x7;
527 	struct tegra_shared_plane *plane;
528 	unsigned int num_formats;
529 	struct drm_plane *p;
530 	const u32 *formats;
531 	int err;
532 
533 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
534 	if (!plane)
535 		return ERR_PTR(-ENOMEM);
536 
537 	plane->base.offset = 0x0a00 + 0x0300 * index;
538 	plane->base.index = index;
539 	plane->base.depth = 0;
540 
541 	plane->wgrp = &hub->wgrps[wgrp];
542 	plane->wgrp->parent = dc->dev;
543 
544 	p = &plane->base.base;
545 
546 	num_formats = ARRAY_SIZE(tegra_shared_plane_formats);
547 	formats = tegra_shared_plane_formats;
548 
549 	err = drm_universal_plane_init(drm, p, possible_crtcs,
550 				       &tegra_plane_funcs, formats,
551 				       num_formats, NULL, type, NULL);
552 	if (err < 0) {
553 		kfree(plane);
554 		return ERR_PTR(err);
555 	}
556 
557 	drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
558 
559 	return p;
560 }
561 
562 static void tegra_display_hub_update(struct tegra_dc *dc)
563 {
564 	u32 value;
565 
566 	pm_runtime_get_sync(dc->dev);
567 
568 	value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
569 	value &= ~LATENCY_EVENT;
570 	tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL);
571 
572 	value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
573 	value = CURS_SLOTS(1) | WGRP_SLOTS(1);
574 	tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
575 
576 	tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL);
577 	tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
578 	tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
579 	tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
580 
581 	pm_runtime_put(dc->dev);
582 }
583 
584 void tegra_display_hub_atomic_commit(struct drm_device *drm,
585 				     struct drm_atomic_state *state)
586 {
587 	struct tegra_atomic_state *s = to_tegra_atomic_state(state);
588 	struct tegra_drm *tegra = drm->dev_private;
589 	struct tegra_display_hub *hub = tegra->hub;
590 	struct device *dev = hub->client.dev;
591 	int err;
592 
593 	if (s->clk_disp) {
594 		err = clk_set_rate(s->clk_disp, s->rate);
595 		if (err < 0)
596 			dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
597 				s->clk_disp, s->rate);
598 
599 		err = clk_set_parent(hub->clk_disp, s->clk_disp);
600 		if (err < 0)
601 			dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
602 				hub->clk_disp, s->clk_disp, err);
603 	}
604 
605 	if (s->dc)
606 		tegra_display_hub_update(s->dc);
607 }
608 
609 static int tegra_display_hub_init(struct host1x_client *client)
610 {
611 	struct tegra_display_hub *hub = to_tegra_display_hub(client);
612 	struct drm_device *drm = dev_get_drvdata(client->parent);
613 	struct tegra_drm *tegra = drm->dev_private;
614 
615 	tegra->hub = hub;
616 
617 	return 0;
618 }
619 
620 static int tegra_display_hub_exit(struct host1x_client *client)
621 {
622 	struct drm_device *drm = dev_get_drvdata(client->parent);
623 	struct tegra_drm *tegra = drm->dev_private;
624 
625 	tegra->hub = NULL;
626 
627 	return 0;
628 }
629 
630 static const struct host1x_client_ops tegra_display_hub_ops = {
631 	.init = tegra_display_hub_init,
632 	.exit = tegra_display_hub_exit,
633 };
634 
635 static int tegra_display_hub_probe(struct platform_device *pdev)
636 {
637 	struct tegra_display_hub *hub;
638 	unsigned int i;
639 	int err;
640 
641 	hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
642 	if (!hub)
643 		return -ENOMEM;
644 
645 	hub->soc = of_device_get_match_data(&pdev->dev);
646 
647 	hub->clk_disp = devm_clk_get(&pdev->dev, "disp");
648 	if (IS_ERR(hub->clk_disp)) {
649 		err = PTR_ERR(hub->clk_disp);
650 		return err;
651 	}
652 
653 	hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc");
654 	if (IS_ERR(hub->clk_dsc)) {
655 		err = PTR_ERR(hub->clk_dsc);
656 		return err;
657 	}
658 
659 	hub->clk_hub = devm_clk_get(&pdev->dev, "hub");
660 	if (IS_ERR(hub->clk_hub)) {
661 		err = PTR_ERR(hub->clk_hub);
662 		return err;
663 	}
664 
665 	hub->rst = devm_reset_control_get(&pdev->dev, "misc");
666 	if (IS_ERR(hub->rst)) {
667 		err = PTR_ERR(hub->rst);
668 		return err;
669 	}
670 
671 	hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps,
672 				  sizeof(*hub->wgrps), GFP_KERNEL);
673 	if (!hub->wgrps)
674 		return -ENOMEM;
675 
676 	for (i = 0; i < hub->soc->num_wgrps; i++) {
677 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
678 		char id[8];
679 
680 		snprintf(id, sizeof(id), "wgrp%u", i);
681 		mutex_init(&wgrp->lock);
682 		wgrp->usecount = 0;
683 		wgrp->index = i;
684 
685 		wgrp->rst = devm_reset_control_get(&pdev->dev, id);
686 		if (IS_ERR(wgrp->rst))
687 			return PTR_ERR(wgrp->rst);
688 
689 		err = reset_control_assert(wgrp->rst);
690 		if (err < 0)
691 			return err;
692 	}
693 
694 	/* XXX: enable clock across reset? */
695 	err = reset_control_assert(hub->rst);
696 	if (err < 0)
697 		return err;
698 
699 	platform_set_drvdata(pdev, hub);
700 	pm_runtime_enable(&pdev->dev);
701 
702 	INIT_LIST_HEAD(&hub->client.list);
703 	hub->client.ops = &tegra_display_hub_ops;
704 	hub->client.dev = &pdev->dev;
705 
706 	err = host1x_client_register(&hub->client);
707 	if (err < 0)
708 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
709 			err);
710 
711 	return err;
712 }
713 
714 static int tegra_display_hub_remove(struct platform_device *pdev)
715 {
716 	struct tegra_display_hub *hub = platform_get_drvdata(pdev);
717 	int err;
718 
719 	err = host1x_client_unregister(&hub->client);
720 	if (err < 0) {
721 		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
722 			err);
723 	}
724 
725 	pm_runtime_disable(&pdev->dev);
726 
727 	return err;
728 }
729 
730 static int tegra_display_hub_suspend(struct device *dev)
731 {
732 	struct tegra_display_hub *hub = dev_get_drvdata(dev);
733 	int err;
734 
735 	err = reset_control_assert(hub->rst);
736 	if (err < 0)
737 		return err;
738 
739 	clk_disable_unprepare(hub->clk_hub);
740 	clk_disable_unprepare(hub->clk_dsc);
741 	clk_disable_unprepare(hub->clk_disp);
742 
743 	return 0;
744 }
745 
746 static int tegra_display_hub_resume(struct device *dev)
747 {
748 	struct tegra_display_hub *hub = dev_get_drvdata(dev);
749 	int err;
750 
751 	err = clk_prepare_enable(hub->clk_disp);
752 	if (err < 0)
753 		return err;
754 
755 	err = clk_prepare_enable(hub->clk_dsc);
756 	if (err < 0)
757 		goto disable_disp;
758 
759 	err = clk_prepare_enable(hub->clk_hub);
760 	if (err < 0)
761 		goto disable_dsc;
762 
763 	err = reset_control_deassert(hub->rst);
764 	if (err < 0)
765 		goto disable_hub;
766 
767 	return 0;
768 
769 disable_hub:
770 	clk_disable_unprepare(hub->clk_hub);
771 disable_dsc:
772 	clk_disable_unprepare(hub->clk_dsc);
773 disable_disp:
774 	clk_disable_unprepare(hub->clk_disp);
775 	return err;
776 }
777 
778 static const struct dev_pm_ops tegra_display_hub_pm_ops = {
779 	SET_RUNTIME_PM_OPS(tegra_display_hub_suspend,
780 			   tegra_display_hub_resume, NULL)
781 };
782 
783 static const struct tegra_display_hub_soc tegra186_display_hub = {
784 	.num_wgrps = 6,
785 };
786 
787 static const struct of_device_id tegra_display_hub_of_match[] = {
788 	{
789 		.compatible = "nvidia,tegra186-display",
790 		.data = &tegra186_display_hub
791 	}, {
792 		/* sentinel */
793 	}
794 };
795 MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match);
796 
797 struct platform_driver tegra_display_hub_driver = {
798 	.driver = {
799 		.name = "tegra-display-hub",
800 		.of_match_table = tegra_display_hub_of_match,
801 		.pm = &tegra_display_hub_pm_ops,
802 	},
803 	.probe = tegra_display_hub_probe,
804 	.remove = tegra_display_hub_remove,
805 };
806