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