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