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