0001
0002
0003
0004
0005
0006 #include <linux/clk.h>
0007 #include <linux/delay.h>
0008 #include <linux/dma-mapping.h>
0009 #include <linux/host1x.h>
0010 #include <linux/module.h>
0011 #include <linux/of.h>
0012 #include <linux/of_device.h>
0013 #include <linux/of_graph.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/pm_runtime.h>
0016 #include <linux/reset.h>
0017
0018 #include <drm/drm_atomic.h>
0019 #include <drm/drm_atomic_helper.h>
0020 #include <drm/drm_blend.h>
0021 #include <drm/drm_fourcc.h>
0022 #include <drm/drm_framebuffer.h>
0023 #include <drm/drm_probe_helper.h>
0024
0025 #include "drm.h"
0026 #include "dc.h"
0027 #include "plane.h"
0028
0029 #define NFB 24
0030
0031 static const u32 tegra_shared_plane_formats[] = {
0032 DRM_FORMAT_ARGB1555,
0033 DRM_FORMAT_RGB565,
0034 DRM_FORMAT_RGBA5551,
0035 DRM_FORMAT_ARGB8888,
0036 DRM_FORMAT_ABGR8888,
0037
0038 DRM_FORMAT_ABGR4444,
0039 DRM_FORMAT_ABGR1555,
0040 DRM_FORMAT_BGRA5551,
0041 DRM_FORMAT_XRGB1555,
0042 DRM_FORMAT_RGBX5551,
0043 DRM_FORMAT_XBGR1555,
0044 DRM_FORMAT_BGRX5551,
0045 DRM_FORMAT_BGR565,
0046 DRM_FORMAT_XRGB8888,
0047 DRM_FORMAT_XBGR8888,
0048
0049 DRM_FORMAT_UYVY,
0050 DRM_FORMAT_YUYV,
0051 DRM_FORMAT_YUV420,
0052 DRM_FORMAT_YUV422,
0053 };
0054
0055 static const u64 tegra_shared_plane_modifiers[] = {
0056 DRM_FORMAT_MOD_LINEAR,
0057 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0),
0058 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1),
0059 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2),
0060 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3),
0061 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4),
0062 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5),
0063
0064
0065
0066
0067
0068 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(0) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
0069 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(1) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
0070 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(2) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
0071 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(3) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
0072 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(4) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
0073 DRM_FORMAT_MOD_NVIDIA_16BX2_BLOCK(5) | DRM_FORMAT_MOD_NVIDIA_SECTOR_LAYOUT,
0074
0075 DRM_FORMAT_MOD_INVALID
0076 };
0077
0078 static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
0079 unsigned int offset)
0080 {
0081 if (offset >= 0x500 && offset <= 0x581) {
0082 offset = 0x000 + (offset - 0x500);
0083 return plane->offset + offset;
0084 }
0085
0086 if (offset >= 0x700 && offset <= 0x73c) {
0087 offset = 0x180 + (offset - 0x700);
0088 return plane->offset + offset;
0089 }
0090
0091 if (offset >= 0x800 && offset <= 0x83e) {
0092 offset = 0x1c0 + (offset - 0x800);
0093 return plane->offset + offset;
0094 }
0095
0096 dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
0097
0098 return plane->offset + offset;
0099 }
0100
0101 static inline u32 tegra_plane_readl(struct tegra_plane *plane,
0102 unsigned int offset)
0103 {
0104 return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
0105 }
0106
0107 static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
0108 unsigned int offset)
0109 {
0110 tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
0111 }
0112
0113 static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
0114 {
0115 int err = 0;
0116
0117 mutex_lock(&wgrp->lock);
0118
0119 if (wgrp->usecount == 0) {
0120 err = host1x_client_resume(wgrp->parent);
0121 if (err < 0) {
0122 dev_err(wgrp->parent->dev, "failed to resume: %d\n", err);
0123 goto unlock;
0124 }
0125
0126 reset_control_deassert(wgrp->rst);
0127 }
0128
0129 wgrp->usecount++;
0130
0131 unlock:
0132 mutex_unlock(&wgrp->lock);
0133 return err;
0134 }
0135
0136 static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
0137 {
0138 int err;
0139
0140 mutex_lock(&wgrp->lock);
0141
0142 if (wgrp->usecount == 1) {
0143 err = reset_control_assert(wgrp->rst);
0144 if (err < 0) {
0145 pr_err("failed to assert reset for window group %u\n",
0146 wgrp->index);
0147 }
0148
0149 host1x_client_suspend(wgrp->parent);
0150 }
0151
0152 wgrp->usecount--;
0153 mutex_unlock(&wgrp->lock);
0154 }
0155
0156 int tegra_display_hub_prepare(struct tegra_display_hub *hub)
0157 {
0158 unsigned int i;
0159
0160
0161
0162
0163
0164
0165
0166 for (i = 0; i < hub->soc->num_wgrps; i++) {
0167 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
0168
0169
0170 if (wgrp->parent)
0171 tegra_windowgroup_enable(wgrp);
0172 }
0173
0174 return 0;
0175 }
0176
0177 void tegra_display_hub_cleanup(struct tegra_display_hub *hub)
0178 {
0179 unsigned int i;
0180
0181
0182
0183
0184
0185 for (i = 0; i < hub->soc->num_wgrps; i++) {
0186 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
0187
0188
0189 if (wgrp->parent)
0190 tegra_windowgroup_disable(wgrp);
0191 }
0192 }
0193
0194 static void tegra_shared_plane_update(struct tegra_plane *plane)
0195 {
0196 struct tegra_dc *dc = plane->dc;
0197 unsigned long timeout;
0198 u32 mask, value;
0199
0200 mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index;
0201 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
0202
0203 timeout = jiffies + msecs_to_jiffies(1000);
0204
0205 while (time_before(jiffies, timeout)) {
0206 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
0207 if ((value & mask) == 0)
0208 break;
0209
0210 usleep_range(100, 400);
0211 }
0212 }
0213
0214 static void tegra_shared_plane_activate(struct tegra_plane *plane)
0215 {
0216 struct tegra_dc *dc = plane->dc;
0217 unsigned long timeout;
0218 u32 mask, value;
0219
0220 mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index;
0221 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
0222
0223 timeout = jiffies + msecs_to_jiffies(1000);
0224
0225 while (time_before(jiffies, timeout)) {
0226 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
0227 if ((value & mask) == 0)
0228 break;
0229
0230 usleep_range(100, 400);
0231 }
0232 }
0233
0234 static unsigned int
0235 tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc)
0236 {
0237 unsigned int offset =
0238 tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
0239
0240 return tegra_dc_readl(dc, offset) & OWNER_MASK;
0241 }
0242
0243 static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc,
0244 struct tegra_plane *plane)
0245 {
0246 struct device *dev = dc->dev;
0247
0248 if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) {
0249 if (plane->dc == dc)
0250 return true;
0251
0252 dev_WARN(dev, "head %u owns window %u but is not attached\n",
0253 dc->pipe, plane->index);
0254 }
0255
0256 return false;
0257 }
0258
0259 static int tegra_shared_plane_set_owner(struct tegra_plane *plane,
0260 struct tegra_dc *new)
0261 {
0262 unsigned int offset =
0263 tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
0264 struct tegra_dc *old = plane->dc, *dc = new ? new : old;
0265 struct device *dev = new ? new->dev : old->dev;
0266 unsigned int owner, index = plane->index;
0267 u32 value;
0268
0269 value = tegra_dc_readl(dc, offset);
0270 owner = value & OWNER_MASK;
0271
0272 if (new && (owner != OWNER_MASK && owner != new->pipe)) {
0273 dev_WARN(dev, "window %u owned by head %u\n", index, owner);
0274 return -EBUSY;
0275 }
0276
0277
0278
0279
0280
0281
0282 if (old && owner == OWNER_MASK)
0283 dev_dbg(dev, "window %u not owned by head %u but %u\n", index,
0284 old->pipe, owner);
0285
0286 value &= ~OWNER_MASK;
0287
0288 if (new)
0289 value |= OWNER(new->pipe);
0290 else
0291 value |= OWNER_MASK;
0292
0293 tegra_dc_writel(dc, value, offset);
0294
0295 plane->dc = new;
0296
0297 return 0;
0298 }
0299
0300 static void tegra_shared_plane_setup_scaler(struct tegra_plane *plane)
0301 {
0302 static const unsigned int coeffs[192] = {
0303 0x00000000, 0x3c70e400, 0x3bb037e4, 0x0c51cc9c,
0304 0x00100001, 0x3bf0dbfa, 0x3d00f406, 0x3fe003ff,
0305 0x00300002, 0x3b80cbf5, 0x3da1040d, 0x3fb003fe,
0306 0x00400002, 0x3b20bff1, 0x3e511015, 0x3f9003fc,
0307 0x00500002, 0x3ad0b3ed, 0x3f21201d, 0x3f5003fb,
0308 0x00500003, 0x3aa0a3e9, 0x3ff13026, 0x3f2007f9,
0309 0x00500403, 0x3a7097e6, 0x00e1402f, 0x3ee007f7,
0310 0x00500403, 0x3a608be4, 0x01d14c38, 0x3ea00bf6,
0311 0x00500403, 0x3a507fe2, 0x02e15c42, 0x3e500ff4,
0312 0x00500402, 0x3a6073e1, 0x03f16c4d, 0x3e000ff2,
0313 0x00400402, 0x3a706be0, 0x05117858, 0x3db013f0,
0314 0x00300402, 0x3a905fe0, 0x06318863, 0x3d6017ee,
0315 0x00300402, 0x3ab057e0, 0x0771986e, 0x3d001beb,
0316 0x00200001, 0x3af04fe1, 0x08a1a47a, 0x3cb023e9,
0317 0x00100001, 0x3b2047e2, 0x09e1b485, 0x3c6027e7,
0318 0x00100000, 0x3b703fe2, 0x0b11c091, 0x3c002fe6,
0319 0x3f203800, 0x0391103f, 0x3ff0a014, 0x0811606c,
0320 0x3f2037ff, 0x0351083c, 0x03e11842, 0x3f203c00,
0321 0x3f302fff, 0x03010439, 0x04311c45, 0x3f104401,
0322 0x3f302fff, 0x02c0fc35, 0x04812448, 0x3f104802,
0323 0x3f4027ff, 0x0270f832, 0x04c1284b, 0x3f205003,
0324 0x3f4023ff, 0x0230f030, 0x0511304e, 0x3f205403,
0325 0x3f601fff, 0x01f0e82d, 0x05613451, 0x3f205c04,
0326 0x3f701bfe, 0x01b0e02a, 0x05a13c54, 0x3f306006,
0327 0x3f7017fe, 0x0170d827, 0x05f14057, 0x3f406807,
0328 0x3f8017ff, 0x0140d424, 0x0641445a, 0x3f406c08,
0329 0x3fa013ff, 0x0100cc22, 0x0681485d, 0x3f507409,
0330 0x3fa00fff, 0x00d0c41f, 0x06d14c60, 0x3f607c0b,
0331 0x3fc00fff, 0x0090bc1c, 0x07115063, 0x3f80840c,
0332 0x3fd00bff, 0x0070b41a, 0x07515465, 0x3f908c0e,
0333 0x3fe007ff, 0x0040b018, 0x07915868, 0x3fb0900f,
0334 0x3ff00400, 0x0010a816, 0x07d15c6a, 0x3fd09811,
0335 0x00a04c0e, 0x0460f442, 0x0240a827, 0x05c15859,
0336 0x0090440d, 0x0440f040, 0x0480fc43, 0x00b05010,
0337 0x0080400c, 0x0410ec3e, 0x04910044, 0x00d05411,
0338 0x0070380b, 0x03f0e83d, 0x04b10846, 0x00e05812,
0339 0x0060340a, 0x03d0e43b, 0x04d10c48, 0x00f06013,
0340 0x00503009, 0x03b0e039, 0x04e11449, 0x01106415,
0341 0x00402c08, 0x0390d838, 0x05011c4b, 0x01206c16,
0342 0x00302807, 0x0370d436, 0x0511204c, 0x01407018,
0343 0x00302406, 0x0340d034, 0x0531244e, 0x01507419,
0344 0x00202005, 0x0320cc32, 0x05412c50, 0x01707c1b,
0345 0x00101c04, 0x0300c431, 0x05613451, 0x0180801d,
0346 0x00101803, 0x02e0c02f, 0x05713853, 0x01a0881e,
0347 0x00101002, 0x02b0bc2d, 0x05814054, 0x01c08c20,
0348 0x00000c02, 0x02a0b82c, 0x05914455, 0x01e09421,
0349 0x00000801, 0x0280b02a, 0x05a14c57, 0x02009c23,
0350 0x00000400, 0x0260ac28, 0x05b15458, 0x0220a025,
0351 };
0352 unsigned int ratio, row, column;
0353
0354 for (ratio = 0; ratio <= 2; ratio++) {
0355 for (row = 0; row <= 15; row++) {
0356 for (column = 0; column <= 3; column++) {
0357 unsigned int index = (ratio << 6) + (row << 2) + column;
0358 u32 value;
0359
0360 value = COEFF_INDEX(index) | COEFF_DATA(coeffs[index]);
0361 tegra_plane_writel(plane, value,
0362 DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_COEFF);
0363 }
0364 }
0365 }
0366 }
0367
0368 static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
0369 struct tegra_plane *plane)
0370 {
0371 u32 value;
0372 int err;
0373
0374 if (!tegra_dc_owns_shared_plane(dc, plane)) {
0375 err = tegra_shared_plane_set_owner(plane, dc);
0376 if (err < 0)
0377 return;
0378 }
0379
0380 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
0381 value |= MODE_FOUR_LINES;
0382 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
0383
0384 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
0385 value = SLOTS(1);
0386 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
0387
0388
0389 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
0390 value &= ~LATENCY_CTL_MODE_ENABLE;
0391 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
0392
0393 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
0394 value |= WATERMARK_MASK;
0395 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
0396
0397
0398 value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
0399 value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0);
0400 tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
0401
0402
0403 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
0404 value = MEMPOOL_ENTRIES(0x331);
0405 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
0406
0407 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP);
0408 value &= ~THREAD_NUM_MASK;
0409 value |= THREAD_NUM(plane->base.index);
0410 value |= THREAD_GROUP_ENABLE;
0411 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP);
0412
0413 tegra_shared_plane_setup_scaler(plane);
0414
0415 tegra_shared_plane_update(plane);
0416 tegra_shared_plane_activate(plane);
0417 }
0418
0419 static void tegra_dc_remove_shared_plane(struct tegra_dc *dc,
0420 struct tegra_plane *plane)
0421 {
0422 tegra_shared_plane_set_owner(plane, NULL);
0423 }
0424
0425 static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
0426 struct drm_atomic_state *state)
0427 {
0428 struct drm_plane_state *new_plane_state = drm_atomic_get_new_plane_state(state,
0429 plane);
0430 struct tegra_plane_state *plane_state = to_tegra_plane_state(new_plane_state);
0431 struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane);
0432 struct tegra_bo_tiling *tiling = &plane_state->tiling;
0433 struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
0434 int err;
0435
0436
0437 if (!new_plane_state->crtc || !new_plane_state->fb)
0438 return 0;
0439
0440 err = tegra_plane_format(new_plane_state->fb->format->format,
0441 &plane_state->format,
0442 &plane_state->swap);
0443 if (err < 0)
0444 return err;
0445
0446 err = tegra_fb_get_tiling(new_plane_state->fb, tiling);
0447 if (err < 0)
0448 return err;
0449
0450 if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
0451 !dc->soc->supports_block_linear) {
0452 DRM_ERROR("hardware doesn't support block linear mode\n");
0453 return -EINVAL;
0454 }
0455
0456 if (tiling->sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU &&
0457 !dc->soc->supports_sector_layout) {
0458 DRM_ERROR("hardware doesn't support GPU sector layout\n");
0459 return -EINVAL;
0460 }
0461
0462
0463
0464
0465
0466
0467 if (new_plane_state->fb->format->num_planes > 2) {
0468 if (new_plane_state->fb->pitches[2] != new_plane_state->fb->pitches[1]) {
0469 DRM_ERROR("unsupported UV-plane configuration\n");
0470 return -EINVAL;
0471 }
0472 }
0473
0474
0475
0476 err = tegra_plane_state_add(&tegra->base, new_plane_state);
0477 if (err < 0)
0478 return err;
0479
0480 return 0;
0481 }
0482
0483 static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
0484 struct drm_atomic_state *state)
0485 {
0486 struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state,
0487 plane);
0488 struct tegra_plane *p = to_tegra_plane(plane);
0489 struct tegra_dc *dc;
0490 u32 value;
0491 int err;
0492
0493
0494 if (!old_state || !old_state->crtc)
0495 return;
0496
0497 dc = to_tegra_dc(old_state->crtc);
0498
0499 err = host1x_client_resume(&dc->client);
0500 if (err < 0) {
0501 dev_err(dc->dev, "failed to resume: %d\n", err);
0502 return;
0503 }
0504
0505
0506
0507
0508
0509
0510 if (WARN_ON(p->dc == NULL))
0511 p->dc = dc;
0512
0513 value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
0514 value &= ~WIN_ENABLE;
0515 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
0516
0517 tegra_dc_remove_shared_plane(dc, p);
0518
0519 host1x_client_suspend(&dc->client);
0520 }
0521
0522 static inline u32 compute_phase_incr(fixed20_12 in, unsigned int out)
0523 {
0524 u64 tmp, tmp1, tmp2;
0525
0526 tmp = (u64)dfixed_trunc(in);
0527 tmp2 = (u64)out;
0528 tmp1 = (tmp << NFB) + (tmp2 >> 1);
0529 do_div(tmp1, tmp2);
0530
0531 return lower_32_bits(tmp1);
0532 }
0533
0534 static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
0535 struct drm_atomic_state *state)
0536 {
0537 struct drm_plane_state *new_state = drm_atomic_get_new_plane_state(state,
0538 plane);
0539 struct tegra_plane_state *tegra_plane_state = to_tegra_plane_state(new_state);
0540 struct tegra_dc *dc = to_tegra_dc(new_state->crtc);
0541 unsigned int zpos = new_state->normalized_zpos;
0542 struct drm_framebuffer *fb = new_state->fb;
0543 struct tegra_plane *p = to_tegra_plane(plane);
0544 u32 value, min_width, bypass = 0;
0545 dma_addr_t base, addr_flag = 0;
0546 unsigned int bpc, planes;
0547 bool yuv;
0548 int err;
0549
0550
0551 if (!new_state->crtc || !new_state->fb)
0552 return;
0553
0554 if (!new_state->visible) {
0555 tegra_shared_plane_atomic_disable(plane, state);
0556 return;
0557 }
0558
0559 err = host1x_client_resume(&dc->client);
0560 if (err < 0) {
0561 dev_err(dc->dev, "failed to resume: %d\n", err);
0562 return;
0563 }
0564
0565 yuv = tegra_plane_format_is_yuv(tegra_plane_state->format, &planes, &bpc);
0566
0567 tegra_dc_assign_shared_plane(dc, p);
0568
0569 tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
0570
0571
0572 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
0573 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
0574 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
0575 tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT);
0576
0577 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
0578 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
0579 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
0580 tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT);
0581
0582 value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos);
0583 tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
0584
0585
0586 min_width = min(new_state->src_w >> 16, new_state->crtc_w);
0587
0588 value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPC);
0589
0590 if (min_width < MAX_PIXELS_5TAP444(value)) {
0591 value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
0592 } else {
0593 value = tegra_plane_readl(p, DC_WINC_PRECOMP_WGRP_PIPE_CAPE);
0594
0595 if (min_width < MAX_PIXELS_2TAP444(value))
0596 value = HORIZONTAL_TAPS_2 | VERTICAL_TAPS_2;
0597 else
0598 dev_err(dc->dev, "invalid minimum width: %u\n", min_width);
0599 }
0600
0601 value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
0602 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
0603
0604 if (new_state->src_w != new_state->crtc_w << 16) {
0605 fixed20_12 width = dfixed_init(new_state->src_w >> 16);
0606 u32 incr = compute_phase_incr(width, new_state->crtc_w) & ~0x1;
0607 u32 init = (1 << (NFB - 1)) + (incr >> 1);
0608
0609 tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_HPHASE_INCR);
0610 tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_H_START_PHASE);
0611 } else {
0612 bypass |= INPUT_SCALER_HBYPASS;
0613 }
0614
0615 if (new_state->src_h != new_state->crtc_h << 16) {
0616 fixed20_12 height = dfixed_init(new_state->src_h >> 16);
0617 u32 incr = compute_phase_incr(height, new_state->crtc_h) & ~0x1;
0618 u32 init = (1 << (NFB - 1)) + (incr >> 1);
0619
0620 tegra_plane_writel(p, incr, DC_WIN_SET_INPUT_SCALER_VPHASE_INCR);
0621 tegra_plane_writel(p, init, DC_WIN_SET_INPUT_SCALER_V_START_PHASE);
0622 } else {
0623 bypass |= INPUT_SCALER_VBYPASS;
0624 }
0625
0626 tegra_plane_writel(p, bypass, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
0627
0628
0629 tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
0630
0631 #ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
0632
0633
0634
0635
0636
0637 if (tegra_plane_state->tiling.sector_layout == TEGRA_BO_SECTOR_LAYOUT_GPU)
0638 addr_flag = BIT_ULL(39);
0639 #endif
0640
0641 base = tegra_plane_state->iova[0] + fb->offsets[0];
0642 base |= addr_flag;
0643
0644 tegra_plane_writel(p, tegra_plane_state->format, DC_WIN_COLOR_DEPTH);
0645 tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
0646
0647 value = V_POSITION(new_state->crtc_y) |
0648 H_POSITION(new_state->crtc_x);
0649 tegra_plane_writel(p, value, DC_WIN_POSITION);
0650
0651 value = V_SIZE(new_state->crtc_h) | H_SIZE(new_state->crtc_w);
0652 tegra_plane_writel(p, value, DC_WIN_SIZE);
0653
0654 value = WIN_ENABLE | COLOR_EXPAND;
0655 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
0656
0657 value = V_SIZE(new_state->src_h >> 16) | H_SIZE(new_state->src_w >> 16);
0658 tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
0659
0660 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);
0661 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR);
0662
0663 value = PITCH(fb->pitches[0]);
0664 tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
0665
0666 if (yuv && planes > 1) {
0667 base = tegra_plane_state->iova[1] + fb->offsets[1];
0668 base |= addr_flag;
0669
0670 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI_U);
0671 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR_U);
0672
0673 if (planes > 2) {
0674 base = tegra_plane_state->iova[2] + fb->offsets[2];
0675 base |= addr_flag;
0676
0677 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI_V);
0678 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR_V);
0679 }
0680
0681 value = PITCH_U(fb->pitches[1]);
0682
0683 if (planes > 2)
0684 value |= PITCH_V(fb->pitches[2]);
0685
0686 tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE_UV);
0687 } else {
0688 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_U);
0689 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_HI_U);
0690 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_V);
0691 tegra_plane_writel(p, 0, DC_WINBUF_START_ADDR_HI_V);
0692 tegra_plane_writel(p, 0, DC_WIN_PLANAR_STORAGE_UV);
0693 }
0694
0695 value = CLAMP_BEFORE_BLEND | INPUT_RANGE_FULL;
0696
0697 if (yuv) {
0698 if (bpc < 12)
0699 value |= DEGAMMA_YUV8_10;
0700 else
0701 value |= DEGAMMA_YUV12;
0702
0703
0704 value |= COLOR_SPACE_YUV_2020;
0705 } else {
0706 if (!tegra_plane_format_is_indexed(tegra_plane_state->format))
0707 value |= DEGAMMA_SRGB;
0708 }
0709
0710 tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
0711
0712 value = OFFSET_X(new_state->src_y >> 16) |
0713 OFFSET_Y(new_state->src_x >> 16);
0714 tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT);
0715
0716 if (dc->soc->supports_block_linear) {
0717 unsigned long height = tegra_plane_state->tiling.value;
0718
0719
0720 switch (tegra_plane_state->tiling.mode) {
0721 case TEGRA_BO_TILING_MODE_PITCH:
0722 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) |
0723 DC_WINBUF_SURFACE_KIND_PITCH;
0724 break;
0725
0726
0727 case TEGRA_BO_TILING_MODE_TILED:
0728 value = DC_WINBUF_SURFACE_KIND_TILED;
0729 break;
0730
0731 case TEGRA_BO_TILING_MODE_BLOCK:
0732 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
0733 DC_WINBUF_SURFACE_KIND_BLOCK;
0734 break;
0735 }
0736
0737 tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
0738 }
0739
0740
0741 value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL);
0742 value &= ~CONTROL_CSC_ENABLE;
0743 tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
0744
0745 host1x_client_suspend(&dc->client);
0746 }
0747
0748 static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
0749 .prepare_fb = tegra_plane_prepare_fb,
0750 .cleanup_fb = tegra_plane_cleanup_fb,
0751 .atomic_check = tegra_shared_plane_atomic_check,
0752 .atomic_update = tegra_shared_plane_atomic_update,
0753 .atomic_disable = tegra_shared_plane_atomic_disable,
0754 };
0755
0756 struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
0757 struct tegra_dc *dc,
0758 unsigned int wgrp,
0759 unsigned int index)
0760 {
0761 enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
0762 struct tegra_drm *tegra = drm->dev_private;
0763 struct tegra_display_hub *hub = tegra->hub;
0764 struct tegra_shared_plane *plane;
0765 unsigned int possible_crtcs;
0766 unsigned int num_formats;
0767 const u64 *modifiers;
0768 struct drm_plane *p;
0769 const u32 *formats;
0770 int err;
0771
0772 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
0773 if (!plane)
0774 return ERR_PTR(-ENOMEM);
0775
0776 plane->base.offset = 0x0a00 + 0x0300 * index;
0777 plane->base.index = index;
0778
0779 plane->wgrp = &hub->wgrps[wgrp];
0780 plane->wgrp->parent = &dc->client;
0781
0782 p = &plane->base.base;
0783
0784
0785 possible_crtcs = BIT(tegra->num_crtcs) - 1;
0786
0787 num_formats = ARRAY_SIZE(tegra_shared_plane_formats);
0788 formats = tegra_shared_plane_formats;
0789 modifiers = tegra_shared_plane_modifiers;
0790
0791 err = drm_universal_plane_init(drm, p, possible_crtcs,
0792 &tegra_plane_funcs, formats,
0793 num_formats, modifiers, type, NULL);
0794 if (err < 0) {
0795 kfree(plane);
0796 return ERR_PTR(err);
0797 }
0798
0799 drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
0800 drm_plane_create_zpos_property(p, 0, 0, 255);
0801
0802 return p;
0803 }
0804
0805 static struct drm_private_state *
0806 tegra_display_hub_duplicate_state(struct drm_private_obj *obj)
0807 {
0808 struct tegra_display_hub_state *state;
0809
0810 state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
0811 if (!state)
0812 return NULL;
0813
0814 __drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
0815
0816 return &state->base;
0817 }
0818
0819 static void tegra_display_hub_destroy_state(struct drm_private_obj *obj,
0820 struct drm_private_state *state)
0821 {
0822 struct tegra_display_hub_state *hub_state =
0823 to_tegra_display_hub_state(state);
0824
0825 kfree(hub_state);
0826 }
0827
0828 static const struct drm_private_state_funcs tegra_display_hub_state_funcs = {
0829 .atomic_duplicate_state = tegra_display_hub_duplicate_state,
0830 .atomic_destroy_state = tegra_display_hub_destroy_state,
0831 };
0832
0833 static struct tegra_display_hub_state *
0834 tegra_display_hub_get_state(struct tegra_display_hub *hub,
0835 struct drm_atomic_state *state)
0836 {
0837 struct drm_private_state *priv;
0838
0839 priv = drm_atomic_get_private_obj_state(state, &hub->base);
0840 if (IS_ERR(priv))
0841 return ERR_CAST(priv);
0842
0843 return to_tegra_display_hub_state(priv);
0844 }
0845
0846 int tegra_display_hub_atomic_check(struct drm_device *drm,
0847 struct drm_atomic_state *state)
0848 {
0849 struct tegra_drm *tegra = drm->dev_private;
0850 struct tegra_display_hub_state *hub_state;
0851 struct drm_crtc_state *old, *new;
0852 struct drm_crtc *crtc;
0853 unsigned int i;
0854
0855 if (!tegra->hub)
0856 return 0;
0857
0858 hub_state = tegra_display_hub_get_state(tegra->hub, state);
0859 if (IS_ERR(hub_state))
0860 return PTR_ERR(hub_state);
0861
0862
0863
0864
0865
0866
0867
0868
0869
0870 for_each_oldnew_crtc_in_state(state, crtc, old, new, i) {
0871 struct tegra_dc_state *dc = to_dc_state(new);
0872
0873 if (new->active) {
0874 if (!hub_state->clk || dc->pclk > hub_state->rate) {
0875 hub_state->dc = to_tegra_dc(dc->base.crtc);
0876 hub_state->clk = hub_state->dc->clk;
0877 hub_state->rate = dc->pclk;
0878 }
0879 }
0880 }
0881
0882 return 0;
0883 }
0884
0885 static void tegra_display_hub_update(struct tegra_dc *dc)
0886 {
0887 u32 value;
0888 int err;
0889
0890 err = host1x_client_resume(&dc->client);
0891 if (err < 0) {
0892 dev_err(dc->dev, "failed to resume: %d\n", err);
0893 return;
0894 }
0895
0896 value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
0897 value &= ~LATENCY_EVENT;
0898 tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL);
0899
0900 value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
0901 value = CURS_SLOTS(1) | WGRP_SLOTS(1);
0902 tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
0903
0904 tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL);
0905 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
0906 tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
0907 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
0908
0909 host1x_client_suspend(&dc->client);
0910 }
0911
0912 void tegra_display_hub_atomic_commit(struct drm_device *drm,
0913 struct drm_atomic_state *state)
0914 {
0915 struct tegra_drm *tegra = drm->dev_private;
0916 struct tegra_display_hub *hub = tegra->hub;
0917 struct tegra_display_hub_state *hub_state;
0918 struct device *dev = hub->client.dev;
0919 int err;
0920
0921 hub_state = to_tegra_display_hub_state(hub->base.state);
0922
0923 if (hub_state->clk) {
0924 err = clk_set_rate(hub_state->clk, hub_state->rate);
0925 if (err < 0)
0926 dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
0927 hub_state->clk, hub_state->rate);
0928
0929 err = clk_set_parent(hub->clk_disp, hub_state->clk);
0930 if (err < 0)
0931 dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
0932 hub->clk_disp, hub_state->clk, err);
0933 }
0934
0935 if (hub_state->dc)
0936 tegra_display_hub_update(hub_state->dc);
0937 }
0938
0939 static int tegra_display_hub_init(struct host1x_client *client)
0940 {
0941 struct tegra_display_hub *hub = to_tegra_display_hub(client);
0942 struct drm_device *drm = dev_get_drvdata(client->host);
0943 struct tegra_drm *tegra = drm->dev_private;
0944 struct tegra_display_hub_state *state;
0945
0946 state = kzalloc(sizeof(*state), GFP_KERNEL);
0947 if (!state)
0948 return -ENOMEM;
0949
0950 drm_atomic_private_obj_init(drm, &hub->base, &state->base,
0951 &tegra_display_hub_state_funcs);
0952
0953 tegra->hub = hub;
0954
0955 return 0;
0956 }
0957
0958 static int tegra_display_hub_exit(struct host1x_client *client)
0959 {
0960 struct drm_device *drm = dev_get_drvdata(client->host);
0961 struct tegra_drm *tegra = drm->dev_private;
0962
0963 drm_atomic_private_obj_fini(&tegra->hub->base);
0964 tegra->hub = NULL;
0965
0966 return 0;
0967 }
0968
0969 static int tegra_display_hub_runtime_suspend(struct host1x_client *client)
0970 {
0971 struct tegra_display_hub *hub = to_tegra_display_hub(client);
0972 struct device *dev = client->dev;
0973 unsigned int i = hub->num_heads;
0974 int err;
0975
0976 err = reset_control_assert(hub->rst);
0977 if (err < 0)
0978 return err;
0979
0980 while (i--)
0981 clk_disable_unprepare(hub->clk_heads[i]);
0982
0983 clk_disable_unprepare(hub->clk_hub);
0984 clk_disable_unprepare(hub->clk_dsc);
0985 clk_disable_unprepare(hub->clk_disp);
0986
0987 pm_runtime_put_sync(dev);
0988
0989 return 0;
0990 }
0991
0992 static int tegra_display_hub_runtime_resume(struct host1x_client *client)
0993 {
0994 struct tegra_display_hub *hub = to_tegra_display_hub(client);
0995 struct device *dev = client->dev;
0996 unsigned int i;
0997 int err;
0998
0999 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
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
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 };