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