blob: 63541dbd864d1e52d4f5fdf2d715a0983dfe6c40 [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);
402 struct drm_framebuffer *fb = plane->state->fb;
Thierry Reding1087fac2017-12-14 13:37:53 +0100403 struct tegra_plane *p = to_tegra_plane(plane);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100404 struct tegra_bo *bo;
405 dma_addr_t base;
406 u32 value;
407
408 /* rien ne va plus */
409 if (!plane->state->crtc || !plane->state->fb)
410 return;
411
412 if (!plane->state->visible) {
413 tegra_shared_plane_atomic_disable(plane, old_state);
414 return;
415 }
416
417 pm_runtime_get_sync(dc->dev);
418
419 tegra_dc_assign_shared_plane(dc, p);
420
421 tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
422
423 /* blending */
424 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
425 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
426 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
427 tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT);
428
429 value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
430 BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
431 BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
432 tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT);
433
Thierry Reding1087fac2017-12-14 13:37:53 +0100434 value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->depth);
Thierry Redingc4755fb2017-11-13 11:08:13 +0100435 tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
436
437 /* bypass scaling */
438 value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
439 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
440
441 value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS;
442 tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
443
444 /* disable compression */
445 tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
446
447 bo = tegra_fb_get_plane(fb, 0);
448 base = bo->paddr;
449
450 tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH);
451 tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
452
453 value = V_POSITION(plane->state->crtc_y) |
454 H_POSITION(plane->state->crtc_x);
455 tegra_plane_writel(p, value, DC_WIN_POSITION);
456
457 value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
458 tegra_plane_writel(p, value, DC_WIN_SIZE);
459
460 value = WIN_ENABLE | COLOR_EXPAND;
461 tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
462
463 value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
464 tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
465
466 tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);
467 tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR);
468
469 value = PITCH(fb->pitches[0]);
470 tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
471
472 value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL;
473 tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
474
475 value = OFFSET_X(plane->state->src_y >> 16) |
476 OFFSET_Y(plane->state->src_x >> 16);
477 tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT);
478
479 if (dc->soc->supports_block_linear) {
480 unsigned long height = state->tiling.value;
481
482 /* XXX */
483 switch (state->tiling.mode) {
484 case TEGRA_BO_TILING_MODE_PITCH:
485 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) |
486 DC_WINBUF_SURFACE_KIND_PITCH;
487 break;
488
489 /* XXX not supported on Tegra186 and later */
490 case TEGRA_BO_TILING_MODE_TILED:
491 value = DC_WINBUF_SURFACE_KIND_TILED;
492 break;
493
494 case TEGRA_BO_TILING_MODE_BLOCK:
495 value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
496 DC_WINBUF_SURFACE_KIND_BLOCK;
497 break;
498 }
499
500 tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
501 }
502
503 /* disable gamut CSC */
504 value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL);
505 value &= ~CONTROL_CSC_ENABLE;
506 tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
507
508 pm_runtime_put(dc->dev);
509}
510
511static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
512 .atomic_check = tegra_shared_plane_atomic_check,
513 .atomic_update = tegra_shared_plane_atomic_update,
514 .atomic_disable = tegra_shared_plane_atomic_disable,
515};
516
517struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
518 struct tegra_dc *dc,
519 unsigned int wgrp,
520 unsigned int index)
521{
522 enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
523 struct tegra_drm *tegra = drm->dev_private;
524 struct tegra_display_hub *hub = tegra->hub;
525 /* planes can be assigned to arbitrary CRTCs */
526 unsigned int possible_crtcs = 0x7;
527 struct tegra_shared_plane *plane;
528 unsigned int num_formats;
529 struct drm_plane *p;
530 const u32 *formats;
531 int err;
532
533 plane = kzalloc(sizeof(*plane), GFP_KERNEL);
534 if (!plane)
535 return ERR_PTR(-ENOMEM);
536
537 plane->base.offset = 0x0a00 + 0x0300 * index;
538 plane->base.index = index;
539 plane->base.depth = 0;
540
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);
558
559 return p;
560}
561
562static void tegra_display_hub_update(struct tegra_dc *dc)
563{
564 u32 value;
565
566 pm_runtime_get_sync(dc->dev);
567
568 value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
569 value &= ~LATENCY_EVENT;
570 tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL);
571
572 value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
573 value = CURS_SLOTS(1) | WGRP_SLOTS(1);
574 tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
575
576 tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL);
577 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
578 tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
579 tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
580
581 pm_runtime_put(dc->dev);
582}
583
584void tegra_display_hub_atomic_commit(struct drm_device *drm,
585 struct drm_atomic_state *state)
586{
587 struct tegra_atomic_state *s = to_tegra_atomic_state(state);
588 struct tegra_drm *tegra = drm->dev_private;
589 struct tegra_display_hub *hub = tegra->hub;
590 struct device *dev = hub->client.dev;
591 int err;
592
593 if (s->clk_disp) {
594 err = clk_set_rate(s->clk_disp, s->rate);
595 if (err < 0)
596 dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
597 s->clk_disp, s->rate);
598
599 err = clk_set_parent(hub->clk_disp, s->clk_disp);
600 if (err < 0)
601 dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
602 hub->clk_disp, s->clk_disp, err);
603 }
604
605 if (s->dc)
606 tegra_display_hub_update(s->dc);
607}
608
609static int tegra_display_hub_init(struct host1x_client *client)
610{
611 struct tegra_display_hub *hub = to_tegra_display_hub(client);
612 struct drm_device *drm = dev_get_drvdata(client->parent);
613 struct tegra_drm *tegra = drm->dev_private;
614
615 tegra->hub = hub;
616
617 return 0;
618}
619
620static int tegra_display_hub_exit(struct host1x_client *client)
621{
622 struct drm_device *drm = dev_get_drvdata(client->parent);
623 struct tegra_drm *tegra = drm->dev_private;
624
625 tegra->hub = NULL;
626
627 return 0;
628}
629
630static const struct host1x_client_ops tegra_display_hub_ops = {
631 .init = tegra_display_hub_init,
632 .exit = tegra_display_hub_exit,
633};
634
635static int tegra_display_hub_probe(struct platform_device *pdev)
636{
637 struct tegra_display_hub *hub;
638 unsigned int i;
639 int err;
640
641 hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
642 if (!hub)
643 return -ENOMEM;
644
645 hub->soc = of_device_get_match_data(&pdev->dev);
646
647 hub->clk_disp = devm_clk_get(&pdev->dev, "disp");
648 if (IS_ERR(hub->clk_disp)) {
649 err = PTR_ERR(hub->clk_disp);
650 return err;
651 }
652
653 hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc");
654 if (IS_ERR(hub->clk_dsc)) {
655 err = PTR_ERR(hub->clk_dsc);
656 return err;
657 }
658
659 hub->clk_hub = devm_clk_get(&pdev->dev, "hub");
660 if (IS_ERR(hub->clk_hub)) {
661 err = PTR_ERR(hub->clk_hub);
662 return err;
663 }
664
665 hub->rst = devm_reset_control_get(&pdev->dev, "misc");
666 if (IS_ERR(hub->rst)) {
667 err = PTR_ERR(hub->rst);
668 return err;
669 }
670
671 hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps,
672 sizeof(*hub->wgrps), GFP_KERNEL);
673 if (!hub->wgrps)
674 return -ENOMEM;
675
676 for (i = 0; i < hub->soc->num_wgrps; i++) {
677 struct tegra_windowgroup *wgrp = &hub->wgrps[i];
678 char id[8];
679
680 snprintf(id, sizeof(id), "wgrp%u", i);
681 mutex_init(&wgrp->lock);
682 wgrp->usecount = 0;
683 wgrp->index = i;
684
685 wgrp->rst = devm_reset_control_get(&pdev->dev, id);
686 if (IS_ERR(wgrp->rst))
687 return PTR_ERR(wgrp->rst);
688
689 err = reset_control_assert(wgrp->rst);
690 if (err < 0)
691 return err;
692 }
693
694 /* XXX: enable clock across reset? */
695 err = reset_control_assert(hub->rst);
696 if (err < 0)
697 return err;
698
699 platform_set_drvdata(pdev, hub);
700 pm_runtime_enable(&pdev->dev);
701
702 INIT_LIST_HEAD(&hub->client.list);
703 hub->client.ops = &tegra_display_hub_ops;
704 hub->client.dev = &pdev->dev;
705
706 err = host1x_client_register(&hub->client);
707 if (err < 0)
708 dev_err(&pdev->dev, "failed to register host1x client: %d\n",
709 err);
710
711 return err;
712}
713
714static int tegra_display_hub_remove(struct platform_device *pdev)
715{
716 struct tegra_display_hub *hub = platform_get_drvdata(pdev);
717 int err;
718
719 err = host1x_client_unregister(&hub->client);
720 if (err < 0) {
721 dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
722 err);
723 }
724
725 pm_runtime_disable(&pdev->dev);
726
727 return err;
728}
729
730static int tegra_display_hub_suspend(struct device *dev)
731{
732 struct tegra_display_hub *hub = dev_get_drvdata(dev);
733 int err;
734
735 err = reset_control_assert(hub->rst);
736 if (err < 0)
737 return err;
738
739 clk_disable_unprepare(hub->clk_hub);
740 clk_disable_unprepare(hub->clk_dsc);
741 clk_disable_unprepare(hub->clk_disp);
742
743 return 0;
744}
745
746static int tegra_display_hub_resume(struct device *dev)
747{
748 struct tegra_display_hub *hub = dev_get_drvdata(dev);
749 int err;
750
751 err = clk_prepare_enable(hub->clk_disp);
752 if (err < 0)
753 return err;
754
755 err = clk_prepare_enable(hub->clk_dsc);
756 if (err < 0)
757 goto disable_disp;
758
759 err = clk_prepare_enable(hub->clk_hub);
760 if (err < 0)
761 goto disable_dsc;
762
763 err = reset_control_deassert(hub->rst);
764 if (err < 0)
765 goto disable_hub;
766
767 return 0;
768
769disable_hub:
770 clk_disable_unprepare(hub->clk_hub);
771disable_dsc:
772 clk_disable_unprepare(hub->clk_dsc);
773disable_disp:
774 clk_disable_unprepare(hub->clk_disp);
775 return err;
776}
777
778static const struct dev_pm_ops tegra_display_hub_pm_ops = {
779 SET_RUNTIME_PM_OPS(tegra_display_hub_suspend,
780 tegra_display_hub_resume, NULL)
781};
782
783static const struct tegra_display_hub_soc tegra186_display_hub = {
784 .num_wgrps = 6,
785};
786
787static const struct of_device_id tegra_display_hub_of_match[] = {
788 {
789 .compatible = "nvidia,tegra186-display",
790 .data = &tegra186_display_hub
791 }, {
792 /* sentinel */
793 }
794};
795MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match);
796
797struct platform_driver tegra_display_hub_driver = {
798 .driver = {
799 .name = "tegra-display-hub",
800 .of_match_table = tegra_display_hub_of_match,
801 .pm = &tegra_display_hub_pm_ops,
802 },
803 .probe = tegra_display_hub_probe,
804 .remove = tegra_display_hub_remove,
805};