Skip to contentSkip to conversation

Add memory bandwidth management to NVIDIA Tegra DRM driver

PATCH Superseded Dmitry Osipenko 2 participants 5 messages May 10, 2021 – May 31, 2021

Subsystems (3)

v17 of 18 Superseded Latest is v18 →

lore
Share & tools
lore
#0 lore raw

v17 cover Add memory bandwidth management to NVIDIA Tegra DRM driver

This series adds memory bandwidth management to the NVIDIA Tegra DRM driver, which is done using interconnect framework. It fixes display corruption that happens due to insufficient memory bandwidth.

Changelog:

v17: - No changes, re-sending for v5.14.

v16: - Implemented suggestions that were given by Michał Mirosław to v15.

- Added r-b from Michał Mirosław to the debug-stats patch.

- Rebased on top of a recent linux-next.

- Removed bandwidth scaling based on width difference of src/dst windows since it's not actual anymore. Apparently the recent memory driver changes fixed problems that I witnessed before.

- Average bandwidth calculation now won't overflow for 4k resolutions.

- Average bandwidth calculation now uses the size of the visible area instead of the src area since debug stats of the memory controller clearly show that downscaled window takes less bandwidth, proportionally to the scaled size.

- Bandwidth calculation now uses "adjusted mode" of the CRTC, which is what used for h/w programming, instead of the mode that was requested by userspace, although the two usually match in practice.

v15: - Corrected tegra_plane_icc_names[] NULL-check that was partially lost
by accident in v14 after unsuccessful rebase.

v14: - Made improvements that were suggested by Michał Mirosław to v13:

- Changed 'unsigned int' to 'bool'.
- Renamed functions which calculate bandwidth state.
- Reworked comment in the code that explains why downscaled plane require higher bandwidth.
- Added round-up to bandwidth calculation.
- Added sanity checks of the plane index and fixed out-of-bounds access which happened on T124 due to the cursor plane index.

v13: - No code changes. Patches missed v5.12, re-sending them for v5.13.

Dmitry Osipenko (2):
drm/tegra: dc: Support memory bandwidth management drm/tegra: dc: Extend debug stats with total number of events

 drivers/gpu/drm/tegra/Kconfig |   1 +
 drivers/gpu/drm/tegra/dc.c    | 362 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/dc.h    |  19 ++
 drivers/gpu/drm/tegra/drm.c   |  14 ++
 drivers/gpu/drm/tegra/hub.c   |   3 +
 drivers/gpu/drm/tegra/plane.c | 116 +++++++++++
 drivers/gpu/drm/tegra/plane.h |  15 ++
 7 files changed, 530 insertions(+)
Signature · 1 line
2.30.2
#1 lore raw

PATCH v17 1/2 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.

---
 drivers/gpu/drm/tegra/Kconfig |   1 +
 drivers/gpu/drm/tegra/dc.c    | 352 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/dc.h    |  14 ++
 drivers/gpu/drm/tegra/drm.c   |  14 ++
 drivers/gpu/drm/tegra/hub.c   |   3 +
 drivers/gpu/drm/tegra/plane.c | 116 +++++++++++
 drivers/gpu/drm/tegra/plane.h |  15 ++
 7 files changed, 515 insertions(+)
drivers/gpu/drm/tegra/Kconfig +1 -0
index 5043dcaf1cf9..1650a448eabd 100644
@@ -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
drivers/gpu/drm/tegra/dc.c +352 -0

469 lines not on this page · raw message

drivers/gpu/drm/tegra/dc.h +14 -0
index 29f19c3c6149..db10af097033 100644
@@ -15,6 +15,8 @@
 
 struct tegra_output;
 
+#define TEGRA_DC_LEGACY_PLANES_NUM	7
+
 struct tegra_dc_state {
 	struct drm_crtc_state base;
 
@@ -23,6 +25,8 @@ struct tegra_dc_state {
 	unsigned int div;
 
 	u32 planes;
+
+	unsigned long plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM];
 };
 
 static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -33,6 +37,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 +76,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 +164,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);
drivers/gpu/drm/tegra/drm.c +14 -0
index d76bcb6c5622..a10513d7f2e0 100644
@@ -24,6 +24,7 @@
 #include <asm/dma-iommu.h>
 #endif
 
+#include "dc.h"
 #include "drm.h"
 #include "gem.h"
 
@@ -63,6 +64,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;
@@ -82,6 +94,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
drivers/gpu/drm/tegra/hub.c +3 -0
index bfae8a02f55b..f1bbc5991854 100644
@@ -358,6 +358,9 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
 	int err;
 
+	plane_state->peak_memory_bandwidth = 0;
+	plane_state->avg_memory_bandwidth = 0;
+
 	/* no need for further checks if the plane is being disabled */
 	if (!new_plane_state->crtc || !new_plane_state->fb)
 		return 0;
drivers/gpu/drm/tegra/plane.c +116 -0

143 lines not on this page · raw message

drivers/gpu/drm/tegra/plane.h +15 -0
index c691dd79b27b..f2731aae7d01 100644
@@ -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,10 @@ 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 peak_memory_bandwidth;
+	u32 avg_memory_bandwidth;
 };
 
 static inline struct tegra_plane_state *
@@ -63,6 +71,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,
@@ -77,5 +91,6 @@ int tegra_plane_format(u32 fourcc, u32 *format, u32 *swap);
 bool tegra_plane_format_is_yuv(unsigned int format, bool *planar);
 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 */
Signature · 1 line
2.30.2
#2 lore raw

Re: [PATCH v17 1/2] drm/tegra: dc: Support memory bandwidth management

Quoted 24 lines · Dmitry Osipenko

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.

---
 drivers/gpu/drm/tegra/Kconfig |   1 +
 drivers/gpu/drm/tegra/dc.c    | 352 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/dc.h    |  14 ++
 drivers/gpu/drm/tegra/drm.c   |  14 ++
 drivers/gpu/drm/tegra/hub.c   |   3 +
 drivers/gpu/drm/tegra/plane.c | 116 +++++++++++
 drivers/gpu/drm/tegra/plane.h |  15 ++
 7 files changed, 515 insertions(+)

[...]

Quoted 1 line
drivers/gpu/drm/tegra/dc.c +0 -0

[...]

Quoted 9 lines
drivers/gpu/drm/tegra/dc.c +5 -0
@@ -2011,7 +2143,215 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
 }
 
+static bool tegra_plane_is_cursor(const struct drm_plane_state *state)
+{
+	const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc;
+	const struct drm_format_info *fmt = state->fb->format;
+	unsigned int src_w = drm_rect_width(&state->src) >> 16;

drivers/gpu/drm/tegra/dc.c static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, · line 2160

Quoted 10 lines
drivers/gpu/drm/tegra/dc.c +10 -0
+	unsigned int dst_w = drm_rect_width(&state->dst);
+
+	if (state->plane->type != DRM_PLANE_TYPE_CURSOR)
+		return false;
+
+	if (soc->supports_cursor)
+		return true;
+
+	if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256)
+		return false;

Technically there could be some random overlay window that matches these conditions and is erroneously detected as being a cursor. I wonder if we should add a field to a plane that marks it as being used as cursor for the cases where we don't support a hardware cursor.

[...]

Quoted 14 lines
drivers/gpu/drm/tegra/dc.h +2 -0
index 29f19c3c6149..db10af097033 100644
@@ -15,6 +15,8 @@
 
 struct tegra_output;
 
+#define TEGRA_DC_LEGACY_PLANES_NUM	7
+
 struct tegra_dc_state {
 	struct drm_crtc_state base;
 

drivers/gpu/drm/tegra/dc.h struct tegra_dc_state { · line 29

Quoted 5 lines
drivers/gpu/drm/tegra/dc.h +2 -0
@@ -23,6 +25,8 @@ struct tegra_dc_state {
 	unsigned int div;
 
 	u32 planes;
+
+	unsigned long plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM];

Why can we not store this peak bandwidth value within the plane state? I know that this isn't exactly per-plane data because it depends on the state of other planes, but that doesn't really prevent the value to live within the plane state. The plane state is, after all, part of the global state, and hence any such state needs to be considered within the context of that global atomic state.

I suppose that might make it a little bit more difficult to get at the data, but I think the end result would be less confusing than having an array here with potentially unused fields. It would also get rid of the need to look up planes by their per-CRTC index.

Quoted 79 lines
drivers/gpu/drm/tegra/dc.h +10 -0
 };
 
 static inline struct tegra_dc_state *to_dc_state(struct drm_crtc_state *state)
@@ -33,6 +37,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 +76,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 +164,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);
drivers/gpu/drm/tegra/drm.c +14 -0
index d76bcb6c5622..a10513d7f2e0 100644
@@ -24,6 +24,7 @@
 #include <asm/dma-iommu.h>
 #endif
 
+#include "dc.h"
 #include "drm.h"
 #include "gem.h"
 
@@ -63,6 +64,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;
@@ -82,6 +94,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
drivers/gpu/drm/tegra/hub.c +0 -0
index bfae8a02f55b..f1bbc5991854 100644

drivers/gpu/drm/tegra/hub.c static int tegra_shared_plane_atomic_check(struct drm_plane *plane, · line 363

Quoted 6 lines
drivers/gpu/drm/tegra/hub.c +3 -0
@@ -358,6 +358,9 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
 	int err;
 
+	plane_state->peak_memory_bandwidth = 0;
+	plane_state->avg_memory_bandwidth = 0;
+

Since ICC isn't supported yet on Tegra186 and later, does it even make sense to initialize these?

Thierry

#3 lore raw

Re: [PATCH v17 1/2] drm/tegra: dc: Support memory bandwidth management

31.05.2021 17:13, Thierry Reding пишет:

Quoted 42 lines · Dmitry Osipenko
Quoted 24 lines

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.

---
 drivers/gpu/drm/tegra/Kconfig |   1 +
 drivers/gpu/drm/tegra/dc.c    | 352 ++++++++++++++++++++++++++++++++++
 drivers/gpu/drm/tegra/dc.h    |  14 ++
 drivers/gpu/drm/tegra/drm.c   |  14 ++
 drivers/gpu/drm/tegra/hub.c   |   3 +
 drivers/gpu/drm/tegra/plane.c | 116 +++++++++++
 drivers/gpu/drm/tegra/plane.h |  15 ++
 7 files changed, 515 insertions(+)

[...]

Quoted 1 line
drivers/gpu/drm/tegra/dc.c +0 -0

[...]

Quoted 14 lines
drivers/gpu/drm/tegra/dc.c +10 -0
@@ -2011,7 +2143,215 @@ static void tegra_crtc_atomic_flush(struct drm_crtc *crtc,
 	value = tegra_dc_readl(dc, DC_CMD_STATE_CONTROL);
 }
 
+static bool tegra_plane_is_cursor(const struct drm_plane_state *state)
+{
+	const struct tegra_dc_soc_info *soc = to_tegra_dc(state->crtc)->soc;
+	const struct drm_format_info *fmt = state->fb->format;
+	unsigned int src_w = drm_rect_width(&state->src) >> 16;
+	unsigned int dst_w = drm_rect_width(&state->dst);
+
+	if (state->plane->type != DRM_PLANE_TYPE_CURSOR)
+		return false;
+
Quoted 8 lines

drivers/gpu/drm/tegra/dc.c static void tegra_crtc_atomic_flush(struct drm_crtc *crtc, · line 2160

Quoted 5 lines
drivers/gpu/drm/tegra/dc.c +5 -0
+	if (soc->supports_cursor)
+		return true;
+
+	if (src_w != dst_w || fmt->num_planes != 1 || src_w * fmt->cpp[0] > 256)
+		return false;

Technically there could be some random overlay window that matches these conditions and is erroneously detected as being a cursor.

The "random overlay window" with the DRM_PLANE_TYPE_CURSOR could happen only for the oldest Tegras. It's not a problem at all since everything will work properly anyways because we skip only the small sized plane that doesn't contribute to the BW requirement.

Quoted 3 lines

I wonder if we
should add a field to a plane that marks it as being used as cursor for the cases where we don't support a hardware cursor.

I don't think that we have information about how plane is used in the driver. DRM core should know this, tegra-drm not.

It's unpractical to worry about this case, hence I think it's better to leave this part as-is for now, if you don't mind.

Quoted 28 lines

[...]

Quoted 14 lines
drivers/gpu/drm/tegra/dc.h +2 -0
index 29f19c3c6149..db10af097033 100644
@@ -15,6 +15,8 @@
 
 struct tegra_output;
 
+#define TEGRA_DC_LEGACY_PLANES_NUM	7
+
 struct tegra_dc_state {
 	struct drm_crtc_state base;
 

drivers/gpu/drm/tegra/dc.h struct tegra_dc_state { · line 29

Quoted 5 lines
drivers/gpu/drm/tegra/dc.h +2 -0
@@ -23,6 +25,8 @@ struct tegra_dc_state {
 	unsigned int div;
 
 	u32 planes;
+
+	unsigned long plane_peak_bw[TEGRA_DC_LEGACY_PLANES_NUM];

Why can we not store this peak bandwidth value within the plane state? I know that this isn't exactly per-plane data because it depends on the state of other planes, but that doesn't really prevent the value to live within the plane state. The plane state is, after all, part of the global state, and hence any such state needs to be considered within the context of that global atomic state.

Quoted 4 lines

I suppose that might make it a little bit more difficult to get at the data, but I think the end result would be less confusing than having an array here with potentially unused fields. It would also get rid of the need to look up planes by their per-CRTC index.

The reason I stored the peak_bw in CRTC state is because it feels more natural to me somehow. It shouldn't be a problem to move it to the planes state. I'll prepare the new version shortly.

...

Quoted 9 lines
Quoted 9 lines
drivers/gpu/drm/tegra/dc.h +0 -0
 static const struct drm_mode_config_helper_funcs
drivers/gpu/drm/tegra/hub.c +0 -0
index bfae8a02f55b..f1bbc5991854 100644
@@ -358,6 +358,9 @@ static int tegra_shared_plane_atomic_check(struct drm_plane *plane,
 	struct tegra_dc *dc = to_tegra_dc(new_plane_state->crtc);
 	int err;
 
Quoted 6 lines

drivers/gpu/drm/tegra/hub.c static int tegra_shared_plane_atomic_check(struct drm_plane *plane, · line 363

Quoted 3 lines
drivers/gpu/drm/tegra/hub.c +3 -0
+	plane_state->peak_memory_bandwidth = 0;
+	plane_state->avg_memory_bandwidth = 0;
+

Since ICC isn't supported yet on Tegra186 and later, does it even make sense to initialize these?

I added it for consistency, right now it's not needed for Tegra186+.

#4 lore raw

PATCH v17 2/2 drm/tegra: dc: Extend debug stats with total number of events

In reply to Dmitry Osipenko

It's useful to know the total number of underflow events and currently the debug stats are getting reset each time CRTC is being disabled. Let's account the overall number of events that doesn't get a reset.

---
 drivers/gpu/drm/tegra/dc.c | 10 ++++++++++
 drivers/gpu/drm/tegra/dc.h |  5 +++++
 2 files changed, 15 insertions(+)
drivers/gpu/drm/tegra/dc.c +10 -0
index 9997e4942bf8..d87ef2550e26 100644
@@ -1596,6 +1596,11 @@ static int tegra_dc_show_stats(struct seq_file *s, void *data)
 	seq_printf(s, "underflow: %lu\n", dc->stats.underflow);
 	seq_printf(s, "overflow: %lu\n", dc->stats.overflow);
 
+	seq_printf(s, "frames total: %lu\n", dc->stats.frames_total);
+	seq_printf(s, "vblank total: %lu\n", dc->stats.vblank_total);
+	seq_printf(s, "underflow total: %lu\n", dc->stats.underflow_total);
+	seq_printf(s, "overflow total: %lu\n", dc->stats.overflow_total);
+
 	return 0;
 }
 
@@ -2370,6 +2375,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
 		/*
 		dev_dbg(dc->dev, "%s(): frame end\n", __func__);
 		*/
+		dc->stats.frames_total++;
 		dc->stats.frames++;
 	}
 
@@ -2378,6 +2384,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
 		dev_dbg(dc->dev, "%s(): vertical blank\n", __func__);
 		*/
 		drm_crtc_handle_vblank(&dc->base);
+		dc->stats.vblank_total++;
 		dc->stats.vblank++;
 	}
 
@@ -2385,6 +2392,7 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
 		/*
 		dev_dbg(dc->dev, "%s(): underflow\n", __func__);
 		*/
+		dc->stats.underflow_total++;
 		dc->stats.underflow++;
 	}
 
@@ -2392,11 +2400,13 @@ static irqreturn_t tegra_dc_irq(int irq, void *data)
 		/*
 		dev_dbg(dc->dev, "%s(): overflow\n", __func__);
 		*/
+		dc->stats.overflow_total++;
 		dc->stats.overflow++;
 	}
 
 	if (status & HEAD_UF_INT) {
 		dev_dbg_ratelimited(dc->dev, "%s(): head underflow\n", __func__);
+		dc->stats.underflow_total++;
 		dc->stats.underflow++;
 	}
 
drivers/gpu/drm/tegra/dc.h +5 -0
index db10af097033..063bb777d607 100644
@@ -48,6 +48,11 @@ struct tegra_dc_stats {
 	unsigned long vblank;
 	unsigned long underflow;
 	unsigned long overflow;
+
+	unsigned long frames_total;
+	unsigned long vblank_total;
+	unsigned long underflow_total;
+	unsigned long overflow_total;
 };
 
 struct tegra_windowgroup_soc {
Signature · 1 line
2.30.2