Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/tegra: Add Tegra186 display hub support

The display architecture has changed in several significant ways with
the new Tegra186 SoC. Shared between all display controllers is a set
of common resources referred to as the display hub. The hub generates
accesses to memory and feeds them into various composition pipelines,
each of which being a window that can be assigned to arbitrary heads.

Atomic state is subclassed in order to track the global bandwidth
requirements and select and adjust the hub clocks appropriately. The
plane code is shared to a large degree with earlier SoC generations,
except where the programming differs.

Signed-off-by: Thierry Reding <treding@nvidia.com>

+1103 -3
+1
drivers/gpu/drm/tegra/Makefile
··· 5 5 drm.o \ 6 6 gem.o \ 7 7 fb.o \ 8 + hub.o \ 8 9 plane.o \ 9 10 dc.o \ 10 11 output.o \
+31
drivers/gpu/drm/tegra/dc.c
··· 447 447 num_formats = ARRAY_SIZE(tegra_primary_plane_formats); 448 448 formats = tegra_primary_plane_formats; 449 449 450 + /* 451 + * XXX compute offset so that we can directly access windows. 452 + * 453 + * Always use window A as primary window. 454 + */ 455 + plane->offset = 0; 456 + plane->index = 0; 457 + plane->depth = 255; 458 + 450 459 err = drm_universal_plane_init(drm, &plane->base, possible_crtcs, 451 460 &tegra_plane_funcs, formats, 452 461 num_formats, NULL, ··· 650 641 if (!plane) 651 642 return ERR_PTR(-ENOMEM); 652 643 644 + /* XXX compute offset so that we can directly access windows */ 645 + plane->offset = 0; 653 646 plane->index = index; 647 + plane->depth = 0; 654 648 655 649 num_formats = ARRAY_SIZE(tegra_overlay_plane_formats); 656 650 formats = tegra_overlay_plane_formats; ··· 1394 1382 static int tegra_crtc_atomic_check(struct drm_crtc *crtc, 1395 1383 struct drm_crtc_state *state) 1396 1384 { 1385 + struct tegra_atomic_state *s = to_tegra_atomic_state(state->state); 1386 + struct tegra_dc_state *tegra = to_dc_state(state); 1387 + 1388 + /* 1389 + * The display hub display clock needs to be fed by the display clock 1390 + * with the highest frequency to ensure proper functioning of all the 1391 + * displays. 1392 + * 1393 + * Note that this isn't used before Tegra186, but it doesn't hurt and 1394 + * conditionalizing it would make the code less clean. 1395 + */ 1396 + if (state->active) { 1397 + if (!s->clk_disp || tegra->pclk > s->rate) { 1398 + s->dc = to_tegra_dc(crtc); 1399 + s->clk_disp = s->dc->clk; 1400 + s->rate = tegra->pclk; 1401 + } 1402 + } 1403 + 1397 1404 return 0; 1398 1405 } 1399 1406
+114
drivers/gpu/drm/tegra/dc.h
··· 209 209 #define WIN_B_UPDATE (1 << 10) 210 210 #define WIN_C_UPDATE (1 << 11) 211 211 #define CURSOR_UPDATE (1 << 15) 212 + #define COMMON_ACTREQ (1 << 16) 213 + #define COMMON_UPDATE (1 << 17) 212 214 #define NC_HOST_TRIG (1 << 24) 213 215 214 216 #define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 ··· 488 486 #define CURSOR_SRC_BLEND_MASK (3 << 8) 489 487 #define CURSOR_ALPHA 0xff 490 488 489 + #define DC_WIN_CORE_ACT_CONTROL 0x50e 490 + #define VCOUNTER (0 << 0) 491 + #define HCOUNTER (1 << 0) 492 + 493 + #define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA 0x543 494 + #define LATENCY_CTL_MODE_ENABLE (1 << 2) 495 + 496 + #define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB 0x544 497 + #define WATERMARK_MASK 0x1fffffff 498 + 499 + #define DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER 0x560 500 + #define PIPE_METER_INT(x) (((x) & 0xff) << 8) 501 + #define PIPE_METER_FRAC(x) (((x) & 0xff) << 0) 502 + 503 + #define DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG 0x561 504 + #define MEMPOOL_ENTRIES(x) (((x) & 0xffff) << 0) 505 + 506 + #define DC_WIN_CORE_IHUB_WGRP_FETCH_METER 0x562 507 + #define SLOTS(x) (((x) & 0xff) << 0) 508 + 509 + #define DC_WIN_CORE_IHUB_LINEBUF_CONFIG 0x563 510 + #define MODE_TWO_LINES (0 << 14) 511 + #define MODE_FOUR_LINES (1 << 14) 512 + 513 + #define DC_WIN_CORE_IHUB_THREAD_GROUP 0x568 514 + #define THREAD_NUM_MASK (0x1f << 1) 515 + #define THREAD_NUM(x) (((x) & 0x1f) << 1) 516 + #define THREAD_GROUP_ENABLE (1 << 0) 517 + 491 518 #define DC_WIN_CSC_YOF 0x611 492 519 #define DC_WIN_CSC_KYRGB 0x612 493 520 #define DC_WIN_CSC_KUR 0x613 ··· 627 596 628 597 #define DC_WINBUF_START_ADDR_HI 0x80d 629 598 599 + #define DC_WINBUF_CDE_CONTROL 0x82f 600 + #define ENABLE_SURFACE (1 << 0) 601 + 630 602 #define DC_WINBUF_AD_UFLOW_STATUS 0xbca 631 603 #define DC_WINBUF_BD_UFLOW_STATUS 0xdca 632 604 #define DC_WINBUF_CD_UFLOW_STATUS 0xfca 605 + 606 + /* Tegra186 and later */ 607 + #define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL 0x702 608 + #define OWNER_MASK (0xf << 0) 609 + #define OWNER(x) (((x) & 0xf) << 0) 610 + 611 + #define DC_WIN_CROPPED_SIZE 0x706 612 + 613 + #define DC_WIN_PLANAR_STORAGE 0x709 614 + #define PITCH(x) (((x) >> 6) & 0x1fff) 615 + 616 + #define DC_WIN_SET_PARAMS 0x70d 617 + #define CLAMP_BEFORE_BLEND (1 << 15) 618 + #define DEGAMMA_NONE (0 << 13) 619 + #define DEGAMMA_SRGB (1 << 13) 620 + #define DEGAMMA_YUV8_10 (2 << 13) 621 + #define DEGAMMA_YUV12 (3 << 13) 622 + #define INPUT_RANGE_BYPASS (0 << 10) 623 + #define INPUT_RANGE_LIMITED (1 << 10) 624 + #define INPUT_RANGE_FULL (2 << 10) 625 + #define COLOR_SPACE_RGB (0 << 8) 626 + #define COLOR_SPACE_YUV_601 (1 << 8) 627 + #define COLOR_SPACE_YUV_709 (2 << 8) 628 + #define COLOR_SPACE_YUV_2020 (3 << 8) 629 + 630 + #define DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER 0x70e 631 + #define HORIZONTAL_TAPS_2 (1 << 3) 632 + #define HORIZONTAL_TAPS_5 (4 << 3) 633 + #define VERTICAL_TAPS_2 (1 << 0) 634 + #define VERTICAL_TAPS_5 (4 << 0) 635 + 636 + #define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE 0x711 637 + #define INPUT_SCALER_USE422 (1 << 2) 638 + #define INPUT_SCALER_VBYPASS (1 << 1) 639 + #define INPUT_SCALER_HBYPASS (1 << 0) 640 + 641 + #define DC_WIN_BLEND_LAYER_CONTROL 0x716 642 + #define COLOR_KEY_NONE (0 << 25) 643 + #define COLOR_KEY_SRC (1 << 25) 644 + #define COLOR_KEY_DST (2 << 25) 645 + #define BLEND_BYPASS (1 << 24) 646 + #define K2(x) (((x) & 0xff) << 16) 647 + #define K1(x) (((x) & 0xff) << 8) 648 + #define WINDOW_LAYER_DEPTH(x) (((x) & 0xff) << 0) 649 + 650 + #define DC_WIN_BLEND_MATCH_SELECT 0x717 651 + #define BLEND_FACTOR_DST_ALPHA_ZERO (0 << 12) 652 + #define BLEND_FACTOR_DST_ALPHA_ONE (1 << 12) 653 + #define BLEND_FACTOR_DST_ALPHA_NEG_K1_TIMES_SRC (2 << 12) 654 + #define BLEND_FACTOR_DST_ALPHA_K2 (3 << 12) 655 + #define BLEND_FACTOR_SRC_ALPHA_ZERO (0 << 8) 656 + #define BLEND_FACTOR_SRC_ALPHA_K1 (1 << 8) 657 + #define BLEND_FACTOR_SRC_ALPHA_K2 (2 << 8) 658 + #define BLEND_FACTOR_SRC_ALPHA_NEG_K1_TIMES_DST (3 << 8) 659 + #define BLEND_FACTOR_DST_COLOR_ZERO (0 << 4) 660 + #define BLEND_FACTOR_DST_COLOR_ONE (1 << 4) 661 + #define BLEND_FACTOR_DST_COLOR_K1 (2 << 4) 662 + #define BLEND_FACTOR_DST_COLOR_K2 (3 << 4) 663 + #define BLEND_FACTOR_DST_COLOR_K1_TIMES_DST (4 << 4) 664 + #define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_DST (5 << 4) 665 + #define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC (6 << 4) 666 + #define BLEND_FACTOR_DST_COLOR_NEG_K1 (7 << 4) 667 + #define BLEND_FACTOR_SRC_COLOR_ZERO (0 << 0) 668 + #define BLEND_FACTOR_SRC_COLOR_ONE (1 << 0) 669 + #define BLEND_FACTOR_SRC_COLOR_K1 (2 << 0) 670 + #define BLEND_FACTOR_SRC_COLOR_K1_TIMES_DST (3 << 0) 671 + #define BLEND_FACTOR_SRC_COLOR_NEG_K1_TIMES_DST (4 << 0) 672 + #define BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC (5 << 0) 673 + 674 + #define DC_WIN_BLEND_NOMATCH_SELECT 0x718 675 + 676 + #define DC_WIN_PRECOMP_WGRP_PARAMS 0x724 677 + #define SWAP_UV (1 << 0) 678 + 679 + #define DC_WIN_WINDOW_SET_CONTROL 0x730 680 + #define CONTROL_CSC_ENABLE (1 << 5) 681 + 682 + #define DC_WINBUF_CROPPED_POINT 0x806 683 + #define OFFSET_Y(x) (((x) & 0xffff) << 16) 684 + #define OFFSET_X(x) (((x) & 0xffff) << 0) 633 685 634 686 #endif /* TEGRA_DC_H */
+64 -3
drivers/gpu/drm/tegra/drm.c
··· 33 33 struct mutex lock; 34 34 }; 35 35 36 + static struct drm_atomic_state * 37 + tegra_atomic_state_alloc(struct drm_device *drm) 38 + { 39 + struct tegra_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL); 40 + 41 + if (!state || drm_atomic_state_init(drm, &state->base) < 0) { 42 + kfree(state); 43 + return NULL; 44 + } 45 + 46 + return &state->base; 47 + } 48 + 49 + static void tegra_atomic_state_clear(struct drm_atomic_state *state) 50 + { 51 + struct tegra_atomic_state *tegra = to_tegra_atomic_state(state); 52 + 53 + drm_atomic_state_default_clear(state); 54 + tegra->clk_disp = NULL; 55 + tegra->dc = NULL; 56 + tegra->rate = 0; 57 + } 58 + 59 + static void tegra_atomic_state_free(struct drm_atomic_state *state) 60 + { 61 + drm_atomic_state_default_release(state); 62 + kfree(state); 63 + } 64 + 36 65 static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { 37 66 .fb_create = tegra_fb_create, 38 67 #ifdef CONFIG_DRM_FBDEV_EMULATION ··· 69 40 #endif 70 41 .atomic_check = drm_atomic_helper_check, 71 42 .atomic_commit = drm_atomic_helper_commit, 43 + .atomic_state_alloc = tegra_atomic_state_alloc, 44 + .atomic_state_clear = tegra_atomic_state_clear, 45 + .atomic_state_free = tegra_atomic_state_free, 72 46 }; 47 + 48 + static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state) 49 + { 50 + struct drm_device *drm = old_state->dev; 51 + struct tegra_drm *tegra = drm->dev_private; 52 + 53 + if (tegra->hub) { 54 + drm_atomic_helper_commit_modeset_disables(drm, old_state); 55 + tegra_display_hub_atomic_commit(drm, old_state); 56 + drm_atomic_helper_commit_planes(drm, old_state, 0); 57 + drm_atomic_helper_commit_modeset_enables(drm, old_state); 58 + drm_atomic_helper_commit_hw_done(old_state); 59 + drm_atomic_helper_wait_for_vblanks(drm, old_state); 60 + drm_atomic_helper_cleanup_planes(drm, old_state); 61 + } else { 62 + drm_atomic_helper_commit_tail_rpm(old_state); 63 + } 64 + } 73 65 74 66 static const struct drm_mode_config_helper_funcs 75 67 tegra_drm_mode_config_helpers = { 76 - .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm, 68 + .atomic_commit_tail = tegra_atomic_commit_tail, 77 69 }; 78 70 79 71 static int tegra_drm_load(struct drm_device *drm, unsigned long flags) ··· 169 119 if (err < 0) 170 120 goto fbdev; 171 121 122 + if (tegra->hub) { 123 + err = tegra_display_hub_prepare(tegra->hub); 124 + if (err < 0) 125 + goto device; 126 + } 127 + 172 128 /* 173 129 * We don't use the drm_irq_install() helpers provided by the DRM 174 130 * core, so we need to set this manually in order to allow the ··· 187 131 188 132 err = drm_vblank_init(drm, drm->mode_config.num_crtc); 189 133 if (err < 0) 190 - goto device; 134 + goto hub; 191 135 192 136 drm_mode_config_reset(drm); 193 137 194 138 err = tegra_drm_fb_init(drm); 195 139 if (err < 0) 196 - goto device; 140 + goto hub; 197 141 198 142 return 0; 199 143 144 + hub: 145 + if (tegra->hub) 146 + tegra_display_hub_cleanup(tegra->hub); 200 147 device: 201 148 host1x_device_exit(device); 202 149 fbdev: ··· 1294 1235 { .compatible = "nvidia,tegra210-sor", }, 1295 1236 { .compatible = "nvidia,tegra210-sor1", }, 1296 1237 { .compatible = "nvidia,tegra210-vic", }, 1238 + { .compatible = "nvidia,tegra186-display", }, 1297 1239 { .compatible = "nvidia,tegra186-vic", }, 1298 1240 { /* sentinel */ } 1299 1241 }; ··· 1310 1250 }; 1311 1251 1312 1252 static struct platform_driver * const drivers[] = { 1253 + &tegra_display_hub_driver, 1313 1254 &tegra_dc_driver, 1314 1255 &tegra_hdmi_driver, 1315 1256 &tegra_dsi_driver,
+19
drivers/gpu/drm/tegra/drm.h
··· 16 16 #include <linux/of_gpio.h> 17 17 18 18 #include <drm/drmP.h> 19 + #include <drm/drm_atomic.h> 19 20 #include <drm/drm_crtc_helper.h> 20 21 #include <drm/drm_edid.h> 21 22 #include <drm/drm_encoder.h> ··· 24 23 #include <drm/drm_fixed.h> 25 24 26 25 #include "gem.h" 26 + #include "hub.h" 27 27 #include "trace.h" 28 28 29 29 struct reset_control; ··· 41 39 struct tegra_fb *fb; 42 40 }; 43 41 #endif 42 + 43 + struct tegra_atomic_state { 44 + struct drm_atomic_state base; 45 + 46 + struct clk *clk_disp; 47 + struct tegra_dc *dc; 48 + unsigned long rate; 49 + }; 50 + 51 + static inline struct tegra_atomic_state * 52 + to_tegra_atomic_state(struct drm_atomic_state *state) 53 + { 54 + return container_of(state, struct tegra_atomic_state, base); 55 + } 44 56 45 57 struct tegra_drm { 46 58 struct drm_device *drm; ··· 77 61 #endif 78 62 79 63 unsigned int pitch_align; 64 + 65 + struct tegra_display_hub *hub; 80 66 81 67 struct drm_atomic_state *state; 82 68 }; ··· 205 187 void tegra_fb_output_poll_changed(struct drm_device *drm); 206 188 #endif 207 189 190 + extern struct platform_driver tegra_display_hub_driver; 208 191 extern struct platform_driver tegra_dc_driver; 209 192 extern struct platform_driver tegra_hdmi_driver; 210 193 extern struct platform_driver tegra_dsi_driver;
+790
drivers/gpu/drm/tegra/hub.c
··· 1 + /* 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 + 28 + static const u32 tegra_shared_plane_formats[] = { 29 + DRM_FORMAT_XBGR8888, 30 + DRM_FORMAT_XRGB8888, 31 + DRM_FORMAT_RGB565, 32 + }; 33 + 34 + static inline unsigned int tegra_plane_offset(struct tegra_shared_plane *plane, 35 + unsigned int offset) 36 + { 37 + struct tegra_plane *p = &plane->base; 38 + 39 + if (offset >= 0x500 && offset <= 0x581) { 40 + offset = 0x000 + (offset - 0x500); 41 + return p->offset + offset; 42 + } 43 + 44 + if (offset >= 0x700 && offset <= 0x73c) { 45 + offset = 0x180 + (offset - 0x700); 46 + return p->offset + offset; 47 + } 48 + 49 + if (offset >= 0x800 && offset <= 0x83e) { 50 + offset = 0x1c0 + (offset - 0x800); 51 + return p->offset + offset; 52 + } 53 + 54 + dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset); 55 + 56 + return p->offset + offset; 57 + } 58 + 59 + static inline u32 tegra_plane_readl(struct tegra_shared_plane *plane, 60 + unsigned int offset) 61 + { 62 + return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset)); 63 + } 64 + 65 + static inline void tegra_plane_writel(struct tegra_shared_plane *plane, 66 + u32 value, unsigned int offset) 67 + { 68 + tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset)); 69 + } 70 + 71 + static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp) 72 + { 73 + mutex_lock(&wgrp->lock); 74 + 75 + if (wgrp->usecount == 0) { 76 + pm_runtime_get_sync(wgrp->parent); 77 + reset_control_deassert(wgrp->rst); 78 + } 79 + 80 + wgrp->usecount++; 81 + mutex_unlock(&wgrp->lock); 82 + 83 + return 0; 84 + } 85 + 86 + static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp) 87 + { 88 + int err; 89 + 90 + mutex_lock(&wgrp->lock); 91 + 92 + if (wgrp->usecount == 1) { 93 + err = reset_control_assert(wgrp->rst); 94 + if (err < 0) { 95 + pr_err("failed to assert reset for window group %u\n", 96 + wgrp->index); 97 + } 98 + 99 + pm_runtime_put(wgrp->parent); 100 + } 101 + 102 + wgrp->usecount--; 103 + mutex_unlock(&wgrp->lock); 104 + } 105 + 106 + int tegra_display_hub_prepare(struct tegra_display_hub *hub) 107 + { 108 + unsigned int i; 109 + 110 + /* 111 + * XXX Enabling/disabling windowgroups needs to happen when the owner 112 + * display controller is disabled. There's currently no good point at 113 + * which this could be executed, so unconditionally enable all window 114 + * groups for now. 115 + */ 116 + for (i = 0; i < hub->soc->num_wgrps; i++) { 117 + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; 118 + 119 + tegra_windowgroup_enable(wgrp); 120 + } 121 + 122 + return 0; 123 + } 124 + 125 + void tegra_display_hub_cleanup(struct tegra_display_hub *hub) 126 + { 127 + unsigned int i; 128 + 129 + /* 130 + * XXX Remove this once window groups can be more fine-grainedly 131 + * enabled and disabled. 132 + */ 133 + for (i = 0; i < hub->soc->num_wgrps; i++) { 134 + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; 135 + 136 + tegra_windowgroup_disable(wgrp); 137 + } 138 + } 139 + 140 + static void tegra_shared_plane_update(struct tegra_shared_plane *plane) 141 + { 142 + struct tegra_dc *dc = plane->dc; 143 + unsigned long timeout; 144 + u32 mask, value; 145 + 146 + mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index; 147 + tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); 148 + 149 + timeout = jiffies + msecs_to_jiffies(1000); 150 + 151 + while (time_before(jiffies, timeout)) { 152 + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 153 + if ((value & mask) == 0) 154 + break; 155 + 156 + usleep_range(100, 400); 157 + } 158 + } 159 + 160 + static void tegra_shared_plane_activate(struct tegra_shared_plane *plane) 161 + { 162 + struct tegra_dc *dc = plane->dc; 163 + unsigned long timeout; 164 + u32 mask, value; 165 + 166 + mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index; 167 + tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL); 168 + 169 + timeout = jiffies + msecs_to_jiffies(1000); 170 + 171 + while (time_before(jiffies, timeout)) { 172 + value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 173 + if ((value & mask) == 0) 174 + break; 175 + 176 + usleep_range(100, 400); 177 + } 178 + } 179 + 180 + static unsigned int 181 + tegra_shared_plane_get_owner(struct tegra_shared_plane *plane, 182 + struct tegra_dc *dc) 183 + { 184 + unsigned int offset = 185 + tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); 186 + 187 + return tegra_dc_readl(dc, offset) & OWNER_MASK; 188 + } 189 + 190 + static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc, 191 + struct tegra_shared_plane *plane) 192 + { 193 + struct device *dev = dc->dev; 194 + 195 + if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) { 196 + if (plane->dc == dc) 197 + return true; 198 + 199 + dev_WARN(dev, "head %u owns window %u but is not attached\n", 200 + dc->pipe, plane->base.index); 201 + } 202 + 203 + return false; 204 + } 205 + 206 + static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane, 207 + struct tegra_dc *new) 208 + { 209 + unsigned int offset = 210 + tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL); 211 + struct tegra_dc *old = plane->dc, *dc = new ? new : old; 212 + struct device *dev = new ? new->dev : old->dev; 213 + unsigned int owner, index = plane->base.index; 214 + u32 value; 215 + 216 + value = tegra_dc_readl(dc, offset); 217 + owner = value & OWNER_MASK; 218 + 219 + if (new && (owner != OWNER_MASK && owner != new->pipe)) { 220 + dev_WARN(dev, "window %u owned by head %u\n", index, owner); 221 + return -EBUSY; 222 + } 223 + 224 + /* 225 + * This seems to happen whenever the head has been disabled with one 226 + * or more windows being active. This is harmless because we'll just 227 + * reassign the window to the new head anyway. 228 + */ 229 + if (old && owner == OWNER_MASK) 230 + dev_dbg(dev, "window %u not owned by head %u but %u\n", index, 231 + old->pipe, owner); 232 + 233 + value &= ~OWNER_MASK; 234 + 235 + if (new) 236 + value |= OWNER(new->pipe); 237 + else 238 + value |= OWNER_MASK; 239 + 240 + tegra_dc_writel(dc, value, offset); 241 + 242 + plane->dc = new; 243 + 244 + return 0; 245 + } 246 + 247 + static void tegra_dc_assign_shared_plane(struct tegra_dc *dc, 248 + struct tegra_shared_plane *plane) 249 + { 250 + u32 value; 251 + int err; 252 + 253 + if (!tegra_dc_owns_shared_plane(dc, plane)) { 254 + err = tegra_shared_plane_set_owner(plane, dc); 255 + if (err < 0) 256 + return; 257 + } 258 + 259 + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); 260 + value |= MODE_FOUR_LINES; 261 + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG); 262 + 263 + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); 264 + value = SLOTS(1); 265 + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER); 266 + 267 + /* disable watermark */ 268 + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); 269 + value &= ~LATENCY_CTL_MODE_ENABLE; 270 + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA); 271 + 272 + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); 273 + value |= WATERMARK_MASK; 274 + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB); 275 + 276 + /* pipe meter */ 277 + value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); 278 + value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0); 279 + tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER); 280 + 281 + /* mempool entries */ 282 + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); 283 + value = MEMPOOL_ENTRIES(0x331); 284 + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG); 285 + 286 + value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP); 287 + value &= ~THREAD_NUM_MASK; 288 + value |= THREAD_NUM(plane->base.index); 289 + value |= THREAD_GROUP_ENABLE; 290 + tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP); 291 + 292 + tegra_shared_plane_update(plane); 293 + tegra_shared_plane_activate(plane); 294 + } 295 + 296 + static void tegra_dc_remove_shared_plane(struct tegra_dc *dc, 297 + struct tegra_shared_plane *plane) 298 + { 299 + tegra_shared_plane_set_owner(plane, NULL); 300 + } 301 + 302 + static int tegra_shared_plane_atomic_check(struct drm_plane *plane, 303 + struct drm_plane_state *state) 304 + { 305 + struct tegra_plane_state *plane_state = to_tegra_plane_state(state); 306 + struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane); 307 + struct tegra_bo_tiling *tiling = &plane_state->tiling; 308 + struct tegra_dc *dc = to_tegra_dc(state->crtc); 309 + int err; 310 + 311 + /* no need for further checks if the plane is being disabled */ 312 + if (!state->crtc || !state->fb) 313 + return 0; 314 + 315 + err = tegra_plane_format(state->fb->format->format, 316 + &plane_state->format, 317 + &plane_state->swap); 318 + if (err < 0) 319 + return err; 320 + 321 + err = tegra_fb_get_tiling(state->fb, tiling); 322 + if (err < 0) 323 + return err; 324 + 325 + if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK && 326 + !dc->soc->supports_block_linear) { 327 + DRM_ERROR("hardware doesn't support block linear mode\n"); 328 + return -EINVAL; 329 + } 330 + 331 + /* 332 + * Tegra doesn't support different strides for U and V planes so we 333 + * error out if the user tries to display a framebuffer with such a 334 + * configuration. 335 + */ 336 + if (state->fb->format->num_planes > 2) { 337 + if (state->fb->pitches[2] != state->fb->pitches[1]) { 338 + DRM_ERROR("unsupported UV-plane configuration\n"); 339 + return -EINVAL; 340 + } 341 + } 342 + 343 + /* XXX scaling is not yet supported, add a check here */ 344 + 345 + err = tegra_plane_state_add(&tegra->base, state); 346 + if (err < 0) 347 + return err; 348 + 349 + return 0; 350 + } 351 + 352 + static void tegra_shared_plane_atomic_disable(struct drm_plane *plane, 353 + struct drm_plane_state *old_state) 354 + { 355 + struct tegra_shared_plane *p = to_tegra_shared_plane(plane); 356 + struct tegra_dc *dc = to_tegra_dc(old_state->crtc); 357 + u32 value; 358 + 359 + /* rien ne va plus */ 360 + if (!old_state || !old_state->crtc) 361 + return; 362 + 363 + /* 364 + * XXX Legacy helpers seem to sometimes call ->atomic_disable() even 365 + * on planes that are already disabled. Make sure we fallback to the 366 + * head for this particular state instead of crashing. 367 + */ 368 + if (WARN_ON(p->dc == NULL)) 369 + p->dc = dc; 370 + 371 + pm_runtime_get_sync(dc->dev); 372 + 373 + value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS); 374 + value &= ~WIN_ENABLE; 375 + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 376 + 377 + tegra_dc_remove_shared_plane(dc, p); 378 + 379 + pm_runtime_put(dc->dev); 380 + } 381 + 382 + static void tegra_shared_plane_atomic_update(struct drm_plane *plane, 383 + struct drm_plane_state *old_state) 384 + { 385 + struct tegra_plane_state *state = to_tegra_plane_state(plane->state); 386 + struct tegra_shared_plane *p = to_tegra_shared_plane(plane); 387 + struct tegra_dc *dc = to_tegra_dc(plane->state->crtc); 388 + struct drm_framebuffer *fb = plane->state->fb; 389 + struct tegra_bo *bo; 390 + dma_addr_t base; 391 + u32 value; 392 + 393 + /* rien ne va plus */ 394 + if (!plane->state->crtc || !plane->state->fb) 395 + return; 396 + 397 + if (!plane->state->visible) { 398 + tegra_shared_plane_atomic_disable(plane, old_state); 399 + return; 400 + } 401 + 402 + pm_runtime_get_sync(dc->dev); 403 + 404 + tegra_dc_assign_shared_plane(dc, p); 405 + 406 + tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL); 407 + 408 + /* blending */ 409 + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 410 + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 411 + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 412 + tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT); 413 + 414 + value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 | 415 + BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC | 416 + BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC; 417 + tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT); 418 + 419 + value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->base.depth); 420 + tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL); 421 + 422 + /* bypass scaling */ 423 + value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5; 424 + tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER); 425 + 426 + value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS; 427 + tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE); 428 + 429 + /* disable compression */ 430 + tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL); 431 + 432 + bo = tegra_fb_get_plane(fb, 0); 433 + base = bo->paddr; 434 + 435 + tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH); 436 + tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS); 437 + 438 + value = V_POSITION(plane->state->crtc_y) | 439 + H_POSITION(plane->state->crtc_x); 440 + tegra_plane_writel(p, value, DC_WIN_POSITION); 441 + 442 + value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w); 443 + tegra_plane_writel(p, value, DC_WIN_SIZE); 444 + 445 + value = WIN_ENABLE | COLOR_EXPAND; 446 + tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS); 447 + 448 + value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w); 449 + tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE); 450 + 451 + tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI); 452 + tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR); 453 + 454 + value = PITCH(fb->pitches[0]); 455 + tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE); 456 + 457 + value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL; 458 + tegra_plane_writel(p, value, DC_WIN_SET_PARAMS); 459 + 460 + value = OFFSET_X(plane->state->src_y >> 16) | 461 + OFFSET_Y(plane->state->src_x >> 16); 462 + tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT); 463 + 464 + if (dc->soc->supports_block_linear) { 465 + unsigned long height = state->tiling.value; 466 + 467 + /* XXX */ 468 + switch (state->tiling.mode) { 469 + case TEGRA_BO_TILING_MODE_PITCH: 470 + value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) | 471 + DC_WINBUF_SURFACE_KIND_PITCH; 472 + break; 473 + 474 + /* XXX not supported on Tegra186 and later */ 475 + case TEGRA_BO_TILING_MODE_TILED: 476 + value = DC_WINBUF_SURFACE_KIND_TILED; 477 + break; 478 + 479 + case TEGRA_BO_TILING_MODE_BLOCK: 480 + value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) | 481 + DC_WINBUF_SURFACE_KIND_BLOCK; 482 + break; 483 + } 484 + 485 + tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND); 486 + } 487 + 488 + /* disable gamut CSC */ 489 + value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL); 490 + value &= ~CONTROL_CSC_ENABLE; 491 + tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL); 492 + 493 + pm_runtime_put(dc->dev); 494 + } 495 + 496 + static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = { 497 + .atomic_check = tegra_shared_plane_atomic_check, 498 + .atomic_update = tegra_shared_plane_atomic_update, 499 + .atomic_disable = tegra_shared_plane_atomic_disable, 500 + }; 501 + 502 + struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, 503 + struct tegra_dc *dc, 504 + unsigned int wgrp, 505 + unsigned int index) 506 + { 507 + enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY; 508 + struct tegra_drm *tegra = drm->dev_private; 509 + struct tegra_display_hub *hub = tegra->hub; 510 + /* planes can be assigned to arbitrary CRTCs */ 511 + unsigned int possible_crtcs = 0x7; 512 + struct tegra_shared_plane *plane; 513 + unsigned int num_formats; 514 + struct drm_plane *p; 515 + const u32 *formats; 516 + int err; 517 + 518 + plane = kzalloc(sizeof(*plane), GFP_KERNEL); 519 + if (!plane) 520 + return ERR_PTR(-ENOMEM); 521 + 522 + plane->base.offset = 0x0a00 + 0x0300 * index; 523 + plane->base.index = index; 524 + plane->base.depth = 0; 525 + 526 + plane->wgrp = &hub->wgrps[wgrp]; 527 + plane->wgrp->parent = dc->dev; 528 + 529 + p = &plane->base.base; 530 + 531 + num_formats = ARRAY_SIZE(tegra_shared_plane_formats); 532 + formats = tegra_shared_plane_formats; 533 + 534 + err = drm_universal_plane_init(drm, p, possible_crtcs, 535 + &tegra_plane_funcs, formats, 536 + num_formats, NULL, type, NULL); 537 + if (err < 0) { 538 + kfree(plane); 539 + return ERR_PTR(err); 540 + } 541 + 542 + drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs); 543 + 544 + return p; 545 + } 546 + 547 + static void tegra_display_hub_update(struct tegra_dc *dc) 548 + { 549 + u32 value; 550 + 551 + pm_runtime_get_sync(dc->dev); 552 + 553 + value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL); 554 + value &= ~LATENCY_EVENT; 555 + tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL); 556 + 557 + value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); 558 + value = CURS_SLOTS(1) | WGRP_SLOTS(1); 559 + tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER); 560 + 561 + tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL); 562 + tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 563 + tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL); 564 + tegra_dc_readl(dc, DC_CMD_STATE_CONTROL); 565 + 566 + pm_runtime_put(dc->dev); 567 + } 568 + 569 + void tegra_display_hub_atomic_commit(struct drm_device *drm, 570 + struct drm_atomic_state *state) 571 + { 572 + struct tegra_atomic_state *s = to_tegra_atomic_state(state); 573 + struct tegra_drm *tegra = drm->dev_private; 574 + struct tegra_display_hub *hub = tegra->hub; 575 + struct device *dev = hub->client.dev; 576 + int err; 577 + 578 + if (s->clk_disp) { 579 + err = clk_set_rate(s->clk_disp, s->rate); 580 + if (err < 0) 581 + dev_err(dev, "failed to set rate of %pC to %lu Hz\n", 582 + s->clk_disp, s->rate); 583 + 584 + err = clk_set_parent(hub->clk_disp, s->clk_disp); 585 + if (err < 0) 586 + dev_err(dev, "failed to set parent of %pC to %pC: %d\n", 587 + hub->clk_disp, s->clk_disp, err); 588 + } 589 + 590 + if (s->dc) 591 + tegra_display_hub_update(s->dc); 592 + } 593 + 594 + static int tegra_display_hub_init(struct host1x_client *client) 595 + { 596 + struct tegra_display_hub *hub = to_tegra_display_hub(client); 597 + struct drm_device *drm = dev_get_drvdata(client->parent); 598 + struct tegra_drm *tegra = drm->dev_private; 599 + 600 + tegra->hub = hub; 601 + 602 + return 0; 603 + } 604 + 605 + static int tegra_display_hub_exit(struct host1x_client *client) 606 + { 607 + struct drm_device *drm = dev_get_drvdata(client->parent); 608 + struct tegra_drm *tegra = drm->dev_private; 609 + 610 + tegra->hub = NULL; 611 + 612 + return 0; 613 + } 614 + 615 + static const struct host1x_client_ops tegra_display_hub_ops = { 616 + .init = tegra_display_hub_init, 617 + .exit = tegra_display_hub_exit, 618 + }; 619 + 620 + static int tegra_display_hub_probe(struct platform_device *pdev) 621 + { 622 + struct tegra_display_hub *hub; 623 + unsigned int i; 624 + int err; 625 + 626 + hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL); 627 + if (!hub) 628 + return -ENOMEM; 629 + 630 + hub->soc = of_device_get_match_data(&pdev->dev); 631 + 632 + hub->clk_disp = devm_clk_get(&pdev->dev, "disp"); 633 + if (IS_ERR(hub->clk_disp)) { 634 + err = PTR_ERR(hub->clk_disp); 635 + return err; 636 + } 637 + 638 + hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc"); 639 + if (IS_ERR(hub->clk_dsc)) { 640 + err = PTR_ERR(hub->clk_dsc); 641 + return err; 642 + } 643 + 644 + hub->clk_hub = devm_clk_get(&pdev->dev, "hub"); 645 + if (IS_ERR(hub->clk_hub)) { 646 + err = PTR_ERR(hub->clk_hub); 647 + return err; 648 + } 649 + 650 + hub->rst = devm_reset_control_get(&pdev->dev, "misc"); 651 + if (IS_ERR(hub->rst)) { 652 + err = PTR_ERR(hub->rst); 653 + return err; 654 + } 655 + 656 + hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps, 657 + sizeof(*hub->wgrps), GFP_KERNEL); 658 + if (!hub->wgrps) 659 + return -ENOMEM; 660 + 661 + for (i = 0; i < hub->soc->num_wgrps; i++) { 662 + struct tegra_windowgroup *wgrp = &hub->wgrps[i]; 663 + char id[8]; 664 + 665 + snprintf(id, sizeof(id), "wgrp%u", i); 666 + mutex_init(&wgrp->lock); 667 + wgrp->usecount = 0; 668 + wgrp->index = i; 669 + 670 + wgrp->rst = devm_reset_control_get(&pdev->dev, id); 671 + if (IS_ERR(wgrp->rst)) 672 + return PTR_ERR(wgrp->rst); 673 + 674 + err = reset_control_assert(wgrp->rst); 675 + if (err < 0) 676 + return err; 677 + } 678 + 679 + /* XXX: enable clock across reset? */ 680 + err = reset_control_assert(hub->rst); 681 + if (err < 0) 682 + return err; 683 + 684 + platform_set_drvdata(pdev, hub); 685 + pm_runtime_enable(&pdev->dev); 686 + 687 + INIT_LIST_HEAD(&hub->client.list); 688 + hub->client.ops = &tegra_display_hub_ops; 689 + hub->client.dev = &pdev->dev; 690 + 691 + err = host1x_client_register(&hub->client); 692 + if (err < 0) 693 + dev_err(&pdev->dev, "failed to register host1x client: %d\n", 694 + err); 695 + 696 + return err; 697 + } 698 + 699 + static int tegra_display_hub_remove(struct platform_device *pdev) 700 + { 701 + struct tegra_display_hub *hub = platform_get_drvdata(pdev); 702 + int err; 703 + 704 + err = host1x_client_unregister(&hub->client); 705 + if (err < 0) { 706 + dev_err(&pdev->dev, "failed to unregister host1x client: %d\n", 707 + err); 708 + } 709 + 710 + pm_runtime_disable(&pdev->dev); 711 + 712 + return err; 713 + } 714 + 715 + static int tegra_display_hub_suspend(struct device *dev) 716 + { 717 + struct tegra_display_hub *hub = dev_get_drvdata(dev); 718 + int err; 719 + 720 + err = reset_control_assert(hub->rst); 721 + if (err < 0) 722 + return err; 723 + 724 + clk_disable_unprepare(hub->clk_hub); 725 + clk_disable_unprepare(hub->clk_dsc); 726 + clk_disable_unprepare(hub->clk_disp); 727 + 728 + return 0; 729 + } 730 + 731 + static int tegra_display_hub_resume(struct device *dev) 732 + { 733 + struct tegra_display_hub *hub = dev_get_drvdata(dev); 734 + int err; 735 + 736 + err = clk_prepare_enable(hub->clk_disp); 737 + if (err < 0) 738 + return err; 739 + 740 + err = clk_prepare_enable(hub->clk_dsc); 741 + if (err < 0) 742 + goto disable_disp; 743 + 744 + err = clk_prepare_enable(hub->clk_hub); 745 + if (err < 0) 746 + goto disable_dsc; 747 + 748 + err = reset_control_deassert(hub->rst); 749 + if (err < 0) 750 + goto disable_hub; 751 + 752 + return 0; 753 + 754 + disable_hub: 755 + clk_disable_unprepare(hub->clk_hub); 756 + disable_dsc: 757 + clk_disable_unprepare(hub->clk_dsc); 758 + disable_disp: 759 + clk_disable_unprepare(hub->clk_disp); 760 + return err; 761 + } 762 + 763 + static const struct dev_pm_ops tegra_display_hub_pm_ops = { 764 + SET_RUNTIME_PM_OPS(tegra_display_hub_suspend, 765 + tegra_display_hub_resume, NULL) 766 + }; 767 + 768 + static const struct tegra_display_hub_soc tegra186_display_hub = { 769 + .num_wgrps = 6, 770 + }; 771 + 772 + static const struct of_device_id tegra_display_hub_of_match[] = { 773 + { 774 + .compatible = "nvidia,tegra186-display", 775 + .data = &tegra186_display_hub 776 + }, { 777 + /* sentinel */ 778 + } 779 + }; 780 + MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match); 781 + 782 + struct platform_driver tegra_display_hub_driver = { 783 + .driver = { 784 + .name = "tegra-display-hub", 785 + .of_match_table = tegra_display_hub_of_match, 786 + .pm = &tegra_display_hub_pm_ops, 787 + }, 788 + .probe = tegra_display_hub_probe, 789 + .remove = tegra_display_hub_remove, 790 + };
+82
drivers/gpu/drm/tegra/hub.h
··· 1 + /* 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 + #ifndef TEGRA_HUB_H 10 + #define TEGRA_HUB_H 1 11 + 12 + #include <drm/drmP.h> 13 + #include <drm/drm_plane.h> 14 + 15 + #include "plane.h" 16 + 17 + struct tegra_dc; 18 + 19 + struct tegra_windowgroup { 20 + unsigned int usecount; 21 + struct mutex lock; 22 + 23 + unsigned int index; 24 + struct device *parent; 25 + struct reset_control *rst; 26 + }; 27 + 28 + struct tegra_shared_plane { 29 + struct tegra_plane base; 30 + struct tegra_windowgroup *wgrp; 31 + struct tegra_dc *dc; 32 + }; 33 + 34 + static inline struct tegra_shared_plane * 35 + to_tegra_shared_plane(struct drm_plane *plane) 36 + { 37 + return container_of(plane, struct tegra_shared_plane, base.base); 38 + } 39 + 40 + struct tegra_display_hub_soc { 41 + unsigned int num_wgrps; 42 + }; 43 + 44 + struct tegra_display_hub { 45 + struct host1x_client client; 46 + struct clk *clk_disp; 47 + struct clk *clk_dsc; 48 + struct clk *clk_hub; 49 + struct reset_control *rst; 50 + 51 + const struct tegra_display_hub_soc *soc; 52 + struct tegra_windowgroup *wgrps; 53 + }; 54 + 55 + static inline struct tegra_display_hub * 56 + to_tegra_display_hub(struct host1x_client *client) 57 + { 58 + return container_of(client, struct tegra_display_hub, client); 59 + } 60 + 61 + struct tegra_dc; 62 + struct tegra_plane; 63 + 64 + int tegra_display_hub_prepare(struct tegra_display_hub *hub); 65 + void tegra_display_hub_cleanup(struct tegra_display_hub *hub); 66 + 67 + struct drm_plane *tegra_shared_plane_create(struct drm_device *drm, 68 + struct tegra_dc *dc, 69 + unsigned int wgrp, 70 + unsigned int index); 71 + 72 + void tegra_display_hub_atomic_commit(struct drm_device *drm, 73 + struct drm_atomic_state *state); 74 + 75 + #define DC_CMD_IHUB_COMMON_MISC_CTL 0x068 76 + #define LATENCY_EVENT (1 << 3) 77 + 78 + #define DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER 0x451 79 + #define CURS_SLOTS(x) (((x) & 0xff) << 8) 80 + #define WGRP_SLOTS(x) (((x) & 0xff) << 0) 81 + 82 + #endif /* TEGRA_HUB_H */
+2
drivers/gpu/drm/tegra/plane.h
··· 15 15 16 16 struct tegra_plane { 17 17 struct drm_plane base; 18 + unsigned int offset; 18 19 unsigned int index; 20 + unsigned int depth; 19 21 }; 20 22 21 23 struct tegra_cursor {