xref: /openbmc/linux/drivers/gpu/drm/tegra/hub.c (revision c7685190)
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 	unsigned int zpos = plane->state->normalized_zpos;
403 	struct drm_framebuffer *fb = plane->state->fb;
404 	struct tegra_plane *p = to_tegra_plane(plane);
405 	struct tegra_bo *bo;
406 	dma_addr_t base;
407 	u32 value;
408 
409 	/* rien ne va plus */
410 	if (!plane->state->crtc || !plane->state->fb)
411 		return;
412 
413 	if (!plane->state->visible) {
414 		tegra_shared_plane_atomic_disable(plane, old_state);
415 		return;
416 	}
417 
418 	pm_runtime_get_sync(dc->dev);
419 
420 	tegra_dc_assign_shared_plane(dc, p);
421 
422 	tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
423 
424 	/* blending */
425 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
426 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
427 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
428 	tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT);
429 
430 	value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
431 		BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
432 		BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
433 	tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT);
434 
435 	value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos);
436 	tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
437 
438 	/* bypass scaling */
439 	value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
440 	tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
441 
442 	value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS;
443 	tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
444 
445 	/* disable compression */
446 	tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
447 
448 	bo = tegra_fb_get_plane(fb, 0);
449 	base = bo->paddr;
450 
451 	tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH);
452 	tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
453 
454 	value = V_POSITION(plane->state->crtc_y) |
455 		H_POSITION(plane->state->crtc_x);
456 	tegra_plane_writel(p, value, DC_WIN_POSITION);
457 
458 	value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
459 	tegra_plane_writel(p, value, DC_WIN_SIZE);
460 
461 	value = WIN_ENABLE | COLOR_EXPAND;
462 	tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
463 
464 	value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
465 	tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
466 
467 	tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);
468 	tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR);
469 
470 	value = PITCH(fb->pitches[0]);
471 	tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
472 
473 	value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL;
474 	tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
475 
476 	value = OFFSET_X(plane->state->src_y >> 16) |
477 		OFFSET_Y(plane->state->src_x >> 16);
478 	tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT);
479 
480 	if (dc->soc->supports_block_linear) {
481 		unsigned long height = state->tiling.value;
482 
483 		/* XXX */
484 		switch (state->tiling.mode) {
485 		case TEGRA_BO_TILING_MODE_PITCH:
486 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) |
487 				DC_WINBUF_SURFACE_KIND_PITCH;
488 			break;
489 
490 		/* XXX not supported on Tegra186 and later */
491 		case TEGRA_BO_TILING_MODE_TILED:
492 			value = DC_WINBUF_SURFACE_KIND_TILED;
493 			break;
494 
495 		case TEGRA_BO_TILING_MODE_BLOCK:
496 			value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
497 				DC_WINBUF_SURFACE_KIND_BLOCK;
498 			break;
499 		}
500 
501 		tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
502 	}
503 
504 	/* disable gamut CSC */
505 	value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL);
506 	value &= ~CONTROL_CSC_ENABLE;
507 	tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
508 
509 	pm_runtime_put(dc->dev);
510 }
511 
512 static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
513 	.atomic_check = tegra_shared_plane_atomic_check,
514 	.atomic_update = tegra_shared_plane_atomic_update,
515 	.atomic_disable = tegra_shared_plane_atomic_disable,
516 };
517 
518 struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
519 					    struct tegra_dc *dc,
520 					    unsigned int wgrp,
521 					    unsigned int index)
522 {
523 	enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
524 	struct tegra_drm *tegra = drm->dev_private;
525 	struct tegra_display_hub *hub = tegra->hub;
526 	/* planes can be assigned to arbitrary CRTCs */
527 	unsigned int possible_crtcs = 0x7;
528 	struct tegra_shared_plane *plane;
529 	unsigned int num_formats;
530 	struct drm_plane *p;
531 	const u32 *formats;
532 	int err;
533 
534 	plane = kzalloc(sizeof(*plane), GFP_KERNEL);
535 	if (!plane)
536 		return ERR_PTR(-ENOMEM);
537 
538 	plane->base.offset = 0x0a00 + 0x0300 * index;
539 	plane->base.index = index;
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 	drm_plane_create_zpos_property(p, 0, 0, 255);
559 
560 	return p;
561 }
562 
563 static void tegra_display_hub_update(struct tegra_dc *dc)
564 {
565 	u32 value;
566 
567 	pm_runtime_get_sync(dc->dev);
568 
569 	value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
570 	value &= ~LATENCY_EVENT;
571 	tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL);
572 
573 	value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
574 	value = CURS_SLOTS(1) | WGRP_SLOTS(1);
575 	tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
576 
577 	tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL);
578 	tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
579 	tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
580 	tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
581 
582 	pm_runtime_put(dc->dev);
583 }
584 
585 void tegra_display_hub_atomic_commit(struct drm_device *drm,
586 				     struct drm_atomic_state *state)
587 {
588 	struct tegra_atomic_state *s = to_tegra_atomic_state(state);
589 	struct tegra_drm *tegra = drm->dev_private;
590 	struct tegra_display_hub *hub = tegra->hub;
591 	struct device *dev = hub->client.dev;
592 	int err;
593 
594 	if (s->clk_disp) {
595 		err = clk_set_rate(s->clk_disp, s->rate);
596 		if (err < 0)
597 			dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
598 				s->clk_disp, s->rate);
599 
600 		err = clk_set_parent(hub->clk_disp, s->clk_disp);
601 		if (err < 0)
602 			dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
603 				hub->clk_disp, s->clk_disp, err);
604 	}
605 
606 	if (s->dc)
607 		tegra_display_hub_update(s->dc);
608 }
609 
610 static int tegra_display_hub_init(struct host1x_client *client)
611 {
612 	struct tegra_display_hub *hub = to_tegra_display_hub(client);
613 	struct drm_device *drm = dev_get_drvdata(client->parent);
614 	struct tegra_drm *tegra = drm->dev_private;
615 
616 	tegra->hub = hub;
617 
618 	return 0;
619 }
620 
621 static int tegra_display_hub_exit(struct host1x_client *client)
622 {
623 	struct drm_device *drm = dev_get_drvdata(client->parent);
624 	struct tegra_drm *tegra = drm->dev_private;
625 
626 	tegra->hub = NULL;
627 
628 	return 0;
629 }
630 
631 static const struct host1x_client_ops tegra_display_hub_ops = {
632 	.init = tegra_display_hub_init,
633 	.exit = tegra_display_hub_exit,
634 };
635 
636 static int tegra_display_hub_probe(struct platform_device *pdev)
637 {
638 	struct tegra_display_hub *hub;
639 	unsigned int i;
640 	int err;
641 
642 	hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
643 	if (!hub)
644 		return -ENOMEM;
645 
646 	hub->soc = of_device_get_match_data(&pdev->dev);
647 
648 	hub->clk_disp = devm_clk_get(&pdev->dev, "disp");
649 	if (IS_ERR(hub->clk_disp)) {
650 		err = PTR_ERR(hub->clk_disp);
651 		return err;
652 	}
653 
654 	hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc");
655 	if (IS_ERR(hub->clk_dsc)) {
656 		err = PTR_ERR(hub->clk_dsc);
657 		return err;
658 	}
659 
660 	hub->clk_hub = devm_clk_get(&pdev->dev, "hub");
661 	if (IS_ERR(hub->clk_hub)) {
662 		err = PTR_ERR(hub->clk_hub);
663 		return err;
664 	}
665 
666 	hub->rst = devm_reset_control_get(&pdev->dev, "misc");
667 	if (IS_ERR(hub->rst)) {
668 		err = PTR_ERR(hub->rst);
669 		return err;
670 	}
671 
672 	hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps,
673 				  sizeof(*hub->wgrps), GFP_KERNEL);
674 	if (!hub->wgrps)
675 		return -ENOMEM;
676 
677 	for (i = 0; i < hub->soc->num_wgrps; i++) {
678 		struct tegra_windowgroup *wgrp = &hub->wgrps[i];
679 		char id[8];
680 
681 		snprintf(id, sizeof(id), "wgrp%u", i);
682 		mutex_init(&wgrp->lock);
683 		wgrp->usecount = 0;
684 		wgrp->index = i;
685 
686 		wgrp->rst = devm_reset_control_get(&pdev->dev, id);
687 		if (IS_ERR(wgrp->rst))
688 			return PTR_ERR(wgrp->rst);
689 
690 		err = reset_control_assert(wgrp->rst);
691 		if (err < 0)
692 			return err;
693 	}
694 
695 	/* XXX: enable clock across reset? */
696 	err = reset_control_assert(hub->rst);
697 	if (err < 0)
698 		return err;
699 
700 	platform_set_drvdata(pdev, hub);
701 	pm_runtime_enable(&pdev->dev);
702 
703 	INIT_LIST_HEAD(&hub->client.list);
704 	hub->client.ops = &tegra_display_hub_ops;
705 	hub->client.dev = &pdev->dev;
706 
707 	err = host1x_client_register(&hub->client);
708 	if (err < 0)
709 		dev_err(&pdev->dev, "failed to register host1x client: %d\n",
710 			err);
711 
712 	return err;
713 }
714 
715 static int tegra_display_hub_remove(struct platform_device *pdev)
716 {
717 	struct tegra_display_hub *hub = platform_get_drvdata(pdev);
718 	int err;
719 
720 	err = host1x_client_unregister(&hub->client);
721 	if (err < 0) {
722 		dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
723 			err);
724 	}
725 
726 	pm_runtime_disable(&pdev->dev);
727 
728 	return err;
729 }
730 
731 static int __maybe_unused tegra_display_hub_suspend(struct device *dev)
732 {
733 	struct tegra_display_hub *hub = dev_get_drvdata(dev);
734 	int err;
735 
736 	err = reset_control_assert(hub->rst);
737 	if (err < 0)
738 		return err;
739 
740 	clk_disable_unprepare(hub->clk_hub);
741 	clk_disable_unprepare(hub->clk_dsc);
742 	clk_disable_unprepare(hub->clk_disp);
743 
744 	return 0;
745 }
746 
747 static int __maybe_unused tegra_display_hub_resume(struct device *dev)
748 {
749 	struct tegra_display_hub *hub = dev_get_drvdata(dev);
750 	int err;
751 
752 	err = clk_prepare_enable(hub->clk_disp);
753 	if (err < 0)
754 		return err;
755 
756 	err = clk_prepare_enable(hub->clk_dsc);
757 	if (err < 0)
758 		goto disable_disp;
759 
760 	err = clk_prepare_enable(hub->clk_hub);
761 	if (err < 0)
762 		goto disable_dsc;
763 
764 	err = reset_control_deassert(hub->rst);
765 	if (err < 0)
766 		goto disable_hub;
767 
768 	return 0;
769 
770 disable_hub:
771 	clk_disable_unprepare(hub->clk_hub);
772 disable_dsc:
773 	clk_disable_unprepare(hub->clk_dsc);
774 disable_disp:
775 	clk_disable_unprepare(hub->clk_disp);
776 	return err;
777 }
778 
779 static const struct dev_pm_ops tegra_display_hub_pm_ops = {
780 	SET_RUNTIME_PM_OPS(tegra_display_hub_suspend,
781 			   tegra_display_hub_resume, NULL)
782 };
783 
784 static const struct tegra_display_hub_soc tegra186_display_hub = {
785 	.num_wgrps = 6,
786 };
787 
788 static const struct of_device_id tegra_display_hub_of_match[] = {
789 	{
790 		.compatible = "nvidia,tegra186-display",
791 		.data = &tegra186_display_hub
792 	}, {
793 		/* sentinel */
794 	}
795 };
796 MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match);
797 
798 struct platform_driver tegra_display_hub_driver = {
799 	.driver = {
800 		.name = "tegra-display-hub",
801 		.of_match_table = tegra_display_hub_of_match,
802 		.pm = &tegra_display_hub_pm_ops,
803 	},
804 	.probe = tegra_display_hub_probe,
805 	.remove = tegra_display_hub_remove,
806 };
807