Commit c4755fb9 authored by Thierry Reding's avatar Thierry Reding

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: default avatarThierry Reding <treding@nvidia.com>
parent 5acd3514
...@@ -5,6 +5,7 @@ tegra-drm-y := \ ...@@ -5,6 +5,7 @@ tegra-drm-y := \
drm.o \ drm.o \
gem.o \ gem.o \
fb.o \ fb.o \
hub.o \
plane.o \ plane.o \
dc.o \ dc.o \
output.o \ output.o \
......
...@@ -447,6 +447,15 @@ static struct drm_plane *tegra_dc_primary_plane_create(struct drm_device *drm, ...@@ -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); num_formats = ARRAY_SIZE(tegra_primary_plane_formats);
formats = 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, err = drm_universal_plane_init(drm, &plane->base, possible_crtcs,
&tegra_plane_funcs, formats, &tegra_plane_funcs, formats,
num_formats, NULL, num_formats, NULL,
...@@ -641,7 +650,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm, ...@@ -641,7 +650,10 @@ static struct drm_plane *tegra_dc_overlay_plane_create(struct drm_device *drm,
if (!plane) if (!plane)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
/* XXX compute offset so that we can directly access windows */
plane->offset = 0;
plane->index = index; plane->index = index;
plane->depth = 0;
num_formats = ARRAY_SIZE(tegra_overlay_plane_formats); num_formats = ARRAY_SIZE(tegra_overlay_plane_formats);
formats = tegra_overlay_plane_formats; formats = tegra_overlay_plane_formats;
...@@ -1382,6 +1394,25 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc, ...@@ -1382,6 +1394,25 @@ static void tegra_crtc_atomic_enable(struct drm_crtc *crtc,
static int tegra_crtc_atomic_check(struct drm_crtc *crtc, static int tegra_crtc_atomic_check(struct drm_crtc *crtc,
struct drm_crtc_state *state) 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; return 0;
} }
......
...@@ -209,6 +209,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); ...@@ -209,6 +209,8 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define WIN_B_UPDATE (1 << 10) #define WIN_B_UPDATE (1 << 10)
#define WIN_C_UPDATE (1 << 11) #define WIN_C_UPDATE (1 << 11)
#define CURSOR_UPDATE (1 << 15) #define CURSOR_UPDATE (1 << 15)
#define COMMON_ACTREQ (1 << 16)
#define COMMON_UPDATE (1 << 17)
#define NC_HOST_TRIG (1 << 24) #define NC_HOST_TRIG (1 << 24)
#define DC_CMD_DISPLAY_WINDOW_HEADER 0x042 #define DC_CMD_DISPLAY_WINDOW_HEADER 0x042
...@@ -486,6 +488,35 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); ...@@ -486,6 +488,35 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define CURSOR_SRC_BLEND_MASK (3 << 8) #define CURSOR_SRC_BLEND_MASK (3 << 8)
#define CURSOR_ALPHA 0xff #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_YOF 0x611
#define DC_WIN_CSC_KYRGB 0x612 #define DC_WIN_CSC_KYRGB 0x612
#define DC_WIN_CSC_KUR 0x613 #define DC_WIN_CSC_KUR 0x613
...@@ -596,8 +627,91 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc); ...@@ -596,8 +627,91 @@ int tegra_dc_rgb_exit(struct tegra_dc *dc);
#define DC_WINBUF_START_ADDR_HI 0x80d #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_AD_UFLOW_STATUS 0xbca
#define DC_WINBUF_BD_UFLOW_STATUS 0xdca #define DC_WINBUF_BD_UFLOW_STATUS 0xdca
#define DC_WINBUF_CD_UFLOW_STATUS 0xfca #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 */ #endif /* TEGRA_DC_H */
...@@ -33,6 +33,35 @@ struct tegra_drm_file { ...@@ -33,6 +33,35 @@ struct tegra_drm_file {
struct mutex lock; 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 = { static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
.fb_create = tegra_fb_create, .fb_create = tegra_fb_create,
#ifdef CONFIG_DRM_FBDEV_EMULATION #ifdef CONFIG_DRM_FBDEV_EMULATION
...@@ -40,11 +69,32 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = { ...@@ -40,11 +69,32 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
#endif #endif
.atomic_check = drm_atomic_helper_check, .atomic_check = drm_atomic_helper_check,
.atomic_commit = drm_atomic_helper_commit, .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 static const struct drm_mode_config_helper_funcs
tegra_drm_mode_config_helpers = { 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) 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) ...@@ -119,6 +169,12 @@ static int tegra_drm_load(struct drm_device *drm, unsigned long flags)
if (err < 0) if (err < 0)
goto fbdev; 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 * 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 * 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) ...@@ -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); err = drm_vblank_init(drm, drm->mode_config.num_crtc);
if (err < 0) if (err < 0)
goto device; goto hub;
drm_mode_config_reset(drm); drm_mode_config_reset(drm);
err = tegra_drm_fb_init(drm); err = tegra_drm_fb_init(drm);
if (err < 0) if (err < 0)
goto device; goto hub;
return 0; return 0;
hub:
if (tegra->hub)
tegra_display_hub_cleanup(tegra->hub);
device: device:
host1x_device_exit(device); host1x_device_exit(device);
fbdev: fbdev:
...@@ -1235,6 +1294,7 @@ static const struct of_device_id host1x_drm_subdevs[] = { ...@@ -1235,6 +1294,7 @@ static const struct of_device_id host1x_drm_subdevs[] = {
{ .compatible = "nvidia,tegra210-sor", }, { .compatible = "nvidia,tegra210-sor", },
{ .compatible = "nvidia,tegra210-sor1", }, { .compatible = "nvidia,tegra210-sor1", },
{ .compatible = "nvidia,tegra210-vic", }, { .compatible = "nvidia,tegra210-vic", },
{ .compatible = "nvidia,tegra186-display", },
{ .compatible = "nvidia,tegra186-vic", }, { .compatible = "nvidia,tegra186-vic", },
{ /* sentinel */ } { /* sentinel */ }
}; };
...@@ -1250,6 +1310,7 @@ static struct host1x_driver host1x_drm_driver = { ...@@ -1250,6 +1310,7 @@ static struct host1x_driver host1x_drm_driver = {
}; };
static struct platform_driver * const drivers[] = { static struct platform_driver * const drivers[] = {
&tegra_display_hub_driver,
&tegra_dc_driver, &tegra_dc_driver,
&tegra_hdmi_driver, &tegra_hdmi_driver,
&tegra_dsi_driver, &tegra_dsi_driver,
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <drm/drmP.h> #include <drm/drmP.h>
#include <drm/drm_atomic.h>
#include <drm/drm_crtc_helper.h> #include <drm/drm_crtc_helper.h>
#include <drm/drm_edid.h> #include <drm/drm_edid.h>
#include <drm/drm_encoder.h> #include <drm/drm_encoder.h>
...@@ -23,6 +24,7 @@ ...@@ -23,6 +24,7 @@
#include <drm/drm_fixed.h> #include <drm/drm_fixed.h>
#include "gem.h" #include "gem.h"
#include "hub.h"
#include "trace.h" #include "trace.h"
struct reset_control; struct reset_control;
...@@ -40,6 +42,20 @@ struct tegra_fbdev { ...@@ -40,6 +42,20 @@ struct tegra_fbdev {
}; };
#endif #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 tegra_drm {
struct drm_device *drm; struct drm_device *drm;
...@@ -62,6 +78,8 @@ struct tegra_drm { ...@@ -62,6 +78,8 @@ struct tegra_drm {
unsigned int pitch_align; unsigned int pitch_align;
struct tegra_display_hub *hub;
struct drm_atomic_state *state; struct drm_atomic_state *state;
}; };
...@@ -187,6 +205,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev); ...@@ -187,6 +205,7 @@ void tegra_fbdev_restore_mode(struct tegra_fbdev *fbdev);
void tegra_fb_output_poll_changed(struct drm_device *drm); void tegra_fb_output_poll_changed(struct drm_device *drm);
#endif #endif
extern struct platform_driver tegra_display_hub_driver;
extern struct platform_driver tegra_dc_driver; extern struct platform_driver tegra_dc_driver;
extern struct platform_driver tegra_hdmi_driver; extern struct platform_driver tegra_hdmi_driver;
extern struct platform_driver tegra_dsi_driver; extern struct platform_driver tegra_dsi_driver;
......
This diff is collapsed.
/*
* 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 */
...@@ -15,7 +15,9 @@ struct tegra_bo; ...@@ -15,7 +15,9 @@ struct tegra_bo;
struct tegra_plane { struct tegra_plane {
struct drm_plane base; struct drm_plane base;
unsigned int offset;
unsigned int index; unsigned int index;
unsigned int depth;
}; };
struct tegra_cursor { struct tegra_cursor {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment