Commit 04d5d5df authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding

drm/tegra: dc: Support memory bandwidth management

Display controller (DC) performs isochronous memory transfers, and thus,
has a requirement for a minimum memory bandwidth that shall be fulfilled,
otherwise framebuffer data can't be fetched fast enough and this results
in a DC's data-FIFO underflow that follows by a visual corruption.

The Memory Controller drivers provide facility for memory bandwidth
management via interconnect API. Let's wire up the interconnect API
support to the DC driver in order to fix the distorted display output
on T30 Ouya, T124 TK1 and other Tegra devices.

Tested-by: Peter Geis <pgwipeout@gmail.com> # Ouya T30
Tested-by: Matt Merhar <mattmerhar@protonmail.com> # Ouya T30
Tested-by: Nicolas Chauvet <kwizart@gmail.com> # PAZ00 T20 and TK1 T124
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
[treding@nvidia.com: unbreak Tegra186+ display support]
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent ef531d01
......@@ -9,6 +9,7 @@ config DRM_TEGRA
select DRM_MIPI_DSI
select DRM_PANEL
select TEGRA_HOST1X
select INTERCONNECT
select IOMMU_IOVA
select CEC_CORE if CEC_NOTIFIER
help
......
This diff is collapsed.
......@@ -15,6 +15,8 @@
struct tegra_output;
#define TEGRA_DC_LEGACY_PLANES_NUM 7
struct tegra_dc_state {
struct drm_crtc_state base;
......@@ -33,6 +35,12 @@ static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
return NULL;
}
static inline const struct tegra_dc_state *
to_const_dc_state(const struct drm_crtc_state *state)
{
return to_dc_state((struct drm_crtc_state *)state);
}
struct tegra_dc_stats {
unsigned long frames;
unsigned long vblank;
......@@ -66,7 +74,9 @@ struct tegra_dc_soc_info {
unsigned int num_overlay_formats;
const u64 *modifiers;
bool has_win_a_without_filters;
bool has_win_b_vfilter_mem_client;
bool has_win_c_without_vert_filter;
bool plane_tiled_memory_bandwidth_x2;
};
struct tegra_dc {
......@@ -152,6 +162,8 @@ int tegra_dc_state_setup_clock(struct tegra_dc *dc,
struct drm_crtc_state *crtc_state,
struct clk *clk, unsigned long pclk,
unsigned int div);
void tegra_crtc_atomic_post_commit(struct drm_crtc *crtc,
struct drm_atomic_state *state);
/* from rgb.c */
int tegra_dc_rgb_probe(struct tegra_dc *dc);
......
......@@ -21,9 +21,10 @@
#include <drm/drm_prime.h>
#include <drm/drm_vblank.h>
#include "uapi.h"
#include "dc.h"
#include "drm.h"
#include "gem.h"
#include "uapi.h"
#define DRIVER_NAME "tegra"
#define DRIVER_DESC "NVIDIA Tegra graphics"
......@@ -56,6 +57,17 @@ static const struct drm_mode_config_funcs tegra_drm_mode_config_funcs = {
.atomic_commit = drm_atomic_helper_commit,
};
static void tegra_atomic_post_commit(struct drm_device *drm,
struct drm_atomic_state *old_state)
{
struct drm_crtc_state *old_crtc_state __maybe_unused;
struct drm_crtc *crtc;
unsigned int i;
for_each_old_crtc_in_state(old_state, crtc, old_crtc_state, i)
tegra_crtc_atomic_post_commit(crtc, old_state);
}
static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state)
{
struct drm_device *drm = old_state->dev;
......@@ -75,6 +87,8 @@ static void tegra_atomic_commit_tail(struct drm_atomic_state *old_state)
} else {
drm_atomic_helper_commit_tail_rpm(old_state);
}
tegra_atomic_post_commit(drm, old_state);
}
static const struct drm_mode_config_helper_funcs
......
......@@ -4,6 +4,7 @@
*/
#include <linux/iommu.h>
#include <linux/interconnect.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
......@@ -64,6 +65,9 @@ tegra_plane_atomic_duplicate_state(struct drm_plane *plane)
copy->reflect_x = state->reflect_x;
copy->reflect_y = state->reflect_y;
copy->opaque = state->opaque;
copy->total_peak_memory_bandwidth = state->total_peak_memory_bandwidth;
copy->peak_memory_bandwidth = state->peak_memory_bandwidth;
copy->avg_memory_bandwidth = state->avg_memory_bandwidth;
for (i = 0; i < 2; i++)
copy->blending[i] = state->blending[i];
......@@ -244,6 +248,78 @@ void tegra_plane_cleanup_fb(struct drm_plane *plane,
tegra_dc_unpin(dc, to_tegra_plane_state(state));
}
static int tegra_plane_calculate_memory_bandwidth(struct drm_plane_state *state)
{
struct tegra_plane_state *tegra_state = to_tegra_plane_state(state);
unsigned int i, bpp, dst_w, dst_h, src_w, src_h, mul;
const struct tegra_dc_soc_info *soc;
const struct drm_format_info *fmt;
struct drm_crtc_state *crtc_state;
u64 avg_bandwidth, peak_bandwidth;
if (!state->visible)
return 0;
crtc_state = drm_atomic_get_new_crtc_state(state->state, state->crtc);
if (!crtc_state)
return -EINVAL;
src_w = drm_rect_width(&state->src) >> 16;
src_h = drm_rect_height(&state->src) >> 16;
dst_w = drm_rect_width(&state->dst);
dst_h = drm_rect_height(&state->dst);
fmt = state->fb->format;
soc = to_tegra_dc(state->crtc)->soc;
/*
* Note that real memory bandwidth vary depending on format and
* memory layout, we are not taking that into account because small
* estimation error isn't important since bandwidth is rounded up
* anyway.
*/
for (i = 0, bpp = 0; i < fmt->num_planes; i++) {
unsigned int bpp_plane = fmt->cpp[i] * 8;
/*
* Sub-sampling is relevant for chroma planes only and vertical
* readouts are not cached, hence only horizontal sub-sampling
* matters.
*/
if (i > 0)
bpp_plane /= fmt->hsub;
bpp += bpp_plane;
}
/* average bandwidth in kbytes/sec */
avg_bandwidth = min(src_w, dst_w) * min(src_h, dst_h);
avg_bandwidth *= drm_mode_vrefresh(&crtc_state->adjusted_mode);
avg_bandwidth = DIV_ROUND_UP(avg_bandwidth * bpp, 8) + 999;
do_div(avg_bandwidth, 1000);
/* mode.clock in kHz, peak bandwidth in kbytes/sec */
peak_bandwidth = DIV_ROUND_UP(crtc_state->adjusted_mode.clock * bpp, 8);
/*
* Tegra30/114 Memory Controller can't interleave DC memory requests
* for the tiled windows because DC uses 16-bytes atom, while DDR3
* uses 32-bytes atom. Hence there is x2 memory overfetch for tiled
* framebuffer and DDR3 on these SoCs.
*/
if (soc->plane_tiled_memory_bandwidth_x2 &&
tegra_state->tiling.mode == TEGRA_BO_TILING_MODE_TILED)
mul = 2;
else
mul = 1;
/* ICC bandwidth in kbytes/sec */
tegra_state->peak_memory_bandwidth = kBps_to_icc(peak_bandwidth) * mul;
tegra_state->avg_memory_bandwidth = kBps_to_icc(avg_bandwidth) * mul;
return 0;
}
int tegra_plane_state_add(struct tegra_plane *plane,
struct drm_plane_state *state)
{
......@@ -262,6 +338,10 @@ int tegra_plane_state_add(struct tegra_plane *plane,
if (err < 0)
return err;
err = tegra_plane_calculate_memory_bandwidth(state);
if (err < 0)
return err;
tegra = to_dc_state(crtc_state);
tegra->planes |= WIN_A_ACT_REQ << plane->index;
......@@ -646,3 +726,40 @@ int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
return 0;
}
static const char * const tegra_plane_icc_names[TEGRA_DC_LEGACY_PLANES_NUM] = {
"wina", "winb", "winc", NULL, NULL, NULL, "cursor",
};
int tegra_plane_interconnect_init(struct tegra_plane *plane)
{
const char *icc_name = tegra_plane_icc_names[plane->index];
struct device *dev = plane->dc->dev;
struct tegra_dc *dc = plane->dc;
int err;
if (WARN_ON(plane->index >= TEGRA_DC_LEGACY_PLANES_NUM) ||
WARN_ON(!tegra_plane_icc_names[plane->index]))
return -EINVAL;
plane->icc_mem = devm_of_icc_get(dev, icc_name);
err = PTR_ERR_OR_ZERO(plane->icc_mem);
if (err) {
dev_err_probe(dev, err, "failed to get %s interconnect\n",
icc_name);
return err;
}
/* plane B on T20/30 has a dedicated memory client for a 6-tap vertical filter */
if (plane->index == 1 && dc->soc->has_win_b_vfilter_mem_client) {
plane->icc_mem_vfilter = devm_of_icc_get(dev, "winb-vfilter");
err = PTR_ERR_OR_ZERO(plane->icc_mem_vfilter);
if (err) {
dev_err_probe(dev, err, "failed to get %s interconnect\n",
"winb-vfilter");
return err;
}
}
return 0;
}
......@@ -8,6 +8,7 @@
#include <drm/drm_plane.h>
struct icc_path;
struct tegra_bo;
struct tegra_dc;
......@@ -16,6 +17,9 @@ struct tegra_plane {
struct tegra_dc *dc;
unsigned int offset;
unsigned int index;
struct icc_path *icc_mem;
struct icc_path *icc_mem_vfilter;
};
struct tegra_cursor {
......@@ -52,6 +56,11 @@ struct tegra_plane_state {
/* used for legacy blending support only */
struct tegra_plane_legacy_blending_state blending[2];
bool opaque;
/* bandwidths are in ICC units, i.e. kbytes/sec */
u32 total_peak_memory_bandwidth;
u32 peak_memory_bandwidth;
u32 avg_memory_bandwidth;
};
static inline struct tegra_plane_state *
......@@ -63,6 +72,12 @@ to_tegra_plane_state(struct drm_plane_state *state)
return NULL;
}
static inline const struct tegra_plane_state *
to_const_tegra_plane_state(const struct drm_plane_state *state)
{
return to_tegra_plane_state((struct drm_plane_state *)state);
}
extern const struct drm_plane_funcs tegra_plane_funcs;
int tegra_plane_prepare_fb(struct drm_plane *plane,
......@@ -78,5 +93,6 @@ bool tegra_plane_format_is_indexed(unsigned int format);
bool tegra_plane_format_is_yuv(unsigned int format, bool *planar, unsigned int *bpc);
int tegra_plane_setup_legacy_state(struct tegra_plane *tegra,
struct tegra_plane_state *state);
int tegra_plane_interconnect_init(struct tegra_plane *plane);
#endif /* TEGRA_PLANE_H */
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