Merge branch 'x86-urgent-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cris-mirror.git] / drivers / gpu / drm / tegra / hub.c
blobe10a47d573138b164acc90c690c86ccddf9d0b08
1 /*
2 * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
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 */
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>
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>
24 #include "drm.h"
25 #include "dc.h"
26 #include "plane.h"
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,
52 static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
53 unsigned int offset)
55 if (offset >= 0x500 && offset <= 0x581) {
56 offset = 0x000 + (offset - 0x500);
57 return plane->offset + offset;
60 if (offset >= 0x700 && offset <= 0x73c) {
61 offset = 0x180 + (offset - 0x700);
62 return plane->offset + offset;
65 if (offset >= 0x800 && offset <= 0x83e) {
66 offset = 0x1c0 + (offset - 0x800);
67 return plane->offset + offset;
70 dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
72 return plane->offset + offset;
75 static inline u32 tegra_plane_readl(struct tegra_plane *plane,
76 unsigned int offset)
78 return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
81 static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
82 unsigned int offset)
84 tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
87 static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
89 mutex_lock(&wgrp->lock);
91 if (wgrp->usecount == 0) {
92 pm_runtime_get_sync(wgrp->parent);
93 reset_control_deassert(wgrp->rst);
96 wgrp->usecount++;
97 mutex_unlock(&wgrp->lock);
99 return 0;
102 static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
104 int err;
106 mutex_lock(&wgrp->lock);
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);
115 pm_runtime_put(wgrp->parent);
118 wgrp->usecount--;
119 mutex_unlock(&wgrp->lock);
122 int tegra_display_hub_prepare(struct tegra_display_hub *hub)
124 unsigned int i;
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.
132 for (i = 0; i < hub->soc->num_wgrps; i++) {
133 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
135 tegra_windowgroup_enable(wgrp);
138 return 0;
141 void tegra_display_hub_cleanup(struct tegra_display_hub *hub)
143 unsigned int i;
146 * XXX Remove this once window groups can be more fine-grainedly
147 * enabled and disabled.
149 for (i = 0; i < hub->soc->num_wgrps; i++) {
150 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
152 tegra_windowgroup_disable(wgrp);
156 static void tegra_shared_plane_update(struct tegra_plane *plane)
158 struct tegra_dc *dc = plane->dc;
159 unsigned long timeout;
160 u32 mask, value;
162 mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index;
163 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
165 timeout = jiffies + msecs_to_jiffies(1000);
167 while (time_before(jiffies, timeout)) {
168 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
169 if ((value & mask) == 0)
170 break;
172 usleep_range(100, 400);
176 static void tegra_shared_plane_activate(struct tegra_plane *plane)
178 struct tegra_dc *dc = plane->dc;
179 unsigned long timeout;
180 u32 mask, value;
182 mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index;
183 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
185 timeout = jiffies + msecs_to_jiffies(1000);
187 while (time_before(jiffies, timeout)) {
188 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
189 if ((value & mask) == 0)
190 break;
192 usleep_range(100, 400);
196 static unsigned int
197 tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc)
199 unsigned int offset =
200 tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
202 return tegra_dc_readl(dc, offset) & OWNER_MASK;
205 static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc,
206 struct tegra_plane *plane)
208 struct device *dev = dc->dev;
210 if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) {
211 if (plane->dc == dc)
212 return true;
214 dev_WARN(dev, "head %u owns window %u but is not attached\n",
215 dc->pipe, plane->index);
218 return false;
221 static int tegra_shared_plane_set_owner(struct tegra_plane *plane,
222 struct tegra_dc *new)
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;
231 value = tegra_dc_readl(dc, offset);
232 owner = value & OWNER_MASK;
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;
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.
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);
248 value &= ~OWNER_MASK;
250 if (new)
251 value |= OWNER(new->pipe);
252 else
253 value |= OWNER_MASK;
255 tegra_dc_writel(dc, value, offset);
257 plane->dc = new;
259 return 0;
262 static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
263 struct tegra_plane *plane)
265 u32 value;
266 int err;
268 if (!tegra_dc_owns_shared_plane(dc, plane)) {
269 err = tegra_shared_plane_set_owner(plane, dc);
270 if (err < 0)
271 return;
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);
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);
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);
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);
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);
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);
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);
307 tegra_shared_plane_update(plane);
308 tegra_shared_plane_activate(plane);
311 static void tegra_dc_remove_shared_plane(struct tegra_dc *dc,
312 struct tegra_plane *plane)
314 tegra_shared_plane_set_owner(plane, NULL);
317 static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
318 struct drm_plane_state *state)
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;
326 /* no need for further checks if the plane is being disabled */
327 if (!state->crtc || !state->fb)
328 return 0;
330 err = tegra_plane_format(state->fb->format->format,
331 &plane_state->format,
332 &plane_state->swap);
333 if (err < 0)
334 return err;
336 err = tegra_fb_get_tiling(state->fb, tiling);
337 if (err < 0)
338 return err;
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;
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.
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;
358 /* XXX scaling is not yet supported, add a check here */
360 err = tegra_plane_state_add(&tegra->base, state);
361 if (err < 0)
362 return err;
364 return 0;
367 static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
368 struct drm_plane_state *old_state)
370 struct tegra_dc *dc = to_tegra_dc(old_state->crtc);
371 struct tegra_plane *p = to_tegra_plane(plane);
372 u32 value;
374 /* rien ne va plus */
375 if (!old_state || !old_state->crtc)
376 return;
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.
383 if (WARN_ON(p->dc == NULL))
384 p->dc = dc;
386 pm_runtime_get_sync(dc->dev);
388 value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
389 value &= ~WIN_ENABLE;
390 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
392 tegra_dc_remove_shared_plane(dc, p);
394 pm_runtime_put(dc->dev);
397 static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
398 struct drm_plane_state *old_state)
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;
409 /* rien ne va plus */
410 if (!plane->state->crtc || !plane->state->fb)
411 return;
413 if (!plane->state->visible) {
414 tegra_shared_plane_atomic_disable(plane, old_state);
415 return;
418 pm_runtime_get_sync(dc->dev);
420 tegra_dc_assign_shared_plane(dc, p);
422 tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
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);
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);
435 value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos);
436 tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
438 /* bypass scaling */
439 value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
440 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
442 value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS;
443 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
445 /* disable compression */
446 tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
448 bo = tegra_fb_get_plane(fb, 0);
449 base = bo->paddr;
451 tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH);
452 tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
454 value = V_POSITION(plane->state->crtc_y) |
455 H_POSITION(plane->state->crtc_x);
456 tegra_plane_writel(p, value, DC_WIN_POSITION);
458 value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
459 tegra_plane_writel(p, value, DC_WIN_SIZE);
461 value = WIN_ENABLE | COLOR_EXPAND;
462 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
464 value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
465 tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
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);
470 value = PITCH(fb->pitches[0]);
471 tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
473 value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL;
474 tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
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);
480 if (dc->soc->supports_block_linear) {
481 unsigned long height = state->tiling.value;
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;
490 /* XXX not supported on Tegra186 and later */
491 case TEGRA_BO_TILING_MODE_TILED:
492 value = DC_WINBUF_SURFACE_KIND_TILED;
493 break;
495 case TEGRA_BO_TILING_MODE_BLOCK:
496 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
497 DC_WINBUF_SURFACE_KIND_BLOCK;
498 break;
501 tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
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);
509 pm_runtime_put(dc->dev);
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,
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)
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;
534 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
535 if (!plane)
536 return ERR_PTR(-ENOMEM);
538 plane->base.offset = 0x0a00 + 0x0300 * index;
539 plane->base.index = index;
541 plane->wgrp = &hub->wgrps[wgrp];
542 plane->wgrp->parent = dc->dev;
544 p = &plane->base.base;
546 num_formats = ARRAY_SIZE(tegra_shared_plane_formats);
547 formats = tegra_shared_plane_formats;
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);
557 drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
558 drm_plane_create_zpos_property(p, 0, 0, 255);
560 return p;
563 static void tegra_display_hub_update(struct tegra_dc *dc)
565 u32 value;
567 pm_runtime_get_sync(dc->dev);
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);
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);
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);
582 pm_runtime_put(dc->dev);
585 void tegra_display_hub_atomic_commit(struct drm_device *drm,
586 struct drm_atomic_state *state)
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;
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);
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);
606 if (s->dc)
607 tegra_display_hub_update(s->dc);
610 static int tegra_display_hub_init(struct host1x_client *client)
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;
616 tegra->hub = hub;
618 return 0;
621 static int tegra_display_hub_exit(struct host1x_client *client)
623 struct drm_device *drm = dev_get_drvdata(client->parent);
624 struct tegra_drm *tegra = drm->dev_private;
626 tegra->hub = NULL;
628 return 0;
631 static const struct host1x_client_ops tegra_display_hub_ops = {
632 .init = tegra_display_hub_init,
633 .exit = tegra_display_hub_exit,
636 static int tegra_display_hub_probe(struct platform_device *pdev)
638 struct tegra_display_hub *hub;
639 unsigned int i;
640 int err;
642 hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
643 if (!hub)
644 return -ENOMEM;
646 hub->soc = of_device_get_match_data(&pdev->dev);
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;
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;
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;
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;
672 hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps,
673 sizeof(*hub->wgrps), GFP_KERNEL);
674 if (!hub->wgrps)
675 return -ENOMEM;
677 for (i = 0; i < hub->soc->num_wgrps; i++) {
678 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
679 char id[8];
681 snprintf(id, sizeof(id), "wgrp%u", i);
682 mutex_init(&wgrp->lock);
683 wgrp->usecount = 0;
684 wgrp->index = i;
686 wgrp->rst = devm_reset_control_get(&pdev->dev, id);
687 if (IS_ERR(wgrp->rst))
688 return PTR_ERR(wgrp->rst);
690 err = reset_control_assert(wgrp->rst);
691 if (err < 0)
692 return err;
695 /* XXX: enable clock across reset? */
696 err = reset_control_assert(hub->rst);
697 if (err < 0)
698 return err;
700 platform_set_drvdata(pdev, hub);
701 pm_runtime_enable(&pdev->dev);
703 INIT_LIST_HEAD(&hub->client.list);
704 hub->client.ops = &tegra_display_hub_ops;
705 hub->client.dev = &pdev->dev;
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);
712 return err;
715 static int tegra_display_hub_remove(struct platform_device *pdev)
717 struct tegra_display_hub *hub = platform_get_drvdata(pdev);
718 int err;
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);
726 pm_runtime_disable(&pdev->dev);
728 return err;
731 static int __maybe_unused tegra_display_hub_suspend(struct device *dev)
733 struct tegra_display_hub *hub = dev_get_drvdata(dev);
734 int err;
736 err = reset_control_assert(hub->rst);
737 if (err < 0)
738 return err;
740 clk_disable_unprepare(hub->clk_hub);
741 clk_disable_unprepare(hub->clk_dsc);
742 clk_disable_unprepare(hub->clk_disp);
744 return 0;
747 static int __maybe_unused tegra_display_hub_resume(struct device *dev)
749 struct tegra_display_hub *hub = dev_get_drvdata(dev);
750 int err;
752 err = clk_prepare_enable(hub->clk_disp);
753 if (err < 0)
754 return err;
756 err = clk_prepare_enable(hub->clk_dsc);
757 if (err < 0)
758 goto disable_disp;
760 err = clk_prepare_enable(hub->clk_hub);
761 if (err < 0)
762 goto disable_dsc;
764 err = reset_control_deassert(hub->rst);
765 if (err < 0)
766 goto disable_hub;
768 return 0;
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;
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)
784 static const struct tegra_display_hub_soc tegra186_display_hub = {
785 .num_wgrps = 6,
788 static const struct of_device_id tegra_display_hub_of_match[] = {
790 .compatible = "nvidia,tegra186-display",
791 .data = &tegra186_display_hub
792 }, {
793 /* sentinel */
796 MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match);
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,
804 .probe = tegra_display_hub_probe,
805 .remove = tegra_display_hub_remove,