summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/tegra
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/tegra')
-rw-r--r--drivers/gpu/drm/tegra/Makefile1
-rw-r--r--drivers/gpu/drm/tegra/dc.c31
-rw-r--r--drivers/gpu/drm/tegra/dc.h114
-rw-r--r--drivers/gpu/drm/tegra/drm.c67
-rw-r--r--drivers/gpu/drm/tegra/drm.h19
-rw-r--r--drivers/gpu/drm/tegra/hub.c790
-rw-r--r--drivers/gpu/drm/tegra/hub.h82
-rw-r--r--drivers/gpu/drm/tegra/plane.h2
8 files changed, 1103 insertions, 3 deletions
diff --git a/drivers/gpu/drm/tegra/Makefile b/drivers/gpu/drm/tegra/Makefile
index a47784765217..2e0d6213f6bc 100644
--- a/drivers/gpu/drm/tegra/Makefile
+++ b/drivers/gpu/drm/tegra/Makefile
@@ -5,6 +5,7 @@ tegra-drm-y := \
drm.o \
gem.o \
fb.o \
+ hub.o \
plane.o \
dc.o \
output.o \
diff --git a/drivers/gpu/drm/tegra/dc.c b/drivers/gpu/drm/tegra/dc.c
index 3aa0da6244db..25e998f8f339 100644
--- a/drivers/gpu/drm/tegra/dc.c
+++ b/drivers/gpu/drm/tegra/dc.c
@@ -447,6 +447,15 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm,
num_formats = ARRAY_SIZE(tegra_primary_plane_formats);
formats = tegra_primary_plane_formats;
+ /*
+ * XXX compute offset so that we can directly access windows.
+ *
+ * Always use window A as primary window.
+ */
+ plane->offset = 0;
+ plane->index = 0;
+ plane->depth = 255;
+
err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
&tegra_plane_funcs, formats,
num_formats, NULL,
@@ -641,7 +650,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
if (!plane)
return ERR_PTR(-ENOMEM);
+ /* XXX compute offset so that we can directly access windows */
+ plane->offset = 0;
plane->index = index;
+ plane->depth = 0;
num_formats = ARRAY_SIZE(tegra_overlay_plane_formats);
formats = tegra_overlay_plane_formats;
@@ -1382,6 +1394,25 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
+ struct tegra_atomic_state *s = to_tegra_atomic_state(state->state);
+ struct tegra_dc_state *tegra = to_dc_state(state);
+
+ /*
+ * The display hub display clock needs to be fed by the display clock
+ * with the highest frequency to ensure proper functioning of all the
+ * displays.
+ *
+ * Note that this isn't used before Tegra186, but it doesn't hurt and
+ * conditionalizing it would make the code less clean.
+ */
+ if (state->active) {
+ if (!s->clk_disp || tegra->pclk > s->rate) {
+ s->dc = to_tegra_dc(crtc);
+ s->clk_disp = s->dc->clk;
+ s->rate = tegra->pclk;
+ }
+ }
+
return 0;
}
diff --git a/drivers/gpu/drm/tegra/dc.h b/drivers/gpu/drm/tegra/dc.h
index b65dfbb0af89..22c5091006bc 100644
--- a/drivers/gpu/drm/tegra/dc.h
+++ b/drivers/gpu/drm/tegra/dc.h
@@ -209,6 +209,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define WIN_B_UPDATE (1 << 10)
#define WIN_C_UPDATE (1 << 11)
#define CURSOR_UPDATE (1 << 15)
+#define COMMON_ACTREQ (1 << 16)
+#define COMMON_UPDATE (1 << 17)
#define NC_HOST_TRIG (1 << 24)
#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042
@@ -486,6 +488,35 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define CURSOR_SRC_BLEND_MASK (3 << 8)
#define CURSOR_ALPHA 0xff
+#define DC_WIN_CORE_ACT_CONTROL 0x50e
+#define VCOUNTER (0 << 0)
+#define HCOUNTER (1 << 0)
+
+#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA 0x543
+#define LATENCY_CTL_MODE_ENABLE (1 << 2)
+
+#define DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB 0x544
+#define WATERMARK_MASK 0x1fffffff
+
+#define DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER 0x560
+#define PIPE_METER_INT(x) (((x) & 0xff) << 8)
+#define PIPE_METER_FRAC(x) (((x) & 0xff) << 0)
+
+#define DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG 0x561
+#define MEMPOOL_ENTRIES(x) (((x) & 0xffff) << 0)
+
+#define DC_WIN_CORE_IHUB_WGRP_FETCH_METER 0x562
+#define SLOTS(x) (((x) & 0xff) << 0)
+
+#define DC_WIN_CORE_IHUB_LINEBUF_CONFIG 0x563
+#define MODE_TWO_LINES (0 << 14)
+#define MODE_FOUR_LINES (1 << 14)
+
+#define DC_WIN_CORE_IHUB_THREAD_GROUP 0x568
+#define THREAD_NUM_MASK (0x1f << 1)
+#define THREAD_NUM(x) (((x) & 0x1f) << 1)
+#define THREAD_GROUP_ENABLE (1 << 0)
+
#define DC_WIN_CSC_YOF 0x611
#define DC_WIN_CSC_KYRGB 0x612
#define DC_WIN_CSC_KUR 0x613
@@ -596,8 +627,91 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define DC_WINBUF_START_ADDR_HI 0x80d
+#define DC_WINBUF_CDE_CONTROL 0x82f
+#define ENABLE_SURFACE (1 << 0)
+
#define DC_WINBUF_AD_UFLOW_STATUS 0xbca
#define DC_WINBUF_BD_UFLOW_STATUS 0xdca
#define DC_WINBUF_CD_UFLOW_STATUS 0xfca
+/* Tegra186 and later */
+#define DC_WIN_CORE_WINDOWGROUP_SET_CONTROL 0x702
+#define OWNER_MASK (0xf << 0)
+#define OWNER(x) (((x) & 0xf) << 0)
+
+#define DC_WIN_CROPPED_SIZE 0x706
+
+#define DC_WIN_PLANAR_STORAGE 0x709
+#define PITCH(x) (((x) >> 6) & 0x1fff)
+
+#define DC_WIN_SET_PARAMS 0x70d
+#define CLAMP_BEFORE_BLEND (1 << 15)
+#define DEGAMMA_NONE (0 << 13)
+#define DEGAMMA_SRGB (1 << 13)
+#define DEGAMMA_YUV8_10 (2 << 13)
+#define DEGAMMA_YUV12 (3 << 13)
+#define INPUT_RANGE_BYPASS (0 << 10)
+#define INPUT_RANGE_LIMITED (1 << 10)
+#define INPUT_RANGE_FULL (2 << 10)
+#define COLOR_SPACE_RGB (0 << 8)
+#define COLOR_SPACE_YUV_601 (1 << 8)
+#define COLOR_SPACE_YUV_709 (2 << 8)
+#define COLOR_SPACE_YUV_2020 (3 << 8)
+
+#define DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER 0x70e
+#define HORIZONTAL_TAPS_2 (1 << 3)
+#define HORIZONTAL_TAPS_5 (4 << 3)
+#define VERTICAL_TAPS_2 (1 << 0)
+#define VERTICAL_TAPS_5 (4 << 0)
+
+#define DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE 0x711
+#define INPUT_SCALER_USE422 (1 << 2)
+#define INPUT_SCALER_VBYPASS (1 << 1)
+#define INPUT_SCALER_HBYPASS (1 << 0)
+
+#define DC_WIN_BLEND_LAYER_CONTROL 0x716
+#define COLOR_KEY_NONE (0 << 25)
+#define COLOR_KEY_SRC (1 << 25)
+#define COLOR_KEY_DST (2 << 25)
+#define BLEND_BYPASS (1 << 24)
+#define K2(x) (((x) & 0xff) << 16)
+#define K1(x) (((x) & 0xff) << 8)
+#define WINDOW_LAYER_DEPTH(x) (((x) & 0xff) << 0)
+
+#define DC_WIN_BLEND_MATCH_SELECT 0x717
+#define BLEND_FACTOR_DST_ALPHA_ZERO (0 << 12)
+#define BLEND_FACTOR_DST_ALPHA_ONE (1 << 12)
+#define BLEND_FACTOR_DST_ALPHA_NEG_K1_TIMES_SRC (2 << 12)
+#define BLEND_FACTOR_DST_ALPHA_K2 (3 << 12)
+#define BLEND_FACTOR_SRC_ALPHA_ZERO (0 << 8)
+#define BLEND_FACTOR_SRC_ALPHA_K1 (1 << 8)
+#define BLEND_FACTOR_SRC_ALPHA_K2 (2 << 8)
+#define BLEND_FACTOR_SRC_ALPHA_NEG_K1_TIMES_DST (3 << 8)
+#define BLEND_FACTOR_DST_COLOR_ZERO (0 << 4)
+#define BLEND_FACTOR_DST_COLOR_ONE (1 << 4)
+#define BLEND_FACTOR_DST_COLOR_K1 (2 << 4)
+#define BLEND_FACTOR_DST_COLOR_K2 (3 << 4)
+#define BLEND_FACTOR_DST_COLOR_K1_TIMES_DST (4 << 4)
+#define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_DST (5 << 4)
+#define BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC (6 << 4)
+#define BLEND_FACTOR_DST_COLOR_NEG_K1 (7 << 4)
+#define BLEND_FACTOR_SRC_COLOR_ZERO (0 << 0)
+#define BLEND_FACTOR_SRC_COLOR_ONE (1 << 0)
+#define BLEND_FACTOR_SRC_COLOR_K1 (2 << 0)
+#define BLEND_FACTOR_SRC_COLOR_K1_TIMES_DST (3 << 0)
+#define BLEND_FACTOR_SRC_COLOR_NEG_K1_TIMES_DST (4 << 0)
+#define BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC (5 << 0)
+
+#define DC_WIN_BLEND_NOMATCH_SELECT 0x718
+
+#define DC_WIN_PRECOMP_WGRP_PARAMS 0x724
+#define SWAP_UV (1 << 0)
+
+#define DC_WIN_WINDOW_SET_CONTROL 0x730
+#define CONTROL_CSC_ENABLE (1 << 5)
+
+#define DC_WINBUF_CROPPED_POINT 0x806
+#define OFFSET_Y(x) (((x) & 0xffff) << 16)
+#define OFFSET_X(x) (((x) & 0xffff) << 0)
+
#endif /* TEGRA_DC_H */
diff --git a/drivers/gpu/drm/tegra/drm.c b/drivers/gpu/drm/tegra/drm.c
index 90d876fc6ea7..98a2494b4ed0 100644
--- a/drivers/gpu/drm/tegra/drm.c
+++ b/drivers/gpu/drm/tegra/drm.c
@@ -33,6 +33,35 @@ struct tegra_drm_file {
struct mutex lock;
};
+static struct drm_atomic_state *
+tegra_atomic_state_alloc(struct drm_device *drm)
+{
+ struct tegra_atomic_state *state = kzalloc(sizeof(*state), GFP_KERNEL);
+
+ if (!state || drm_atomic_state_init(drm, &state->base) < 0) {
+ kfree(state);
+ return NULL;
+ }
+
+ return &state->base;
+}
+
+static void tegra_atomic_state_clear(struct drm_atomic_state *state)
+{
+ struct tegra_atomic_state *tegra = to_tegra_atomic_state(state);
+
+ drm_atomic_state_default_clear(state);
+ tegra->clk_disp = NULL;
+ tegra->dc = NULL;
+ tegra->rate = 0;
+}
+
+static void tegra_atomic_state_free(struct drm_atomic_state *state)
+{
+ drm_atomic_state_default_release(state);
+ kfree(state);
+}
+
static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
.fb_create = tegra_fb_create,
#ifdef CONFIG_DRM_FBDEV_EMULATION
@@ -40,11 +69,32 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
#endif
.atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit,
+ .atomic_state_alloc = tegra_atomic_state_alloc,
+ .atomic_state_clear = tegra_atomic_state_clear,
+ .atomic_state_free = tegra_atomic_state_free,
};
+static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state)
+{
+ struct drm_device *drm = old_state->dev;
+ struct tegra_drm *tegra = drm->dev_private;
+
+ if (tegra->hub) {
+ drm_atomic_helper_commit_modeset_disables(drm, old_state);
+ tegra_display_hub_atomic_commit(drm, old_state);
+ drm_atomic_helper_commit_planes(drm, old_state, 0);
+ drm_atomic_helper_commit_modeset_enables(drm, old_state);
+ drm_atomic_helper_commit_hw_done(old_state);
+ drm_atomic_helper_wait_for_vblanks(drm, old_state);
+ drm_atomic_helper_cleanup_planes(drm, old_state);
+ } else {
+ drm_atomic_helper_commit_tail_rpm(old_state);
+ }
+}
+
static const struct drm_mode_config_helper_funcs
tegra_drm_mode_config_helpers = {
- .atomic_commit_tail = drm_atomic_helper_commit_tail_rpm,
+ .atomic_commit_tail = tegra_atomic_commit_tail,
};
static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
@@ -119,6 +169,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (err < 0)
goto fbdev;
+ if (tegra->hub) {
+ err = tegra_display_hub_prepare(tegra->hub);
+ if (err < 0)
+ goto device;
+ }
+
/*
* We don't use the drm_irq_install() helpers provided by the DRM
* core, so we need to set this manually in order to allow the
@@ -131,16 +187,19 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
err = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (err < 0)
- goto device;
+ goto hub;
drm_mode_config_reset(drm);
err = tegra_drm_fb_init(drm);
if (err < 0)
- goto device;
+ goto hub;
return 0;
+hub:
+ if (tegra->hub)
+ tegra_display_hub_cleanup(tegra->hub);
device:
host1x_device_exit(device);
fbdev:
@@ -1235,6 +1294,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra210-sor", },
{ .compatible = "nvidia,tegra210-sor1", },
{ .compatible = "nvidia,tegra210-vic", },
+ { .compatible = "nvidia,tegra186-display", },
{ .compatible = "nvidia,tegra186-vic", },
{ /* sentinel */ }
};
@@ -1250,6 +1310,7 @@ static struct host1x_driver host1x_drm_driver = {
};
static struct platform_driver * const drivers[] = {
+ &tegra_display_hub_driver,
&tegra_dc_driver,
&tegra_hdmi_driver,
&tegra_dsi_driver,
diff --git a/drivers/gpu/drm/tegra/drm.h b/drivers/gpu/drm/tegra/drm.h
index a3d84c4af357..d4dfd239e2a5 100644
--- a/drivers/gpu/drm/tegra/drm.h
+++ b/drivers/gpu/drm/tegra/drm.h
@@ -16,6 +16,7 @@
#include <linux/of_gpio.h>
#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
@@ -23,6 +24,7 @@
#include <drm/drm_fixed.h>
#include "gem.h"
+#include "hub.h"
#include "trace.h"
struct reset_control;
@@ -40,6 +42,20 @@ struct tegra_fbdev {
};
#endif
+struct tegra_atomic_state {
+ struct drm_atomic_state base;
+
+ struct clk *clk_disp;
+ struct tegra_dc *dc;
+ unsigned long rate;
+};
+
+static inline struct tegra_atomic_state *
+to_tegra_atomic_state(struct drm_atomic_state *state)
+{
+ return container_of(state, struct tegra_atomic_state, base);
+}
+
struct tegra_drm {
struct drm_device *drm;
@@ -62,6 +78,8 @@ struct tegra_drm {
unsigned int pitch_align;
+ struct tegra_display_hub *hub;
+
struct drm_atomic_state *state;
};
@@ -187,6 +205,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
void tegra_fb_output_poll_changed(struct drm_device *drm);
#endif
+extern struct platform_driver tegra_display_hub_driver;
extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_dsi_driver;
diff --git a/drivers/gpu/drm/tegra/hub.c b/drivers/gpu/drm/tegra/hub.c
new file mode 100644
index 000000000000..33d008fb8745
--- /dev/null
+++ b/drivers/gpu/drm/tegra/hub.c
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/host1x.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_graph.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset.h>
+
+#include <drm/drmP.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_atomic_helper.h>
+#include <drm/drm_crtc_helper.h>
+
+#include "drm.h"
+#include "dc.h"
+#include "plane.h"
+
+static const u32 tegra_shared_plane_formats[] = {
+ DRM_FORMAT_XBGR8888,
+ DRM_FORMAT_XRGB8888,
+ DRM_FORMAT_RGB565,
+};
+
+static inline unsigned int tegra_plane_offset(struct tegra_shared_plane *plane,
+ unsigned int offset)
+{
+ struct tegra_plane *p = &plane->base;
+
+ if (offset >= 0x500 && offset <= 0x581) {
+ offset = 0x000 + (offset - 0x500);
+ return p->offset + offset;
+ }
+
+ if (offset >= 0x700 && offset <= 0x73c) {
+ offset = 0x180 + (offset - 0x700);
+ return p->offset + offset;
+ }
+
+ if (offset >= 0x800 && offset <= 0x83e) {
+ offset = 0x1c0 + (offset - 0x800);
+ return p->offset + offset;
+ }
+
+ dev_WARN(plane->dc->dev, "invalid offset: %x\n", offset);
+
+ return p->offset + offset;
+}
+
+static inline u32 tegra_plane_readl(struct tegra_shared_plane *plane,
+ unsigned int offset)
+{
+ return tegra_dc_readl(plane->dc, tegra_plane_offset(plane, offset));
+}
+
+static inline void tegra_plane_writel(struct tegra_shared_plane *plane,
+ u32 value, unsigned int offset)
+{
+ tegra_dc_writel(plane->dc, value, tegra_plane_offset(plane, offset));
+}
+
+static int tegra_windowgroup_enable(struct tegra_windowgroup *wgrp)
+{
+ mutex_lock(&wgrp->lock);
+
+ if (wgrp->usecount == 0) {
+ pm_runtime_get_sync(wgrp->parent);
+ reset_control_deassert(wgrp->rst);
+ }
+
+ wgrp->usecount++;
+ mutex_unlock(&wgrp->lock);
+
+ return 0;
+}
+
+static void tegra_windowgroup_disable(struct tegra_windowgroup *wgrp)
+{
+ int err;
+
+ mutex_lock(&wgrp->lock);
+
+ if (wgrp->usecount == 1) {
+ err = reset_control_assert(wgrp->rst);
+ if (err < 0) {
+ pr_err("failed to assert reset for window group %u\n",
+ wgrp->index);
+ }
+
+ pm_runtime_put(wgrp->parent);
+ }
+
+ wgrp->usecount--;
+ mutex_unlock(&wgrp->lock);
+}
+
+int tegra_display_hub_prepare(struct tegra_display_hub *hub)
+{
+ unsigned int i;
+
+ /*
+ * XXX Enabling/disabling windowgroups needs to happen when the owner
+ * display controller is disabled. There's currently no good point at
+ * which this could be executed, so unconditionally enable all window
+ * groups for now.
+ */
+ for (i = 0; i < hub->soc->num_wgrps; i++) {
+ struct tegra_windowgroup *wgrp = &hub->wgrps[i];
+
+ tegra_windowgroup_enable(wgrp);
+ }
+
+ return 0;
+}
+
+void tegra_display_hub_cleanup(struct tegra_display_hub *hub)
+{
+ unsigned int i;
+
+ /*
+ * XXX Remove this once window groups can be more fine-grainedly
+ * enabled and disabled.
+ */
+ for (i = 0; i < hub->soc->num_wgrps; i++) {
+ struct tegra_windowgroup *wgrp = &hub->wgrps[i];
+
+ tegra_windowgroup_disable(wgrp);
+ }
+}
+
+static void tegra_shared_plane_update(struct tegra_shared_plane *plane)
+{
+ struct tegra_dc *dc = plane->dc;
+ unsigned long timeout;
+ u32 mask, value;
+
+ mask = COMMON_UPDATE | WIN_A_UPDATE << plane->base.index;
+ tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+ if ((value & mask) == 0)
+ break;
+
+ usleep_range(100, 400);
+ }
+}
+
+static void tegra_shared_plane_activate(struct tegra_shared_plane *plane)
+{
+ struct tegra_dc *dc = plane->dc;
+ unsigned long timeout;
+ u32 mask, value;
+
+ mask = COMMON_ACTREQ | WIN_A_ACT_REQ << plane->base.index;
+ tegra_dc_writel(dc, mask, DC_CMD_STATE_CONTROL);
+
+ timeout = jiffies + msecs_to_jiffies(1000);
+
+ while (time_before(jiffies, timeout)) {
+ value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+ if ((value & mask) == 0)
+ break;
+
+ usleep_range(100, 400);
+ }
+}
+
+static unsigned int
+tegra_shared_plane_get_owner(struct tegra_shared_plane *plane,
+ struct tegra_dc *dc)
+{
+ unsigned int offset =
+ tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
+
+ return tegra_dc_readl(dc, offset) & OWNER_MASK;
+}
+
+static bool tegra_dc_owns_shared_plane(struct tegra_dc *dc,
+ struct tegra_shared_plane *plane)
+{
+ struct device *dev = dc->dev;
+
+ if (tegra_shared_plane_get_owner(plane, dc) == dc->pipe) {
+ if (plane->dc == dc)
+ return true;
+
+ dev_WARN(dev, "head %u owns window %u but is not attached\n",
+ dc->pipe, plane->base.index);
+ }
+
+ return false;
+}
+
+static int tegra_shared_plane_set_owner(struct tegra_shared_plane *plane,
+ struct tegra_dc *new)
+{
+ unsigned int offset =
+ tegra_plane_offset(plane, DC_WIN_CORE_WINDOWGROUP_SET_CONTROL);
+ struct tegra_dc *old = plane->dc, *dc = new ? new : old;
+ struct device *dev = new ? new->dev : old->dev;
+ unsigned int owner, index = plane->base.index;
+ u32 value;
+
+ value = tegra_dc_readl(dc, offset);
+ owner = value & OWNER_MASK;
+
+ if (new && (owner != OWNER_MASK && owner != new->pipe)) {
+ dev_WARN(dev, "window %u owned by head %u\n", index, owner);
+ return -EBUSY;
+ }
+
+ /*
+ * This seems to happen whenever the head has been disabled with one
+ * or more windows being active. This is harmless because we'll just
+ * reassign the window to the new head anyway.
+ */
+ if (old && owner == OWNER_MASK)
+ dev_dbg(dev, "window %u not owned by head %u but %u\n", index,
+ old->pipe, owner);
+
+ value &= ~OWNER_MASK;
+
+ if (new)
+ value |= OWNER(new->pipe);
+ else
+ value |= OWNER_MASK;
+
+ tegra_dc_writel(dc, value, offset);
+
+ plane->dc = new;
+
+ return 0;
+}
+
+static void tegra_dc_assign_shared_plane(struct tegra_dc *dc,
+ struct tegra_shared_plane *plane)
+{
+ u32 value;
+ int err;
+
+ if (!tegra_dc_owns_shared_plane(dc, plane)) {
+ err = tegra_shared_plane_set_owner(plane, dc);
+ if (err < 0)
+ return;
+ }
+
+ value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
+ value |= MODE_FOUR_LINES;
+ tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_LINEBUF_CONFIG);
+
+ value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
+ value = SLOTS(1);
+ tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_FETCH_METER);
+
+ /* disable watermark */
+ value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
+ value &= ~LATENCY_CTL_MODE_ENABLE;
+ tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLA);
+
+ value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
+ value |= WATERMARK_MASK;
+ tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_LATENCY_CTLB);
+
+ /* pipe meter */
+ value = tegra_plane_readl(plane, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
+ value = PIPE_METER_INT(0) | PIPE_METER_FRAC(0);
+ tegra_plane_writel(plane, value, DC_WIN_CORE_PRECOMP_WGRP_PIPE_METER);
+
+ /* mempool entries */
+ value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
+ value = MEMPOOL_ENTRIES(0x331);
+ tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_WGRP_POOL_CONFIG);
+
+ value = tegra_plane_readl(plane, DC_WIN_CORE_IHUB_THREAD_GROUP);
+ value &= ~THREAD_NUM_MASK;
+ value |= THREAD_NUM(plane->base.index);
+ value |= THREAD_GROUP_ENABLE;
+ tegra_plane_writel(plane, value, DC_WIN_CORE_IHUB_THREAD_GROUP);
+
+ tegra_shared_plane_update(plane);
+ tegra_shared_plane_activate(plane);
+}
+
+static void tegra_dc_remove_shared_plane(struct tegra_dc *dc,
+ struct tegra_shared_plane *plane)
+{
+ tegra_shared_plane_set_owner(plane, NULL);
+}
+
+static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
+ struct drm_plane_state *state)
+{
+ struct tegra_plane_state *plane_state = to_tegra_plane_state(state);
+ struct tegra_shared_plane *tegra = to_tegra_shared_plane(plane);
+ struct tegra_bo_tiling *tiling = &plane_state->tiling;
+ struct tegra_dc *dc = to_tegra_dc(state->crtc);
+ int err;
+
+ /* no need for further checks if the plane is being disabled */
+ if (!state->crtc || !state->fb)
+ return 0;
+
+ err = tegra_plane_format(state->fb->format->format,
+ &plane_state->format,
+ &plane_state->swap);
+ if (err < 0)
+ return err;
+
+ err = tegra_fb_get_tiling(state->fb, tiling);
+ if (err < 0)
+ return err;
+
+ if (tiling->mode == TEGRA_BO_TILING_MODE_BLOCK &&
+ !dc->soc->supports_block_linear) {
+ DRM_ERROR("hardware doesn't support block linear mode\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Tegra doesn't support different strides for U and V planes so we
+ * error out if the user tries to display a framebuffer with such a
+ * configuration.
+ */
+ if (state->fb->format->num_planes > 2) {
+ if (state->fb->pitches[2] != state->fb->pitches[1]) {
+ DRM_ERROR("unsupported UV-plane configuration\n");
+ return -EINVAL;
+ }
+ }
+
+ /* XXX scaling is not yet supported, add a check here */
+
+ err = tegra_plane_state_add(&tegra->base, state);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static void tegra_shared_plane_atomic_disable(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct tegra_shared_plane *p = to_tegra_shared_plane(plane);
+ struct tegra_dc *dc = to_tegra_dc(old_state->crtc);
+ u32 value;
+
+ /* rien ne va plus */
+ if (!old_state || !old_state->crtc)
+ return;
+
+ /*
+ * XXX Legacy helpers seem to sometimes call ->atomic_disable() even
+ * on planes that are already disabled. Make sure we fallback to the
+ * head for this particular state instead of crashing.
+ */
+ if (WARN_ON(p->dc == NULL))
+ p->dc = dc;
+
+ pm_runtime_get_sync(dc->dev);
+
+ value = tegra_plane_readl(p, DC_WIN_WIN_OPTIONS);
+ value &= ~WIN_ENABLE;
+ tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
+
+ tegra_dc_remove_shared_plane(dc, p);
+
+ pm_runtime_put(dc->dev);
+}
+
+static void tegra_shared_plane_atomic_update(struct drm_plane *plane,
+ struct drm_plane_state *old_state)
+{
+ struct tegra_plane_state *state = to_tegra_plane_state(plane->state);
+ struct tegra_shared_plane *p = to_tegra_shared_plane(plane);
+ struct tegra_dc *dc = to_tegra_dc(plane->state->crtc);
+ struct drm_framebuffer *fb = plane->state->fb;
+ struct tegra_bo *bo;
+ dma_addr_t base;
+ u32 value;
+
+ /* rien ne va plus */
+ if (!plane->state->crtc || !plane->state->fb)
+ return;
+
+ if (!plane->state->visible) {
+ tegra_shared_plane_atomic_disable(plane, old_state);
+ return;
+ }
+
+ pm_runtime_get_sync(dc->dev);
+
+ tegra_dc_assign_shared_plane(dc, p);
+
+ tegra_plane_writel(p, VCOUNTER, DC_WIN_CORE_ACT_CONTROL);
+
+ /* blending */
+ value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
+ BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
+ BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
+ tegra_plane_writel(p, value, DC_WIN_BLEND_MATCH_SELECT);
+
+ value = BLEND_FACTOR_DST_ALPHA_ZERO | BLEND_FACTOR_SRC_ALPHA_K2 |
+ BLEND_FACTOR_DST_COLOR_NEG_K1_TIMES_SRC |
+ BLEND_FACTOR_SRC_COLOR_K1_TIMES_SRC;
+ tegra_plane_writel(p, value, DC_WIN_BLEND_NOMATCH_SELECT);
+
+ value = K2(255) | K1(255) | WINDOW_LAYER_DEPTH(p->base.depth);
+ tegra_plane_writel(p, value, DC_WIN_BLEND_LAYER_CONTROL);
+
+ /* bypass scaling */
+ value = HORIZONTAL_TAPS_5 | VERTICAL_TAPS_5;
+ tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_CONTROL_INPUT_SCALER);
+
+ value = INPUT_SCALER_VBYPASS | INPUT_SCALER_HBYPASS;
+ tegra_plane_writel(p, value, DC_WIN_WINDOWGROUP_SET_INPUT_SCALER_USAGE);
+
+ /* disable compression */
+ tegra_plane_writel(p, 0, DC_WINBUF_CDE_CONTROL);
+
+ bo = tegra_fb_get_plane(fb, 0);
+ base = bo->paddr;
+
+ tegra_plane_writel(p, state->format, DC_WIN_COLOR_DEPTH);
+ tegra_plane_writel(p, 0, DC_WIN_PRECOMP_WGRP_PARAMS);
+
+ value = V_POSITION(plane->state->crtc_y) |
+ H_POSITION(plane->state->crtc_x);
+ tegra_plane_writel(p, value, DC_WIN_POSITION);
+
+ value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
+ tegra_plane_writel(p, value, DC_WIN_SIZE);
+
+ value = WIN_ENABLE | COLOR_EXPAND;
+ tegra_plane_writel(p, value, DC_WIN_WIN_OPTIONS);
+
+ value = V_SIZE(plane->state->crtc_h) | H_SIZE(plane->state->crtc_w);
+ tegra_plane_writel(p, value, DC_WIN_CROPPED_SIZE);
+
+ tegra_plane_writel(p, upper_32_bits(base), DC_WINBUF_START_ADDR_HI);
+ tegra_plane_writel(p, lower_32_bits(base), DC_WINBUF_START_ADDR);
+
+ value = PITCH(fb->pitches[0]);
+ tegra_plane_writel(p, value, DC_WIN_PLANAR_STORAGE);
+
+ value = CLAMP_BEFORE_BLEND | DEGAMMA_SRGB | INPUT_RANGE_FULL;
+ tegra_plane_writel(p, value, DC_WIN_SET_PARAMS);
+
+ value = OFFSET_X(plane->state->src_y >> 16) |
+ OFFSET_Y(plane->state->src_x >> 16);
+ tegra_plane_writel(p, value, DC_WINBUF_CROPPED_POINT);
+
+ if (dc->soc->supports_block_linear) {
+ unsigned long height = state->tiling.value;
+
+ /* XXX */
+ switch (state->tiling.mode) {
+ case TEGRA_BO_TILING_MODE_PITCH:
+ value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(0) |
+ DC_WINBUF_SURFACE_KIND_PITCH;
+ break;
+
+ /* XXX not supported on Tegra186 and later */
+ case TEGRA_BO_TILING_MODE_TILED:
+ value = DC_WINBUF_SURFACE_KIND_TILED;
+ break;
+
+ case TEGRA_BO_TILING_MODE_BLOCK:
+ value = DC_WINBUF_SURFACE_KIND_BLOCK_HEIGHT(height) |
+ DC_WINBUF_SURFACE_KIND_BLOCK;
+ break;
+ }
+
+ tegra_plane_writel(p, value, DC_WINBUF_SURFACE_KIND);
+ }
+
+ /* disable gamut CSC */
+ value = tegra_plane_readl(p, DC_WIN_WINDOW_SET_CONTROL);
+ value &= ~CONTROL_CSC_ENABLE;
+ tegra_plane_writel(p, value, DC_WIN_WINDOW_SET_CONTROL);
+
+ pm_runtime_put(dc->dev);
+}
+
+static const struct drm_plane_helper_funcs tegra_shared_plane_helper_funcs = {
+ .atomic_check = tegra_shared_plane_atomic_check,
+ .atomic_update = tegra_shared_plane_atomic_update,
+ .atomic_disable = tegra_shared_plane_atomic_disable,
+};
+
+struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
+ struct tegra_dc *dc,
+ unsigned int wgrp,
+ unsigned int index)
+{
+ enum drm_plane_type type = DRM_PLANE_TYPE_OVERLAY;
+ struct tegra_drm *tegra = drm->dev_private;
+ struct tegra_display_hub *hub = tegra->hub;
+ /* planes can be assigned to arbitrary CRTCs */
+ unsigned int possible_crtcs = 0x7;
+ struct tegra_shared_plane *plane;
+ unsigned int num_formats;
+ struct drm_plane *p;
+ const u32 *formats;
+ int err;
+
+ plane = kzalloc(sizeof(*plane), GFP_KERNEL);
+ if (!plane)
+ return ERR_PTR(-ENOMEM);
+
+ plane->base.offset = 0x0a00 + 0x0300 * index;
+ plane->base.index = index;
+ plane->base.depth = 0;
+
+ plane->wgrp = &hub->wgrps[wgrp];
+ plane->wgrp->parent = dc->dev;
+
+ p = &plane->base.base;
+
+ num_formats = ARRAY_SIZE(tegra_shared_plane_formats);
+ formats = tegra_shared_plane_formats;
+
+ err = drm_universal_plane_init(drm, p, possible_crtcs,
+ &tegra_plane_funcs, formats,
+ num_formats, NULL, type, NULL);
+ if (err < 0) {
+ kfree(plane);
+ return ERR_PTR(err);
+ }
+
+ drm_plane_helper_add(p, &tegra_shared_plane_helper_funcs);
+
+ return p;
+}
+
+static void tegra_display_hub_update(struct tegra_dc *dc)
+{
+ u32 value;
+
+ pm_runtime_get_sync(dc->dev);
+
+ value = tegra_dc_readl(dc, DC_CMD_IHUB_COMMON_MISC_CTL);
+ value &= ~LATENCY_EVENT;
+ tegra_dc_writel(dc, value, DC_CMD_IHUB_COMMON_MISC_CTL);
+
+ value = tegra_dc_readl(dc, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
+ value = CURS_SLOTS(1) | WGRP_SLOTS(1);
+ tegra_dc_writel(dc, value, DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER);
+
+ tegra_dc_writel(dc, COMMON_UPDATE, DC_CMD_STATE_CONTROL);
+ tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+ tegra_dc_writel(dc, COMMON_ACTREQ, DC_CMD_STATE_CONTROL);
+ tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
+
+ pm_runtime_put(dc->dev);
+}
+
+void tegra_display_hub_atomic_commit(struct drm_device *drm,
+ struct drm_atomic_state *state)
+{
+ struct tegra_atomic_state *s = to_tegra_atomic_state(state);
+ struct tegra_drm *tegra = drm->dev_private;
+ struct tegra_display_hub *hub = tegra->hub;
+ struct device *dev = hub->client.dev;
+ int err;
+
+ if (s->clk_disp) {
+ err = clk_set_rate(s->clk_disp, s->rate);
+ if (err < 0)
+ dev_err(dev, "failed to set rate of %pC to %lu Hz\n",
+ s->clk_disp, s->rate);
+
+ err = clk_set_parent(hub->clk_disp, s->clk_disp);
+ if (err < 0)
+ dev_err(dev, "failed to set parent of %pC to %pC: %d\n",
+ hub->clk_disp, s->clk_disp, err);
+ }
+
+ if (s->dc)
+ tegra_display_hub_update(s->dc);
+}
+
+static int tegra_display_hub_init(struct host1x_client *client)
+{
+ struct tegra_display_hub *hub = to_tegra_display_hub(client);
+ struct drm_device *drm = dev_get_drvdata(client->parent);
+ struct tegra_drm *tegra = drm->dev_private;
+
+ tegra->hub = hub;
+
+ return 0;
+}
+
+static int tegra_display_hub_exit(struct host1x_client *client)
+{
+ struct drm_device *drm = dev_get_drvdata(client->parent);
+ struct tegra_drm *tegra = drm->dev_private;
+
+ tegra->hub = NULL;
+
+ return 0;
+}
+
+static const struct host1x_client_ops tegra_display_hub_ops = {
+ .init = tegra_display_hub_init,
+ .exit = tegra_display_hub_exit,
+};
+
+static int tegra_display_hub_probe(struct platform_device *pdev)
+{
+ struct tegra_display_hub *hub;
+ unsigned int i;
+ int err;
+
+ hub = devm_kzalloc(&pdev->dev, sizeof(*hub), GFP_KERNEL);
+ if (!hub)
+ return -ENOMEM;
+
+ hub->soc = of_device_get_match_data(&pdev->dev);
+
+ hub->clk_disp = devm_clk_get(&pdev->dev, "disp");
+ if (IS_ERR(hub->clk_disp)) {
+ err = PTR_ERR(hub->clk_disp);
+ return err;
+ }
+
+ hub->clk_dsc = devm_clk_get(&pdev->dev, "dsc");
+ if (IS_ERR(hub->clk_dsc)) {
+ err = PTR_ERR(hub->clk_dsc);
+ return err;
+ }
+
+ hub->clk_hub = devm_clk_get(&pdev->dev, "hub");
+ if (IS_ERR(hub->clk_hub)) {
+ err = PTR_ERR(hub->clk_hub);
+ return err;
+ }
+
+ hub->rst = devm_reset_control_get(&pdev->dev, "misc");
+ if (IS_ERR(hub->rst)) {
+ err = PTR_ERR(hub->rst);
+ return err;
+ }
+
+ hub->wgrps = devm_kcalloc(&pdev->dev, hub->soc->num_wgrps,
+ sizeof(*hub->wgrps), GFP_KERNEL);
+ if (!hub->wgrps)
+ return -ENOMEM;
+
+ for (i = 0; i < hub->soc->num_wgrps; i++) {
+ struct tegra_windowgroup *wgrp = &hub->wgrps[i];
+ char id[8];
+
+ snprintf(id, sizeof(id), "wgrp%u", i);
+ mutex_init(&wgrp->lock);
+ wgrp->usecount = 0;
+ wgrp->index = i;
+
+ wgrp->rst = devm_reset_control_get(&pdev->dev, id);
+ if (IS_ERR(wgrp->rst))
+ return PTR_ERR(wgrp->rst);
+
+ err = reset_control_assert(wgrp->rst);
+ if (err < 0)
+ return err;
+ }
+
+ /* XXX: enable clock across reset? */
+ err = reset_control_assert(hub->rst);
+ if (err < 0)
+ return err;
+
+ platform_set_drvdata(pdev, hub);
+ pm_runtime_enable(&pdev->dev);
+
+ INIT_LIST_HEAD(&hub->client.list);
+ hub->client.ops = &tegra_display_hub_ops;
+ hub->client.dev = &pdev->dev;
+
+ err = host1x_client_register(&hub->client);
+ if (err < 0)
+ dev_err(&pdev->dev, "failed to register host1x client: %d\n",
+ err);
+
+ return err;
+}
+
+static int tegra_display_hub_remove(struct platform_device *pdev)
+{
+ struct tegra_display_hub *hub = platform_get_drvdata(pdev);
+ int err;
+
+ err = host1x_client_unregister(&hub->client);
+ if (err < 0) {
+ dev_err(&pdev->dev, "failed to unregister host1x client: %d\n",
+ err);
+ }
+
+ pm_runtime_disable(&pdev->dev);
+
+ return err;
+}
+
+static int tegra_display_hub_suspend(struct device *dev)
+{
+ struct tegra_display_hub *hub = dev_get_drvdata(dev);
+ int err;
+
+ err = reset_control_assert(hub->rst);
+ if (err < 0)
+ return err;
+
+ clk_disable_unprepare(hub->clk_hub);
+ clk_disable_unprepare(hub->clk_dsc);
+ clk_disable_unprepare(hub->clk_disp);
+
+ return 0;
+}
+
+static int tegra_display_hub_resume(struct device *dev)
+{
+ struct tegra_display_hub *hub = dev_get_drvdata(dev);
+ int err;
+
+ err = clk_prepare_enable(hub->clk_disp);
+ if (err < 0)
+ return err;
+
+ err = clk_prepare_enable(hub->clk_dsc);
+ if (err < 0)
+ goto disable_disp;
+
+ err = clk_prepare_enable(hub->clk_hub);
+ if (err < 0)
+ goto disable_dsc;
+
+ err = reset_control_deassert(hub->rst);
+ if (err < 0)
+ goto disable_hub;
+
+ return 0;
+
+disable_hub:
+ clk_disable_unprepare(hub->clk_hub);
+disable_dsc:
+ clk_disable_unprepare(hub->clk_dsc);
+disable_disp:
+ clk_disable_unprepare(hub->clk_disp);
+ return err;
+}
+
+static const struct dev_pm_ops tegra_display_hub_pm_ops = {
+ SET_RUNTIME_PM_OPS(tegra_display_hub_suspend,
+ tegra_display_hub_resume, NULL)
+};
+
+static const struct tegra_display_hub_soc tegra186_display_hub = {
+ .num_wgrps = 6,
+};
+
+static const struct of_device_id tegra_display_hub_of_match[] = {
+ {
+ .compatible = "nvidia,tegra186-display",
+ .data = &tegra186_display_hub
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, tegra_display_hub_of_match);
+
+struct platform_driver tegra_display_hub_driver = {
+ .driver = {
+ .name = "tegra-display-hub",
+ .of_match_table = tegra_display_hub_of_match,
+ .pm = &tegra_display_hub_pm_ops,
+ },
+ .probe = tegra_display_hub_probe,
+ .remove = tegra_display_hub_remove,
+};
diff --git a/drivers/gpu/drm/tegra/hub.h b/drivers/gpu/drm/tegra/hub.h
new file mode 100644
index 000000000000..965c333736b0
--- /dev/null
+++ b/drivers/gpu/drm/tegra/hub.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef TEGRA_HUB_H
+#define TEGRA_HUB_H 1
+
+#include <drm/drmP.h>
+#include <drm/drm_plane.h>
+
+#include "plane.h"
+
+struct tegra_dc;
+
+struct tegra_windowgroup {
+ unsigned int usecount;
+ struct mutex lock;
+
+ unsigned int index;
+ struct device *parent;
+ struct reset_control *rst;
+};
+
+struct tegra_shared_plane {
+ struct tegra_plane base;
+ struct tegra_windowgroup *wgrp;
+ struct tegra_dc *dc;
+};
+
+static inline struct tegra_shared_plane *
+to_tegra_shared_plane(struct drm_plane *plane)
+{
+ return container_of(plane, struct tegra_shared_plane, base.base);
+}
+
+struct tegra_display_hub_soc {
+ unsigned int num_wgrps;
+};
+
+struct tegra_display_hub {
+ struct host1x_client client;
+ struct clk *clk_disp;
+ struct clk *clk_dsc;
+ struct clk *clk_hub;
+ struct reset_control *rst;
+
+ const struct tegra_display_hub_soc *soc;
+ struct tegra_windowgroup *wgrps;
+};
+
+static inline struct tegra_display_hub *
+to_tegra_display_hub(struct host1x_client *client)
+{
+ return container_of(client, struct tegra_display_hub, client);
+}
+
+struct tegra_dc;
+struct tegra_plane;
+
+int tegra_display_hub_prepare(struct tegra_display_hub *hub);
+void tegra_display_hub_cleanup(struct tegra_display_hub *hub);
+
+struct drm_plane *tegra_shared_plane_create(struct drm_device *drm,
+ struct tegra_dc *dc,
+ unsigned int wgrp,
+ unsigned int index);
+
+void tegra_display_hub_atomic_commit(struct drm_device *drm,
+ struct drm_atomic_state *state);
+
+#define DC_CMD_IHUB_COMMON_MISC_CTL 0x068
+#define LATENCY_EVENT (1 << 3)
+
+#define DC_DISP_IHUB_COMMON_DISPLAY_FETCH_METER 0x451
+#define CURS_SLOTS(x) (((x) & 0xff) << 8)
+#define WGRP_SLOTS(x) (((x) & 0xff) << 0)
+
+#endif /* TEGRA_HUB_H */
diff --git a/drivers/gpu/drm/tegra/plane.h b/drivers/gpu/drm/tegra/plane.h
index 8237b885acd7..fc7566f630fa 100644
--- a/drivers/gpu/drm/tegra/plane.h
+++ b/drivers/gpu/drm/tegra/plane.h
@@ -15,7 +15,9 @@ struct tegra_bo;
struct tegra_plane {
struct drm_plane base;
+ unsigned int offset;
unsigned int index;
+ unsigned int depth;
};
struct tegra_cursor {