1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. 4 */ 5 6 #include <linux/clk.h> 7 #include <linux/delay.h> 8 #include <linux/dma-mapping.h> 9 #include <linux/host1x.h> 10 #include <linux/module.h> 11 #include <linux/of.h> 12 #include <linux/of_graph.h> 13 #include <linux/of_platform.h> 14 #include <linux/platform_device.h> 15 #include <linux/pm_runtime.h> 16 #include <linux/reset.h> 17 18 #include <drm/drm_atomic.h> 19 #include <drm/drm_atomic_helper.h> 20 #include <drm/drm_blend.h> 21 #include <drm/drm_fourcc.h> 22 #include <drm/drm_framebuffer.h> 23 #include <drm/drm_print.h> 24 #include <drm/drm_probe_helper.h> 25 26 #include "drm.h" 27 #include "dc.h" 28 #include "plane.h" 29 30 #define NFB 24 31 32 static const u32 tegra_shared_plane_formats[] = { 33 DRM_FORMAT_ARGB1555, 34 DRM_FORMAT_RGB565, 35 DRM_FORMAT_RGBA5551, 36 DRM_FORMAT_ARGB8888, 37 DRM_FORMAT_ABGR8888, 38 /* new on Tegra114 */ 39 DRM_FORMAT_ABGR4444, 40 DRM_FORMAT_ABGR1555, 41 DRM_FORMAT_BGRA5551, 42 DRM_FORMAT_XRGB1555, 43 DRM_FORMAT_RGBX5551, 44 DRM_FORMAT_XBGR1555, 45 DRM_FORMAT_BGRX5551, 46 DRM_FORMAT_BGR565, 47 DRM_FORMAT_XRGB8888, 48 DRM_FORMAT_XBGR8888, 49 /* planar formats */ 50 DRM_FORMAT_UYVY, 51 DRM_FORMAT_YUYV, 52 DRM_FORMAT_YUV420, 53 DRM_FORMAT_YUV422, 54 }; 55 56 static const u64 tegra_shared_plane_modifiers[] = { 57 DRM_FORMAT_MOD_LINEAR, 58 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0), 59 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1), 60 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2), 61 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3), 62 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4), 63 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5), 64 /* 65 * The GPU sector layout is only supported on Tegra194, but these will 66 * be filtered out later on by ->format_mod_supported() on SoCs where 67 * it isn't supported. 68 */ 69 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT, 70 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT, 71 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT, 72 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT, 73 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT, 74 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT, 75 /* sentinel */ 76 DRM_FORMAT_MOD_INVALID 77 }; 78 79 static inline unsigned int tegra_plane_offset(struct tegra_plane *plane, 80 unsigned int offset) 81 { 82 if (offset >= 0x500 && offset <= 0x581) { 83 offset = 0x000 + (offset - 0x500); 84 return plane->offset + offset; 85 } 86 87 if (offset >= 0x700 && offset <= 0x73c) { 88 offset = 0x180 + (offset - 0x700); 89 return plane->offset + offset; 90 } 91 92 if (offset >= 0x800 && offset <= 0x83e) { 93 offset = 0x1c0 + (offset - 0x800); 94 return plane->offset + offset; 95 } 96 97 dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); 98 99 return plane->offset + offset; 100 } 101 102 static inline u32 tegra_plane_readl(struct tegra_plane *plane, 103 unsigned int offset) 104 { 105 return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); 106 } 107 108 static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value, 109 unsigned int offset) 110 { 111 tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); 112 } 113 114 static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp) 115 { 116 int err = 0; 117 118 mutex_lock(&wgrp->lock); 119 120 if (wgrp->usecount == 0) { 121 err = host1x_client_resume(wgrp->parent); 122 if (err < 0) { 123 dev_err(wgrp->parent->dev, "failed to resume: %d\n", err); 124 goto unlock; 125 } 126 127 reset_control_deassert(wgrp->rst); 128 } 129 130 wgrp->usecount++; 131 132 unlock: 133 mutex_unlock(&wgrp->lock); 134 return err; 135 } 136 137 static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp) 138 { 139 int err; 140 141 mutex_lock(&wgrp->lock); 142 143 if (wgrp->usecount == 1) { 144 err = reset_control_assert(wgrp->rst); 145 if (err < 0) { 146 pr_err("failed to assert reset for window group %u\n", 147 wgrp->index); 148 } 149 150 host1x_client_suspend(wgrp->parent); 151 } 152 153 wgrp->usecount--; 154 mutex_unlock(&wgrp->lock); 155 } 156 157 int tegra_display_hub_prepare(struct tegra_display_hub *hub) 158 { 159 unsigned int i; 160 161 /* 162 * XXX Enabling/disabling windowgroups needs to happen when the owner 163 * display controller is disabled. There's currently no good point at 164 * which this could be executed, so unconditionally enable all window 165 * groups for now. 166 */ 167 for (i = 0; i < hub->soc->num_wgrps; i++) { 168 struct tegra_windowgroup *wgrp = &hub->wgrps[i]; 169 170 /* Skip orphaned window group whose parent DC is disabled */ 171 if (wgrp->parent) 172 tegra_windowgroup_enable(wgrp); 173 } 174 175 return 0; 176 } 177 178 void tegra_display_hub_cleanup(struct tegra_display_hub *hub) 179 { 180 unsigned int i; 181 182 /* 183 * XXX Remove this once window groups can be more fine-grainedly 184 * enabled and disabled. 185 */ 186 for (i = 0; i < hub->soc->num_wgrps; i++) { 187 struct tegra_windowgroup *wgrp = &hub->wgrps[i]; 188 189 /* Skip orphaned window group whose parent DC is disabled */ 190 if (wgrp->parent) 191 tegra_windowgroup_disable(wgrp); 192 } 193 } 194 195 static void tegra_shared_plane_update(struct tegra_plane *plane) 196 { 197 struct tegra_dc *dc = plane->dc; 198 unsigned long timeout; 199 u32 mask, value; 200 201 mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index; 202 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); 203 204 timeout = jiffies + msecs_to_jiffies(1000); 205 206 while (time_before(jiffies, timeout)) { 207 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 208 if ((value & mask) == 0) 209 break; 210 211 usleep_range(100, 400); 212 } 213 } 214 215 static void tegra_shared_plane_activate(struct tegra_plane *plane) 216 { 217 struct tegra_dc *dc = plane->dc; 218 unsigned long timeout; 219 u32 mask, value; 220 221 mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index; 222 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); 223 224 timeout = jiffies + msecs_to_jiffies(1000); 225 226 while (time_before(jiffies, timeout)) { 227 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 228 if ((value & mask) == 0) 229 break; 230 231 usleep_range(100, 400); 232 } 233 } 234 235 static unsigned int 236 tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc) 237 { 238 unsigned int offset = 239 tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); 240 241 return tegra_dc_readl(dc, offset) & OWNER_MASK; 242 } 243 244 static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, 245 struct tegra_plane *plane) 246 { 247 struct device *dev = dc->dev; 248 249 if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) { 250 if (plane->dc == dc) 251 return true; 252 253 dev_WARN(dev, "head %u owns window %u but is not attached\n", 254 dc->pipe, plane->index); 255 } 256 257 return false; 258 } 259 260 static int tegra_shared_plane_set_owner(struct tegra_plane *plane, 261 struct tegra_dc *new) 262 { 263 unsigned int offset = 264 tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); 265 struct tegra_dc *old = plane->dc, *dc = new ? new : old; 266 struct device *dev = new ? new->dev : old->dev; 267 unsigned int owner, index = plane->index; 268 u32 value; 269 270 value = tegra_dc_readl(dc, offset); 271 owner = value & OWNER_MASK; 272 273 if (new && (owner != OWNER_MASK && owner != new->pipe)) { 274 dev_WARN(dev, "window %u owned by head %u\n", index, owner); 275 return -EBUSY; 276 } 277 278 /* 279 * This seems to happen whenever the head has been disabled with one 280 * or more windows being active. This is harmless because we'll just 281 * reassign the window to the new head anyway. 282 */ 283 if (old && owner == OWNER_MASK) 284 dev_dbg(dev, "window %u not owned by head %u but %u\n", index, 285 old->pipe, owner); 286 287 value &= ~OWNER_MASK; 288 289 if (new) 290 value |= OWNER(new->pipe); 291 else 292 value |= OWNER_MASK; 293 294 tegra_dc_writel(dc, value, offset); 295 296 plane->dc = new; 297 298 return 0; 299 } 300 301 static void tegra_shared_plane_setup_scaler(struct tegra_plane *plane) 302 { 303 static const unsigned int coeffs[192] = { 304 0x00000000, 0x3c70e400, 0x3bb037e4, 0x0c51cc9c, 305 0x00100001, 0x3bf0dbfa, 0x3d00f406, 0x3fe003ff, 306 0x00300002, 0x3b80cbf5, 0x3da1040d, 0x3fb003fe, 307 0x00400002, 0x3b20bff1, 0x3e511015, 0x3f9003fc, 308 0x00500002, 0x3ad0b3ed, 0x3f21201d, 0x3f5003fb, 309 0x00500003, 0x3aa0a3e9, 0x3ff13026, 0x3f2007f9, 310 0x00500403, 0x3a7097e6, 0x00e1402f, 0x3ee007f7, 311 0x00500403, 0x3a608be4, 0x01d14c38, 0x3ea00bf6, 312 0x00500403, 0x3a507fe2, 0x02e15c42, 0x3e500ff4, 313 0x00500402, 0x3a6073e1, 0x03f16c4d, 0x3e000ff2, 314 0x00400402, 0x3a706be0, 0x05117858, 0x3db013f0, 315 0x00300402, 0x3a905fe0, 0x06318863, 0x3d6017ee, 316 0x00300402, 0x3ab057e0, 0x0771986e, 0x3d001beb, 317 0x00200001, 0x3af04fe1, 0x08a1a47a, 0x3cb023e9, 318 0x00100001, 0x3b2047e2, 0x09e1b485, 0x3c6027e7, 319 0x00100000, 0x3b703fe2, 0x0b11c091, 0x3c002fe6, 320 0x3f203800, 0x0391103f, 0x3ff0a014, 0x0811606c, 321 0x3f2037ff, 0x0351083c, 0x03e11842, 0x3f203c00, 322 0x3f302fff, 0x03010439, 0x04311c45, 0x3f104401, 323 0x3f302fff, 0x02c0fc35, 0x04812448, 0x3f104802, 324 0x3f4027ff, 0x0270f832, 0x04c1284b, 0x3f205003, 325 0x3f4023ff, 0x0230f030, 0x0511304e, 0x3f205403, 326 0x3f601fff, 0x01f0e82d, 0x05613451, 0x3f205c04, 327 0x3f701bfe, 0x01b0e02a, 0x05a13c54, 0x3f306006, 328 0x3f7017fe, 0x0170d827, 0x05f14057, 0x3f406807, 329 0x3f8017ff, 0x0140d424, 0x0641445a, 0x3f406c08, 330 0x3fa013ff, 0x0100cc22, 0x0681485d, 0x3f507409, 331 0x3fa00fff, 0x00d0c41f, 0x06d14c60, 0x3f607c0b, 332 0x3fc00fff, 0x0090bc1c, 0x07115063, 0x3f80840c, 333 0x3fd00bff, 0x0070b41a, 0x07515465, 0x3f908c0e, 334 0x3fe007ff, 0x0040b018, 0x07915868, 0x3fb0900f, 335 0x3ff00400, 0x0010a816, 0x07d15c6a, 0x3fd09811, 336 0x00a04c0e, 0x0460f442, 0x0240a827, 0x05c15859, 337 0x0090440d, 0x0440f040, 0x0480fc43, 0x00b05010, 338 0x0080400c, 0x0410ec3e, 0x04910044, 0x00d05411, 339 0x0070380b, 0x03f0e83d, 0x04b10846, 0x00e05812, 340 0x0060340a, 0x03d0e43b, 0x04d10c48, 0x00f06013, 341 0x00503009, 0x03b0e039, 0x04e11449, 0x01106415, 342 0x00402c08, 0x0390d838, 0x05011c4b, 0x01206c16, 343 0x00302807, 0x0370d436, 0x0511204c, 0x01407018, 344 0x00302406, 0x0340d034, 0x0531244e, 0x01507419, 345 0x00202005, 0x0320cc32, 0x05412c50, 0x01707c1b, 346 0x00101c04, 0x0300c431, 0x05613451, 0x0180801d, 347 0x00101803, 0x02e0c02f, 0x05713853, 0x01a0881e, 348 0x00101002, 0x02b0bc2d, 0x05814054, 0x01c08c20, 349 0x00000c02, 0x02a0b82c, 0x05914455, 0x01e09421, 350 0x00000801, 0x0280b02a, 0x05a14c57, 0x02009c23, 351 0x00000400, 0x0260ac28, 0x05b15458, 0x0220a025, 352 }; 353 unsigned int ratio, row, column; 354 355 for (ratio = 0; ratio <= 2; ratio++) { 356 for (row = 0; row <= 15; row++) { 357 for (column = 0; column <= 3; column++) { 358 unsigned int index = (ratio << 6) + (row << 2) + column; 359 u32 value; 360 361 value = COEFF_INDEX(index) | COEFF_DATA(coeffs[index]); 362 tegra_plane_writel(plane, value, 363 DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_COEFF); 364 } 365 } 366 } 367 } 368 369 static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, 370 struct tegra_plane *plane) 371 { 372 u32 value; 373 int err; 374 375 if (!tegra_dc_owns_shared_plane(dc, plane)) { 376 err = tegra_shared_plane_set_owner(plane, dc); 377 if (err < 0) 378 return; 379 } 380 381 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); 382 value |= MODE_FOUR_LINES; 383 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); 384 385 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); 386 value = SLOTS(1); 387 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); 388 389 /* disable watermark */ 390 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); 391 value &= ~LATENCY_CTL_MODE_ENABLE; 392 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); 393 394 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); 395 value |= WATERMARK_MASK; 396 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); 397 398 /* pipe meter */ 399 value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); 400 value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0); 401 tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); 402 403 /* mempool entries */ 404 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); 405 value = MEMPOOL_ENTRIES(0x331); 406 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); 407 408 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP); 409 value &= ~THREAD_NUM_MASK; 410 value |= THREAD_NUM(plane->base.index); 411 value |= THREAD_GROUP_ENABLE; 412 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP); 413 414 tegra_shared_plane_setup_scaler(plane); 415 416 tegra_shared_plane_update(plane); 417 tegra_shared_plane_activate(plane); 418 } 419 420 static void tegra_dc_remove_shared_plane(struct tegra_dc *dc, 421 struct tegra_plane *plane) 422 { 423 tegra_shared_plane_set_owner(plane, NULL); 424 } 425 426 static int tegra_shared_plane_atomic_check(struct drm_plane *plane, 427 struct drm_atomic_commit *state) 428 { 429 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state, 430 plane); 431 struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state); 432 struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane); 433 struct tegra_bo_tiling *tiling = &plane_state->tiling; 434 struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc); 435 int err; 436 437 /* no need for further checks if the plane is being disabled */ 438 if (!new_plane_state->crtc || !new_plane_state->fb) 439 return 0; 440 441 err = tegra_plane_format(new_plane_state->fb->format->format, 442 &plane_state->format, 443 &plane_state->swap); 444 if (err < 0) 445 return err; 446 447 err = tegra_fb_get_tiling(new_plane_state->fb, tiling); 448 if (err < 0) 449 return err; 450 451 if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && 452 !dc->soc->supports_block_linear) { 453 DRM_ERROR("hardware doesn't support block linear mode\n"); 454 return -EINVAL; 455 } 456 457 if (tiling->sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU && 458 !dc->soc->supports_sector_layout) { 459 DRM_ERROR("hardware doesn't support GPU sector layout\n"); 460 return -EINVAL; 461 } 462 463 /* 464 * Tegra doesn't support different strides for U and V planes so we 465 * error out if the user tries to display a framebuffer with such a 466 * configuration. 467 */ 468 if (new_plane_state->fb->format->num_planes > 2) { 469 if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) { 470 DRM_ERROR("unsupported UV-plane configuration\n"); 471 return -EINVAL; 472 } 473 } 474 475 /* XXX scaling is not yet supported, add a check here */ 476 477 err = tegra_plane_state_add(&tegra->base, new_plane_state); 478 if (err < 0) 479 return err; 480 481 return 0; 482 } 483 484 static void tegra_shared_plane_atomic_disable(struct drm_plane *plane, 485 struct drm_atomic_commit *state) 486 { 487 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, 488 plane); 489 struct tegra_plane *p = to_tegra_plane(plane); 490 struct tegra_dc *dc; 491 u32 value; 492 int err; 493 494 /* rien ne va plus */ 495 if (!old_state || !old_state->crtc) 496 return; 497 498 dc = to_tegra_dc(old_state->crtc); 499 500 err = host1x_client_resume(&dc->client); 501 if (err < 0) { 502 dev_err(dc->dev, "failed to resume: %d\n", err); 503 return; 504 } 505 506 /* 507 * XXX Legacy helpers seem to sometimes call ->atomic_disable() even 508 * on planes that are already disabled. Make sure we fallback to the 509 * head for this particular state instead of crashing. 510 */ 511 if (WARN_ON(p->dc == NULL)) 512 p->dc = dc; 513 514 value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); 515 value &= ~WIN_ENABLE; 516 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 517 518 tegra_dc_remove_shared_plane(dc, p); 519 520 host1x_client_suspend(&dc->client); 521 } 522 523 static inline u32 compute_phase_incr(fixed20_12 in, unsigned int out) 524 { 525 u64 tmp, tmp1; 526 527 tmp = (u64)dfixed_trunc(in); 528 tmp1 = (tmp << NFB) + ((u64)out >> 1); 529 do_div(tmp1, out); 530 531 return lower_32_bits(tmp1); 532 } 533 534 static void tegra_shared_plane_atomic_update(struct drm_plane *plane, 535 struct drm_atomic_commit *state) 536 { 537 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state, 538 plane); 539 struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state); 540 struct tegra_dc *dc = to_tegra_dc(new_state->crtc); 541 unsigned int zpos = new_state->normalized_zpos; 542 struct drm_framebuffer *fb = new_state->fb; 543 struct tegra_plane *p = to_tegra_plane(plane); 544 u32 value, min_width, bypass = 0; 545 dma_addr_t base, addr_flag = 0; 546 unsigned int bpc, planes; 547 bool yuv; 548 int err; 549 550 /* rien ne va plus */ 551 if (!new_state->crtc || !new_state->fb) 552 return; 553 554 if (!new_state->visible) { 555 tegra_shared_plane_atomic_disable(plane, state); 556 return; 557 } 558 559 err = host1x_client_resume(&dc->client); 560 if (err < 0) { 561 dev_err(dc->dev, "failed to resume: %d\n", err); 562 return; 563 } 564 565 yuv = tegra_plane_format_is_yuv(tegra_plane_state->format, &planes, &bpc); 566 567 tegra_dc_assign_shared_plane(dc, p); 568 569 tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL); 570 571 /* blending */ 572 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 573 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 574 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 575 tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT); 576 577 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 578 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 579 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 580 tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT); 581 582 value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos); 583 tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); 584 585 /* scaling */ 586 min_width = min(new_state->src_w >> 16, new_state->crtc_w); 587 588 value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPC); 589 590 if (min_width < MAX_PIXELS_5TAP444(value)) { 591 value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5; 592 } else { 593 value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPE); 594 595 if (min_width < MAX_PIXELS_2TAP444(value)) 596 value = HORIZONTAL_TAPS_2 | VERTICAL_TAPS_2; 597 else 598 dev_err(dc->dev, "invalid minimum width: %u\n", min_width); 599 } 600 601 value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5; 602 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER); 603 604 if (new_state->src_w != new_state->crtc_w << 16) { 605 fixed20_12 width = dfixed_init(new_state->src_w >> 16); 606 u32 incr = compute_phase_incr(width, new_state->crtc_w) & ~0x1; 607 u32 init = (1 << (NFB - 1)) + (incr >> 1); 608 609 tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_HPHASE_INCR); 610 tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_H_START_PHASE); 611 } else { 612 bypass |= INPUT_SCALER_HBYPASS; 613 } 614 615 if (new_state->src_h != new_state->crtc_h << 16) { 616 fixed20_12 height = dfixed_init(new_state->src_h >> 16); 617 u32 incr = compute_phase_incr(height, new_state->crtc_h) & ~0x1; 618 u32 init = (1 << (NFB - 1)) + (incr >> 1); 619 620 tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_VPHASE_INCR); 621 tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_V_START_PHASE); 622 } else { 623 bypass |= INPUT_SCALER_VBYPASS; 624 } 625 626 tegra_plane_writel(p, bypass, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE); 627 628 /* disable compression */ 629 tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL); 630 631 #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT 632 /* 633 * Physical address bit 39 in Tegra194 is used as a switch for special 634 * logic that swizzles the memory using either the legacy Tegra or the 635 * dGPU sector layout. 636 */ 637 if (tegra_plane_state->tiling.sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU) 638 addr_flag = BIT_ULL(39); 639 #endif 640 641 base = tegra_plane_state->iova[0] + fb->offsets[0]; 642 base |= addr_flag; 643 644 tegra_plane_writel(p, tegra_plane_state->format, DC_WIN_COLOR_DEPTH); 645 tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS); 646 647 value = V_POSITION(new_state->crtc_y) | 648 H_POSITION(new_state->crtc_x); 649 tegra_plane_writel(p, value, DC_WIN_POSITION); 650 651 value = V_SIZE(new_state->crtc_h) | H_SIZE(new_state->crtc_w); 652 tegra_plane_writel(p, value, DC_WIN_SIZE); 653 654 value = WIN_ENABLE | COLOR_EXPAND; 655 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 656 657 value = V_SIZE(new_state->src_h >> 16) | H_SIZE(new_state->src_w >> 16); 658 tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE); 659 660 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI); 661 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR); 662 663 value = PITCH(fb->pitches[0]); 664 tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE); 665 666 if (yuv && planes > 1) { 667 base = tegra_plane_state->iova[1] + fb->offsets[1]; 668 base |= addr_flag; 669 670 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI_U); 671 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR_U); 672 673 if (planes > 2) { 674 base = tegra_plane_state->iova[2] + fb->offsets[2]; 675 base |= addr_flag; 676 677 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI_V); 678 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR_V); 679 } 680 681 value = PITCH_U(fb->pitches[1]); 682 683 if (planes > 2) 684 value |= PITCH_V(fb->pitches[2]); 685 686 tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE_UV); 687 } else { 688 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_U); 689 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_HI_U); 690 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_V); 691 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_HI_V); 692 tegra_plane_writel(p, 0, DC_WIN_PLANAR_STORAGE_UV); 693 } 694 695 value = CLAMP_BEFORE_BLEND | INPUT_RANGE_FULL; 696 697 if (yuv) { 698 if (bpc < 12) 699 value |= DEGAMMA_YUV8_10; 700 else 701 value |= DEGAMMA_YUV12; 702 703 /* XXX parameterize */ 704 value |= COLOR_SPACE_YUV_2020; 705 } else { 706 if (!tegra_plane_format_is_indexed(tegra_plane_state->format)) 707 value |= DEGAMMA_SRGB; 708 } 709 710 tegra_plane_writel(p, value, DC_WIN_SET_PARAMS); 711 712 value = OFFSET_X(new_state->src_y >> 16) | 713 OFFSET_Y(new_state->src_x >> 16); 714 tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT); 715 716 if (dc->soc->supports_block_linear) { 717 unsigned long height = tegra_plane_state->tiling.value; 718 719 /* XXX */ 720 switch (tegra_plane_state->tiling.mode) { 721 case TEGRA_BO_TILING_MODE_PITCH: 722 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) | 723 DC_WINBUF_SURFACE_KIND_PITCH; 724 break; 725 726 /* XXX not supported on Tegra186 and later */ 727 case TEGRA_BO_TILING_MODE_TILED: 728 value = DC_WINBUF_SURFACE_KIND_TILED; 729 break; 730 731 case TEGRA_BO_TILING_MODE_BLOCK: 732 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | 733 DC_WINBUF_SURFACE_KIND_BLOCK; 734 break; 735 } 736 737 tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND); 738 } 739 740 /* disable gamut CSC */ 741 value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL); 742 value &= ~CONTROL_CSC_ENABLE; 743 tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL); 744 745 host1x_client_suspend(&dc->client); 746 } 747 748 static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = { 749 .prepare_fb = tegra_plane_prepare_fb, 750 .cleanup_fb = tegra_plane_cleanup_fb, 751 .atomic_check = tegra_shared_plane_atomic_check, 752 .atomic_update = tegra_shared_plane_atomic_update, 753 .atomic_disable = tegra_shared_plane_atomic_disable, 754 }; 755 756 struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, 757 struct tegra_dc *dc, 758 unsigned int wgrp, 759 unsigned int index, 760 enum drm_plane_type type) 761 { 762 unsigned int blend_caps = BIT(DRM_MODE_BLEND_COVERAGE); 763 struct tegra_drm *tegra = drm->dev_private; 764 struct tegra_display_hub *hub = tegra->hub; 765 struct tegra_shared_plane *plane; 766 unsigned int possible_crtcs; 767 unsigned int num_formats; 768 const u64 *modifiers; 769 struct drm_plane *p; 770 const u32 *formats; 771 int err; 772 773 plane = kzalloc_obj(*plane); 774 if (!plane) 775 return ERR_PTR(-ENOMEM); 776 777 plane->base.offset = 0x0a00 + 0x0300 * index; 778 plane->base.index = index; 779 780 plane->wgrp = &hub->wgrps[wgrp]; 781 plane->wgrp->parent = &dc->client; 782 783 p = &plane->base.base; 784 785 /* planes can be assigned to arbitrary CRTCs */ 786 possible_crtcs = BIT(tegra->num_crtcs) - 1; 787 788 num_formats = ARRAY_SIZE(tegra_shared_plane_formats); 789 formats = tegra_shared_plane_formats; 790 modifiers = tegra_shared_plane_modifiers; 791 792 err = drm_universal_plane_init(drm, p, possible_crtcs, 793 &tegra_plane_funcs, formats, 794 num_formats, modifiers, type, NULL); 795 if (err < 0) { 796 kfree(plane); 797 return ERR_PTR(err); 798 } 799 800 drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs); 801 drm_plane_create_blend_mode_property(p, blend_caps); 802 drm_plane_create_zpos_property(p, 0, 0, 255); 803 804 return p; 805 } 806 807 static struct drm_private_state * 808 tegra_display_hub_duplicate_state(struct drm_private_obj *obj) 809 { 810 struct tegra_display_hub_state *state; 811 812 state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL); 813 if (!state) 814 return NULL; 815 816 __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base); 817 818 return &state->base; 819 } 820 821 static void tegra_display_hub_destroy_state(struct drm_private_obj *obj, 822 struct drm_private_state *state) 823 { 824 struct tegra_display_hub_state *hub_state = 825 to_tegra_display_hub_state(state); 826 827 kfree(hub_state); 828 } 829 830 static struct drm_private_state * 831 tegra_display_hub_create_state(struct drm_private_obj *obj) 832 { 833 struct tegra_display_hub_state *hub_state; 834 835 hub_state = kzalloc_obj(*hub_state); 836 if (!hub_state) 837 return ERR_PTR(-ENOMEM); 838 839 __drm_atomic_helper_private_obj_create_state(obj, &hub_state->base); 840 841 return &hub_state->base; 842 } 843 844 static const struct drm_private_state_funcs tegra_display_hub_state_funcs = { 845 .atomic_create_state = tegra_display_hub_create_state, 846 .atomic_duplicate_state = tegra_display_hub_duplicate_state, 847 .atomic_destroy_state = tegra_display_hub_destroy_state, 848 }; 849 850 static struct tegra_display_hub_state * 851 tegra_display_hub_get_state(struct tegra_display_hub *hub, 852 struct drm_atomic_commit *state) 853 { 854 struct drm_private_state *priv; 855 856 priv = drm_atomic_get_private_obj_state(state, &hub->base); 857 if (IS_ERR(priv)) 858 return ERR_CAST(priv); 859 860 return to_tegra_display_hub_state(priv); 861 } 862 863 int tegra_display_hub_atomic_check(struct drm_device *drm, 864 struct drm_atomic_commit *state) 865 { 866 struct tegra_drm *tegra = drm->dev_private; 867 struct tegra_display_hub_state *hub_state; 868 struct drm_crtc_state *old, *new; 869 struct drm_crtc *crtc; 870 unsigned int i; 871 872 if (!tegra->hub) 873 return 0; 874 875 hub_state = tegra_display_hub_get_state(tegra->hub, state); 876 if (IS_ERR(hub_state)) 877 return PTR_ERR(hub_state); 878 879 /* 880 * The display hub display clock needs to be fed by the display clock 881 * with the highest frequency to ensure proper functioning of all the 882 * displays. 883 * 884 * Note that this isn't used before Tegra186, but it doesn't hurt and 885 * conditionalizing it would make the code less clean. 886 */ 887 for_each_oldnew_crtc_in_state(state, crtc, old, new, i) { 888 struct tegra_dc_state *dc = to_dc_state(new); 889 890 if (new->active) { 891 if (!hub_state->clk || dc->pclk > hub_state->rate) { 892 hub_state->dc = to_tegra_dc(dc->base.crtc); 893 hub_state->clk = hub_state->dc->clk; 894 hub_state->rate = dc->pclk; 895 } 896 } 897 } 898 899 return 0; 900 } 901 902 static void tegra_display_hub_update(struct tegra_dc *dc) 903 { 904 u32 value; 905 int err; 906 907 err = host1x_client_resume(&dc->client); 908 if (err < 0) { 909 dev_err(dc->dev, "failed to resume: %d\n", err); 910 return; 911 } 912 913 value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL); 914 value &= ~LATENCY_EVENT; 915 tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL); 916 917 value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); 918 value = CURS_SLOTS(1) | WGRP_SLOTS(1); 919 tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); 920 921 tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL); 922 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 923 tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL); 924 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 925 926 host1x_client_suspend(&dc->client); 927 } 928 929 void tegra_display_hub_atomic_commit(struct drm_device *drm, 930 struct drm_atomic_commit *state) 931 { 932 struct tegra_drm *tegra = drm->dev_private; 933 struct tegra_display_hub *hub = tegra->hub; 934 struct tegra_display_hub_state *hub_state; 935 struct device *dev = hub->client.dev; 936 int err; 937 938 hub_state = to_tegra_display_hub_state(hub->base.state); 939 940 if (hub_state->clk) { 941 err = clk_set_rate(hub_state->clk, hub_state->rate); 942 if (err < 0) 943 dev_err(dev, "failed to set rate of %pC to %lu Hz\n", 944 hub_state->clk, hub_state->rate); 945 946 err = clk_set_parent(hub->clk_disp, hub_state->clk); 947 if (err < 0) 948 dev_err(dev, "failed to set parent of %pC to %pC: %d\n", 949 hub->clk_disp, hub_state->clk, err); 950 } 951 952 if (hub_state->dc) 953 tegra_display_hub_update(hub_state->dc); 954 } 955 956 static int tegra_display_hub_init(struct host1x_client *client) 957 { 958 struct tegra_display_hub *hub = to_tegra_display_hub(client); 959 struct drm_device *drm = dev_get_drvdata(client->host); 960 struct tegra_drm *tegra = drm->dev_private; 961 962 drm_atomic_private_obj_init(drm, &hub->base, 963 &tegra_display_hub_state_funcs); 964 965 tegra->hub = hub; 966 967 return 0; 968 } 969 970 static int tegra_display_hub_exit(struct host1x_client *client) 971 { 972 struct drm_device *drm = dev_get_drvdata(client->host); 973 struct tegra_drm *tegra = drm->dev_private; 974 975 drm_atomic_private_obj_fini(&tegra->hub->base); 976 tegra->hub = NULL; 977 978 return 0; 979 } 980 981 static int tegra_display_hub_runtime_suspend(struct host1x_client *client) 982 { 983 struct tegra_display_hub *hub = to_tegra_display_hub(client); 984 struct device *dev = client->dev; 985 unsigned int i = hub->num_heads; 986 int err; 987 988 err = reset_control_assert(hub->rst); 989 if (err < 0) 990 return err; 991 992 while (i--) 993 clk_disable_unprepare(hub->clk_heads[i]); 994 995 clk_disable_unprepare(hub->clk_hub); 996 clk_disable_unprepare(hub->clk_dsc); 997 clk_disable_unprepare(hub->clk_disp); 998 999 pm_runtime_put_sync(dev); 1000 1001 return 0; 1002 } 1003 1004 static int tegra_display_hub_runtime_resume(struct host1x_client *client) 1005 { 1006 struct tegra_display_hub *hub = to_tegra_display_hub(client); 1007 struct device *dev = client->dev; 1008 unsigned int i; 1009 int err; 1010 1011 err = pm_runtime_resume_and_get(dev); 1012 if (err < 0) { 1013 dev_err(dev, "failed to get runtime PM: %d\n", err); 1014 return err; 1015 } 1016 1017 err = clk_prepare_enable(hub->clk_disp); 1018 if (err < 0) 1019 goto put_rpm; 1020 1021 err = clk_prepare_enable(hub->clk_dsc); 1022 if (err < 0) 1023 goto disable_disp; 1024 1025 err = clk_prepare_enable(hub->clk_hub); 1026 if (err < 0) 1027 goto disable_dsc; 1028 1029 for (i = 0; i < hub->num_heads; i++) { 1030 err = clk_prepare_enable(hub->clk_heads[i]); 1031 if (err < 0) 1032 goto disable_heads; 1033 } 1034 1035 err = reset_control_deassert(hub->rst); 1036 if (err < 0) 1037 goto disable_heads; 1038 1039 return 0; 1040 1041 disable_heads: 1042 while (i--) 1043 clk_disable_unprepare(hub->clk_heads[i]); 1044 1045 clk_disable_unprepare(hub->clk_hub); 1046 disable_dsc: 1047 clk_disable_unprepare(hub->clk_dsc); 1048 disable_disp: 1049 clk_disable_unprepare(hub->clk_disp); 1050 put_rpm: 1051 pm_runtime_put_sync(dev); 1052 return err; 1053 } 1054 1055 static const struct host1x_client_ops tegra_display_hub_ops = { 1056 .init = tegra_display_hub_init, 1057 .exit = tegra_display_hub_exit, 1058 .suspend = tegra_display_hub_runtime_suspend, 1059 .resume = tegra_display_hub_runtime_resume, 1060 }; 1061 1062 static int tegra_display_hub_probe(struct platform_device *pdev) 1063 { 1064 u64 dma_mask = dma_get_mask(pdev->dev.parent); 1065 struct device_node *child = NULL; 1066 struct tegra_display_hub *hub; 1067 struct clk *clk; 1068 unsigned int i; 1069 int err; 1070 1071 err = dma_coerce_mask_and_coherent(&pdev->dev, dma_mask); 1072 if (err < 0) { 1073 dev_err(&pdev->dev, "failed to set DMA mask: %d\n", err); 1074 return err; 1075 } 1076 1077 hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL); 1078 if (!hub) 1079 return -ENOMEM; 1080 1081 hub->soc = of_device_get_match_data(&pdev->dev); 1082 1083 hub->clk_disp = devm_clk_get(&pdev->dev, "disp"); 1084 if (IS_ERR(hub->clk_disp)) { 1085 err = PTR_ERR(hub->clk_disp); 1086 return err; 1087 } 1088 1089 if (hub->soc->supports_dsc) { 1090 hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc"); 1091 if (IS_ERR(hub->clk_dsc)) { 1092 err = PTR_ERR(hub->clk_dsc); 1093 return err; 1094 } 1095 } 1096 1097 hub->clk_hub = devm_clk_get(&pdev->dev, "hub"); 1098 if (IS_ERR(hub->clk_hub)) { 1099 err = PTR_ERR(hub->clk_hub); 1100 return err; 1101 } 1102 1103 hub->rst = devm_reset_control_get(&pdev->dev, "misc"); 1104 if (IS_ERR(hub->rst)) { 1105 err = PTR_ERR(hub->rst); 1106 return err; 1107 } 1108 1109 hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps, 1110 sizeof(*hub->wgrps), GFP_KERNEL); 1111 if (!hub->wgrps) 1112 return -ENOMEM; 1113 1114 for (i = 0; i < hub->soc->num_wgrps; i++) { 1115 struct tegra_windowgroup *wgrp = &hub->wgrps[i]; 1116 char id[16]; 1117 1118 snprintf(id, sizeof(id), "wgrp%u", i); 1119 mutex_init(&wgrp->lock); 1120 wgrp->usecount = 0; 1121 wgrp->index = i; 1122 1123 wgrp->rst = devm_reset_control_get(&pdev->dev, id); 1124 if (IS_ERR(wgrp->rst)) 1125 return PTR_ERR(wgrp->rst); 1126 1127 err = reset_control_assert(wgrp->rst); 1128 if (err < 0) 1129 return err; 1130 } 1131 1132 hub->num_heads = of_get_child_count(pdev->dev.of_node); 1133 1134 hub->clk_heads = devm_kcalloc(&pdev->dev, hub->num_heads, sizeof(clk), 1135 GFP_KERNEL); 1136 if (!hub->clk_heads) 1137 return -ENOMEM; 1138 1139 for (i = 0; i < hub->num_heads; i++) { 1140 child = of_get_next_child(pdev->dev.of_node, child); 1141 if (!child) { 1142 dev_err(&pdev->dev, "failed to find node for head %u\n", 1143 i); 1144 return -ENODEV; 1145 } 1146 1147 clk = devm_get_clk_from_child(&pdev->dev, child, "dc"); 1148 if (IS_ERR(clk)) { 1149 dev_err(&pdev->dev, "failed to get clock for head %u\n", 1150 i); 1151 of_node_put(child); 1152 return PTR_ERR(clk); 1153 } 1154 1155 hub->clk_heads[i] = clk; 1156 } 1157 1158 of_node_put(child); 1159 1160 /* XXX: enable clock across reset? */ 1161 err = reset_control_assert(hub->rst); 1162 if (err < 0) 1163 return err; 1164 1165 platform_set_drvdata(pdev, hub); 1166 pm_runtime_enable(&pdev->dev); 1167 1168 INIT_LIST_HEAD(&hub->client.list); 1169 hub->client.ops = &tegra_display_hub_ops; 1170 hub->client.dev = &pdev->dev; 1171 1172 err = host1x_client_register(&hub->client); 1173 if (err < 0) 1174 dev_err(&pdev->dev, "failed to register host1x client: %d\n", 1175 err); 1176 1177 err = devm_of_platform_populate(&pdev->dev); 1178 if (err < 0) 1179 goto unregister; 1180 1181 return err; 1182 1183 unregister: 1184 host1x_client_unregister(&hub->client); 1185 pm_runtime_disable(&pdev->dev); 1186 return err; 1187 } 1188 1189 static void tegra_display_hub_remove(struct platform_device *pdev) 1190 { 1191 struct tegra_display_hub *hub = platform_get_drvdata(pdev); 1192 unsigned int i; 1193 1194 host1x_client_unregister(&hub->client); 1195 1196 for (i = 0; i < hub->soc->num_wgrps; i++) { 1197 struct tegra_windowgroup *wgrp = &hub->wgrps[i]; 1198 1199 mutex_destroy(&wgrp->lock); 1200 } 1201 1202 pm_runtime_disable(&pdev->dev); 1203 } 1204 1205 static const struct tegra_display_hub_soc tegra186_display_hub = { 1206 .num_wgrps = 6, 1207 .supports_dsc = true, 1208 }; 1209 1210 static const struct tegra_display_hub_soc tegra194_display_hub = { 1211 .num_wgrps = 6, 1212 .supports_dsc = false, 1213 }; 1214 1215 static const struct of_device_id tegra_display_hub_of_match[] = { 1216 { 1217 .compatible = "nvidia,tegra194-display", 1218 .data = &tegra194_display_hub 1219 }, { 1220 .compatible = "nvidia,tegra186-display", 1221 .data = &tegra186_display_hub 1222 }, { 1223 /* sentinel */ 1224 } 1225 }; 1226 MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match); 1227 1228 struct platform_driver tegra_display_hub_driver = { 1229 .driver = { 1230 .name = "tegra-display-hub", 1231 .of_match_table = tegra_display_hub_of_match, 1232 }, 1233 .probe = tegra_display_hub_probe, 1234 .remove = tegra_display_hub_remove, 1235 }; 1236