blob: e10a47d573138b164acc90c690c86ccddf9d0b08 [file] [log] [blame]
Thierry Redingc4755fb2017-11-13 11:08:13 +01001/*
2 * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 */
8
9#include <linux/clk.h>
10#include <linux/host1x.h>
11#include <linux/module.h>
12#include <linux/of.h>
13#include <linux/of_device.h>
14#include <linux/of_graph.h>
15#include <linux/platform_device.h>
16#include <linux/pm_runtime.h>
17#include <linux/reset.h>
18
19#include <drm/drmP.h>
20#include <drm/drm_atomic.h>
21#include <drm/drm_atomic_helper.h>
22#include <drm/drm_crtc_helper.h>
23
24#include "drm.h"
25#include "dc.h"
26#include "plane.h"
27
28static const u32 tegra_shared_plane_formats[] = {
Thierry Reding511c7022017-11-14 16:07:40 +010029 DRM_FORMAT_ARGB1555,
Thierry Redingc4755fb2017-11-13 11:08:13 +010030 DRM_FORMAT_RGB565,
Thierry Reding511c7022017-11-14 16:07:40 +010031 DRM_FORMAT_RGBA5551,
32 DRM_FORMAT_ARGB8888,
33 DRM_FORMAT_ABGR8888,
34 /* new on Tegra114 */
35 DRM_FORMAT_ABGR4444,
36 DRM_FORMAT_ABGR1555,
37 DRM_FORMAT_BGRA5551,
38 DRM_FORMAT_XRGB1555,
39 DRM_FORMAT_RGBX5551,
40 DRM_FORMAT_XBGR1555,
41 DRM_FORMAT_BGRX5551,
42 DRM_FORMAT_BGR565,
43 DRM_FORMAT_XRGB8888,
44 DRM_FORMAT_XBGR8888,
45 /* planar formats */
46 DRM_FORMAT_UYVY,
47 DRM_FORMAT_YUYV,
48 DRM_FORMAT_YUV420,
49 DRM_FORMAT_YUV422,
Thierry Redingc4755fb2017-11-13 11:08:13 +010050};
51
Thierry Reding1087fac2017-12-14 13:37:53 +010052static inline unsigned int tegra_plane_offset(struct tegra_plane *plane,
Thierry Redingc4755fb2017-11-13 11:08:13 +010053 unsigned int offset)
54{
Thierry Redingc4755fb2017-11-13 11:08:13 +010055 if (offset >= 0x500 && offset <= 0x581) {
56 offset = 0x000 + (offset - 0x500);
Thierry Reding1087fac2017-12-14 13:37:53 +010057 return plane->offset + offset;
Thierry Redingc4755fb2017-11-13 11:08:13 +010058 }
59
60 if (offset >= 0x700 && offset <= 0x73c) {
61 offset = 0x180 + (offset - 0x700);
Thierry Reding1087fac2017-12-14 13:37:53 +010062 return plane->offset + offset;
Thierry Redingc4755fb2017-11-13 11:08:13 +010063 }
64
65 if (offset >= 0x800 && offset <= 0x83e) {
66 offset = 0x1c0 + (offset - 0x800);
Thierry Reding1087fac2017-12-14 13:37:53 +010067 return plane->offset + offset;
Thierry Redingc4755fb2017-11-13 11:08:13 +010068 }
69
70 dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
71
Thierry Reding1087fac2017-12-14 13:37:53 +010072 return plane->offset + offset;
Thierry Redingc4755fb2017-11-13 11:08:13 +010073}
74
Thierry Reding1087fac2017-12-14 13:37:53 +010075static inline u32 tegra_plane_readl(struct tegra_plane *plane,
Thierry Redingc4755fb2017-11-13 11:08:13 +010076 unsigned int offset)
77{
78 return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
79}
80
Thierry Reding1087fac2017-12-14 13:37:53 +010081static inline void tegra_plane_writel(struct tegra_plane *plane, u32 value,
82 unsigned int offset)
Thierry Redingc4755fb2017-11-13 11:08:13 +010083{
84 tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
85}
86
87static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
88{
89 mutex_lock(&wgrp->lock);
90
91 if (wgrp->usecount == 0) {
92 pm_runtime_get_sync(wgrp->parent);
93 reset_control_deassert(wgrp->rst);
94 }
95
96 wgrp->usecount++;
97 mutex_unlock(&wgrp->lock);
98
99 return 0;
100}
101
102static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
103{
104 int err;
105
106 mutex_lock(&wgrp->lock);
107
108 if (wgrp->usecount == 1) {
109 err = reset_control_assert(wgrp->rst);
110 if (err < 0) {
111 pr_err("failed to assert reset for window group %u\n",
112 wgrp->index);
113 }
114
115 pm_runtime_put(wgrp->parent);
116 }
117
118 wgrp->usecount--;
119 mutex_unlock(&wgrp->lock);
120}
121
122int tegra_display_hub_prepare(struct tegra_display_hub *hub)
123{
124 unsigned int i;
125
126 /*
127 * XXX Enabling/disabling windowgroups needs to happen when the owner
128 * display controller is disabled. There's currently no good point at
129 * which this could be executed, so unconditionally enable all window
130 * groups for now.
131 */
132 for (i = 0; i < hub->soc->num_wgrps; i++) {
133 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
134
135 tegra_windowgroup_enable(wgrp);
136 }
137
138 return 0;
139}
140
141void tegra_display_hub_cleanup(struct tegra_display_hub *hub)
142{
143 unsigned int i;
144
145 /*
146 * XXX Remove this once window groups can be more fine-grainedly
147 * enabled and disabled.
148 */
149 for (i = 0; i < hub->soc->num_wgrps; i++) {
150 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
151
152 tegra_windowgroup_disable(wgrp);
153 }
154}
155
Thierry Reding1087fac2017-12-14 13:37:53 +0100156static void tegra_shared_plane_update(struct tegra_plane *plane)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100157{
158 struct tegra_dc *dc = plane->dc;
159 unsigned long timeout;
160 u32 mask, value;
161
162 mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index;
163 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
164
165 timeout = jiffies + msecs_to_jiffies(1000);
166
167 while (time_before(jiffies, timeout)) {
168 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
169 if ((value & mask) == 0)
170 break;
171
172 usleep_range(100, 400);
173 }
174}
175
Thierry Reding1087fac2017-12-14 13:37:53 +0100176static void tegra_shared_plane_activate(struct tegra_plane *plane)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100177{
178 struct tegra_dc *dc = plane->dc;
179 unsigned long timeout;
180 u32 mask, value;
181
182 mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index;
183 tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
184
185 timeout = jiffies + msecs_to_jiffies(1000);
186
187 while (time_before(jiffies, timeout)) {
188 value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
189 if ((value & mask) == 0)
190 break;
191
192 usleep_range(100, 400);
193 }
194}
195
196static unsigned int
Thierry Reding1087fac2017-12-14 13:37:53 +0100197tegra_shared_plane_get_owner(struct tegra_plane *plane, struct tegra_dc *dc)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100198{
199 unsigned int offset =
200 tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
201
202 return tegra_dc_readl(dc, offset) & OWNER_MASK;
203}
204
205static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc,
Thierry Reding1087fac2017-12-14 13:37:53 +0100206 struct tegra_plane *plane)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100207{
208 struct device *dev = dc->dev;
209
210 if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) {
211 if (plane->dc == dc)
212 return true;
213
214 dev_WARN(dev, "head %u owns window %u but is not attached\n",
Thierry Reding1087fac2017-12-14 13:37:53 +0100215 dc->pipe, plane->index);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100216 }
217
218 return false;
219}
220
Thierry Reding1087fac2017-12-14 13:37:53 +0100221static int tegra_shared_plane_set_owner(struct tegra_plane *plane,
Thierry Redingc4755fb2017-11-13 11:08:13 +0100222 struct tegra_dc *new)
223{
224 unsigned int offset =
225 tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
226 struct tegra_dc *old = plane->dc, *dc = new ? new : old;
227 struct device *dev = new ? new->dev : old->dev;
Thierry Reding1087fac2017-12-14 13:37:53 +0100228 unsigned int owner, index = plane->index;
Thierry Redingc4755fb2017-11-13 11:08:13 +0100229 u32 value;
230
231 value = tegra_dc_readl(dc, offset);
232 owner = value & OWNER_MASK;
233
234 if (new && (owner != OWNER_MASK && owner != new->pipe)) {
235 dev_WARN(dev, "window %u owned by head %u\n", index, owner);
236 return -EBUSY;
237 }
238
239 /*
240 * This seems to happen whenever the head has been disabled with one
241 * or more windows being active. This is harmless because we'll just
242 * reassign the window to the new head anyway.
243 */
244 if (old && owner == OWNER_MASK)
245 dev_dbg(dev, "window %u not owned by head %u but %u\n", index,
Thierry Reding1087fac2017-12-14 13:37:53 +0100246 old->pipe, owner);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100247
248 value &= ~OWNER_MASK;
249
250 if (new)
251 value |= OWNER(new->pipe);
252 else
253 value |= OWNER_MASK;
254
255 tegra_dc_writel(dc, value, offset);
256
257 plane->dc = new;
258
259 return 0;
260}
261
262static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
Thierry Reding1087fac2017-12-14 13:37:53 +0100263 struct tegra_plane *plane)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100264{
265 u32 value;
266 int err;
267
268 if (!tegra_dc_owns_shared_plane(dc, plane)) {
269 err = tegra_shared_plane_set_owner(plane, dc);
270 if (err < 0)
271 return;
272 }
273
274 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
275 value |= MODE_FOUR_LINES;
276 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
277
278 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
279 value = SLOTS(1);
280 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
281
282 /* disable watermark */
283 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
284 value &= ~LATENCY_CTL_MODE_ENABLE;
285 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
286
287 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
288 value |= WATERMARK_MASK;
289 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
290
291 /* pipe meter */
292 value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
293 value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0);
294 tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
295
296 /* mempool entries */
297 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
298 value = MEMPOOL_ENTRIES(0x331);
299 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
300
301 value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP);
302 value &= ~THREAD_NUM_MASK;
303 value |= THREAD_NUM(plane->base.index);
304 value |= THREAD_GROUP_ENABLE;
305 tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP);
306
307 tegra_shared_plane_update(plane);
308 tegra_shared_plane_activate(plane);
309}
310
311static void tegra_dc_remove_shared_plane(struct tegra_dc *dc,
Thierry Reding1087fac2017-12-14 13:37:53 +0100312 struct tegra_plane *plane)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100313{
314 tegra_shared_plane_set_owner(plane, NULL);
315}
316
317static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
318 struct drm_plane_state *state)
319{
320 struct tegra_plane_state *plane_state = to_tegra_plane_state(state);
321 struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane);
322 struct tegra_bo_tiling *tiling = &plane_state->tiling;
323 struct tegra_dc *dc = to_tegra_dc(state->crtc);
324 int err;
325
326 /* no need for further checks if the plane is being disabled */
327 if (!state->crtc || !state->fb)
328 return 0;
329
330 err = tegra_plane_format(state->fb->format->format,
331 &plane_state->format,
332 &plane_state->swap);
333 if (err < 0)
334 return err;
335
336 err = tegra_fb_get_tiling(state->fb, tiling);
337 if (err < 0)
338 return err;
339
340 if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
341 !dc->soc->supports_block_linear) {
342 DRM_ERROR("hardware doesn't support block linear mode\n");
343 return -EINVAL;
344 }
345
346 /*
347 * Tegra doesn't support different strides for U and V planes so we
348 * error out if the user tries to display a framebuffer with such a
349 * configuration.
350 */
351 if (state->fb->format->num_planes > 2) {
352 if (state->fb->pitches[2] != state->fb->pitches[1]) {
353 DRM_ERROR("unsupported UV-plane configuration\n");
354 return -EINVAL;
355 }
356 }
357
358 /* XXX scaling is not yet supported, add a check here */
359
360 err = tegra_plane_state_add(&tegra->base, state);
361 if (err < 0)
362 return err;
363
364 return 0;
365}
366
367static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
368 struct drm_plane_state *old_state)
369{
Thierry Redingc4755fb2017-11-13 11:08:13 +0100370 struct tegra_dc *dc = to_tegra_dc(old_state->crtc);
Thierry Reding1087fac2017-12-14 13:37:53 +0100371 struct tegra_plane *p = to_tegra_plane(plane);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100372 u32 value;
373
374 /* rien ne va plus */
375 if (!old_state || !old_state->crtc)
376 return;
377
378 /*
379 * XXX Legacy helpers seem to sometimes call ->atomic_disable() even
380 * on planes that are already disabled. Make sure we fallback to the
381 * head for this particular state instead of crashing.
382 */
383 if (WARN_ON(p->dc == NULL))
384 p->dc = dc;
385
386 pm_runtime_get_sync(dc->dev);
387
388 value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
389 value &= ~WIN_ENABLE;
390 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
391
392 tegra_dc_remove_shared_plane(dc, p);
393
394 pm_runtime_put(dc->dev);
395}
396
397static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
398 struct drm_plane_state *old_state)
399{
400 struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100401 struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
Thierry Redingab7d3f52017-12-14 13:46:20 +0100402 unsigned int zpos = plane->state->normalized_zpos;
Thierry Redingc4755fb2017-11-13 11:08:13 +0100403 struct drm_framebuffer *fb = plane->state->fb;
Thierry Reding1087fac2017-12-14 13:37:53 +0100404 struct tegra_plane *p = to_tegra_plane(plane);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100405 struct tegra_bo *bo;
406 dma_addr_t base;
407 u32 value;
408
409 /* rien ne va plus */
410 if (!plane->state->crtc || !plane->state->fb)
411 return;
412
413 if (!plane->state->visible) {
414 tegra_shared_plane_atomic_disable(plane, old_state);
415 return;
416 }
417
418 pm_runtime_get_sync(dc->dev);
419
420 tegra_dc_assign_shared_plane(dc, p);
421
422 tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
423
424 /* blending */
425 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
426 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
427 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
428 tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT);
429
430 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
431 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
432 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
433 tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT);
434
Thierry Redingab7d3f52017-12-14 13:46:20 +0100435 value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(255 - zpos);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100436 tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
437
438 /* bypass scaling */
439 value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
440 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
441
442 value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS;
443 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
444
445 /* disable compression */
446 tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
447
448 bo = tegra_fb_get_plane(fb, 0);
449 base = bo->paddr;
450
451 tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH);
452 tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
453
454 value = V_POSITION(plane->state->crtc_y) |
455 H_POSITION(plane->state->crtc_x);
456 tegra_plane_writel(p, value, DC_WIN_POSITION);
457
458 value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
459 tegra_plane_writel(p, value, DC_WIN_SIZE);
460
461 value = WIN_ENABLE | COLOR_EXPAND;
462 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
463
464 value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
465 tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
466
467 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);
468 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR);
469
470 value = PITCH(fb->pitches[0]);
471 tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
472
473 value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL;
474 tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
475
476 value = OFFSET_X(plane->state->src_y >> 16) |
477 OFFSET_Y(plane->state->src_x >> 16);
478 tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT);
479
480 if (dc->soc->supports_block_linear) {
481 unsigned long height = state->tiling.value;
482
483 /* XXX */
484 switch (state->tiling.mode) {
485 case TEGRA_BO_TILING_MODE_PITCH:
486 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) |
487 DC_WINBUF_SURFACE_KIND_PITCH;
488 break;
489
490 /* XXX not supported on Tegra186 and later */
491 case TEGRA_BO_TILING_MODE_TILED:
492 value = DC_WINBUF_SURFACE_KIND_TILED;
493 break;
494
495 case TEGRA_BO_TILING_MODE_BLOCK:
496 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
497 DC_WINBUF_SURFACE_KIND_BLOCK;
498 break;
499 }
500
501 tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
502 }
503
504 /* disable gamut CSC */
505 value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL);
506 value &= ~CONTROL_CSC_ENABLE;
507 tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
508
509 pm_runtime_put(dc->dev);
510}
511
512static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
513 .atomic_check = tegra_shared_plane_atomic_check,
514 .atomic_update = tegra_shared_plane_atomic_update,
515 .atomic_disable = tegra_shared_plane_atomic_disable,
516};
517
518struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
519 struct tegra_dc *dc,
520 unsigned int wgrp,
521 unsigned int index)
522{
523 enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
524 struct tegra_drm *tegra = drm->dev_private;
525 struct tegra_display_hub *hub = tegra->hub;
526 /* planes can be assigned to arbitrary CRTCs */
527 unsigned int possible_crtcs = 0x7;
528 struct tegra_shared_plane *plane;
529 unsigned int num_formats;
530 struct drm_plane *p;
531 const u32 *formats;
532 int err;
533
534 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
535 if (!plane)
536 return ERR_PTR(-ENOMEM);
537
538 plane->base.offset = 0x0a00 + 0x0300 * index;
539 plane->base.index = index;
Thierry Redingc4755fb2017-11-13 11:08:13 +0100540
541 plane->wgrp = &hub->wgrps[wgrp];
542 plane->wgrp->parent = dc->dev;
543
544 p = &plane->base.base;
545
546 num_formats = ARRAY_SIZE(tegra_shared_plane_formats);
547 formats = tegra_shared_plane_formats;
548
549 err = drm_universal_plane_init(drm, p, possible_crtcs,
550 &tegra_plane_funcs, formats,
551 num_formats, NULL, type, NULL);
552 if (err < 0) {
553 kfree(plane);
554 return ERR_PTR(err);
555 }
556
557 drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
Thierry Redingab7d3f52017-12-14 13:46:20 +0100558 drm_plane_create_zpos_property(p, 0, 0, 255);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100559
560 return p;
561}
562
563static void tegra_display_hub_update(struct tegra_dc *dc)
564{
565 u32 value;
566
567 pm_runtime_get_sync(dc->dev);
568
569 value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
570 value &= ~LATENCY_EVENT;
571 tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL);
572
573 value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
574 value = CURS_SLOTS(1) | WGRP_SLOTS(1);
575 tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
576
577 tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL);
578 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
579 tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
580 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
581
582 pm_runtime_put(dc->dev);
583}
584
585void tegra_display_hub_atomic_commit(struct drm_device *drm,
586 struct drm_atomic_state *state)
587{
588 struct tegra_atomic_state *s = to_tegra_atomic_state(state);
589 struct tegra_drm *tegra = drm->dev_private;
590 struct tegra_display_hub *hub = tegra->hub;
591 struct device *dev = hub->client.dev;
592 int err;
593
594 if (s->clk_disp) {
595 err = clk_set_rate(s->clk_disp, s->rate);
596 if (err < 0)
597 dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
598 s->clk_disp, s->rate);
599
600 err = clk_set_parent(hub->clk_disp, s->clk_disp);
601 if (err < 0)
602 dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
603 hub->clk_disp, s->clk_disp, err);
604 }
605
606 if (s->dc)
607 tegra_display_hub_update(s->dc);
608}
609
610static int tegra_display_hub_init(struct host1x_client *client)
611{
612 struct tegra_display_hub *hub = to_tegra_display_hub(client);
613 struct drm_device *drm = dev_get_drvdata(client->parent);
614 struct tegra_drm *tegra = drm->dev_private;
615
616 tegra->hub = hub;
617
618 return 0;
619}
620
621static int tegra_display_hub_exit(struct host1x_client *client)
622{
623 struct drm_device *drm = dev_get_drvdata(client->parent);
624 struct tegra_drm *tegra = drm->dev_private;
625
626 tegra->hub = NULL;
627
628 return 0;
629}
630
631static const struct host1x_client_ops tegra_display_hub_ops = {
632 .init = tegra_display_hub_init,
633 .exit = tegra_display_hub_exit,
634};
635
636static int tegra_display_hub_probe(struct platform_device *pdev)
637{
638 struct tegra_display_hub *hub;
639 unsigned int i;
640 int err;
641
642 hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
643 if (!hub)
644 return -ENOMEM;
645
646 hub->soc = of_device_get_match_data(&pdev->dev);
647
648 hub->clk_disp = devm_clk_get(&pdev->dev, "disp");
649 if (IS_ERR(hub->clk_disp)) {
650 err = PTR_ERR(hub->clk_disp);
651 return err;
652 }
653
654 hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc");
655 if (IS_ERR(hub->clk_dsc)) {
656 err = PTR_ERR(hub->clk_dsc);
657 return err;
658 }
659
660 hub->clk_hub = devm_clk_get(&pdev->dev, "hub");
661 if (IS_ERR(hub->clk_hub)) {
662 err = PTR_ERR(hub->clk_hub);
663 return err;
664 }
665
666 hub->rst = devm_reset_control_get(&pdev->dev, "misc");
667 if (IS_ERR(hub->rst)) {
668 err = PTR_ERR(hub->rst);
669 return err;
670 }
671
672 hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps,
673 sizeof(*hub->wgrps), GFP_KERNEL);
674 if (!hub->wgrps)
675 return -ENOMEM;
676
677 for (i = 0; i < hub->soc->num_wgrps; i++) {
678 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
679 char id[8];
680
681 snprintf(id, sizeof(id), "wgrp%u", i);
682 mutex_init(&wgrp->lock);
683 wgrp->usecount = 0;
684 wgrp->index = i;
685
686 wgrp->rst = devm_reset_control_get(&pdev->dev, id);
687 if (IS_ERR(wgrp->rst))
688 return PTR_ERR(wgrp->rst);
689
690 err = reset_control_assert(wgrp->rst);
691 if (err < 0)
692 return err;
693 }
694
695 /* XXX: enable clock across reset? */
696 err = reset_control_assert(hub->rst);
697 if (err < 0)
698 return err;
699
700 platform_set_drvdata(pdev, hub);
701 pm_runtime_enable(&pdev->dev);
702
703 INIT_LIST_HEAD(&hub->client.list);
704 hub->client.ops = &tegra_display_hub_ops;
705 hub->client.dev = &pdev->dev;
706
707 err = host1x_client_register(&hub->client);
708 if (err < 0)
709 dev_err(&pdev->dev, "failed to register host1x client: %d\n",
710 err);
711
712 return err;
713}
714
715static int tegra_display_hub_remove(struct platform_device *pdev)
716{
717 struct tegra_display_hub *hub = platform_get_drvdata(pdev);
718 int err;
719
720 err = host1x_client_unregister(&hub->client);
721 if (err < 0) {
722 dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
723 err);
724 }
725
726 pm_runtime_disable(&pdev->dev);
727
728 return err;
729}
730
Arnd Bergmann013e2b722017-12-15 13:51:52 +0100731static int __maybe_unused tegra_display_hub_suspend(struct device *dev)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100732{
733 struct tegra_display_hub *hub = dev_get_drvdata(dev);
734 int err;
735
736 err = reset_control_assert(hub->rst);
737 if (err < 0)
738 return err;
739
740 clk_disable_unprepare(hub->clk_hub);
741 clk_disable_unprepare(hub->clk_dsc);
742 clk_disable_unprepare(hub->clk_disp);
743
744 return 0;
745}
746
Arnd Bergmann013e2b722017-12-15 13:51:52 +0100747static int __maybe_unused tegra_display_hub_resume(struct device *dev)
Thierry Redingc4755fb2017-11-13 11:08:13 +0100748{
749 struct tegra_display_hub *hub = dev_get_drvdata(dev);
750 int err;
751
752 err = clk_prepare_enable(hub->clk_disp);
753 if (err < 0)
754 return err;
755
756 err = clk_prepare_enable(hub->clk_dsc);
757 if (err < 0)
758 goto disable_disp;
759
760 err = clk_prepare_enable(hub->clk_hub);
761 if (err < 0)
762 goto disable_dsc;
763
764 err = reset_control_deassert(hub->rst);
765 if (err < 0)
766 goto disable_hub;
767
768 return 0;
769
770disable_hub:
771 clk_disable_unprepare(hub->clk_hub);
772disable_dsc:
773 clk_disable_unprepare(hub->clk_dsc);
774disable_disp:
775 clk_disable_unprepare(hub->clk_disp);
776 return err;
777}
778
779static const struct dev_pm_ops tegra_display_hub_pm_ops = {
780 SET_RUNTIME_PM_OPS(tegra_display_hub_suspend,
781 tegra_display_hub_resume, NULL)
782};
783
784static const struct tegra_display_hub_soc tegra186_display_hub = {
785 .num_wgrps = 6,
786};
787
788static const struct of_device_id tegra_display_hub_of_match[] = {
789 {
790 .compatible = "nvidia,tegra186-display",
791 .data = &tegra186_display_hub
792 }, {
793 /* sentinel */
794 }
795};
796MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match);
797
798struct platform_driver tegra_display_hub_driver = {
799 .driver = {
800 .name = "tegra-display-hub",
801 .of_match_table = tegra_display_hub_of_match,
802 .pm = &tegra_display_hub_pm_ops,
803 },
804 .probe = tegra_display_hub_probe,
805 .remove = tegra_display_hub_remove,
806};