On Thu, Oct 30, 2025 at 06:56:07PM -0300, Gustavo Sousa wrote:
> Quoting Matt Roper (2025-10-29 17:54:39-03:00)
> >On Tue, Oct 21, 2025 at 09:28:36PM -0300, Gustavo Sousa wrote:
> >> From: Sai Teja Pottumuttu <[email protected]>
> >>
> >> Starting with Xe3p_LPD, we get two new registers and some bits in
> >> existing registers that expose hardware state information at the time of
> >> underrun notification that can be relevant to debugging.
> >>
> >> Add the necessary logic in the driver to print the available debug
> >> information when an underrun happens.
> >>
> >> v2:
> >> - Use seq_buf to generate planes string. (Jani)
> >> - Move definition of FBC_DEBUG_STATUS to intel_fbc_regs.h. (Ville)
> >> - Change logic for processing UNDERRUN_DBG1 to use loops and move it
> >> to a separate function. (Gustavo)
> >> - Always print underrun error message, even if there wouldn't be any
> >> debug info associated to the underrun. (Gustavo)
> >>
> >> Bspec: 69111, 69561, 74411, 74412
> >> Cc: Jani Nikula <[email protected]>
> >> Cc: Ville Syrjälä <[email protected]>
> >> Signed-off-by: Sai Teja Pottumuttu <[email protected]>
> >> Co-developed-by: Gustavo Sousa <[email protected]>
> >> Signed-off-by: Gustavo Sousa <[email protected]>
> >> ---
> >> drivers/gpu/drm/i915/display/intel_display_regs.h | 20 +++++
> >> drivers/gpu/drm/i915/display/intel_fbc_regs.h | 2 +
> >> drivers/gpu/drm/i915/display/intel_fifo_underrun.c | 87
> >> ++++++++++++++++++++++
> >> 3 files changed, 109 insertions(+)
> >>
> >> diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h
> >> b/drivers/gpu/drm/i915/display/intel_display_regs.h
> >> index 9d71e26a4fa2..c9f8b90faa42 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_display_regs.h
> >> +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h
> >> @@ -882,6 +882,25 @@
> >> #define PIPE_MISC2_FLIP_INFO_PLANE_SEL_MASK
> >> REG_GENMASK(2, 0) /* tgl+ */
> >> #define PIPE_MISC2_FLIP_INFO_PLANE_SEL(plane_id)
> >> REG_FIELD_PREP(PIPE_MISC2_FLIP_INFO_PLANE_SEL_MASK, (plane_id))
> >>
> >> +#define _UNDERRUN_DBG1_A 0x70064
> >> +#define _UNDERRUN_DBG1_B 0x71064
> >> +#define UNDERRUN_DBG1(pipe)
> >> _MMIO_PIPE(pipe, \
> >> +
> >> _UNDERRUN_DBG1_A, \
> >> +
> >> _UNDERRUN_DBG1_B)
> >> +#define UNDERRUN_DBUF_BLOCK_NOT_VALID_MASK
> >> REG_GENMASK(29, 24)
> >> +#define UNDERRUN_DDB_EMPTY_MASK REG_GENMASK(21,
> >> 16)
> >> +#define UNDERRUN_DBUF_NOT_FILLED_MASK
> >> REG_GENMASK(13, 8)
> >> +#define UNDERRUN_BELOW_WM0_MASK REG_GENMASK(5, 0)
> >> +
> >> +#define _UNDERRUN_DBG2_A 0x70068
> >> +#define _UNDERRUN_DBG2_B 0x71068
> >> +#define UNDERRUN_DBG2(pipe)
> >> _MMIO_PIPE(pipe, \
> >> +
> >> _UNDERRUN_DBG2_A, \
> >> +
> >> _UNDERRUN_DBG2_B)
> >> +#define UNDERRUN_FRAME_LINE_COUNTERS_FROZEN REG_BIT(31)
> >> +#define UNDERRUN_PIPE_FRAME_COUNT_MASK REG_GENMASK(30,
> >> 20)
> >> +#define UNDERRUN_LINE_COUNT_MASK REG_GENMASK(19,
> >> 0)
> >> +
> >> #define DPINVGTT _MMIO(VLV_DISPLAY_BASE +
> >> 0x7002c) /* VLV/CHV only */
> >> #define DPINVGTT_EN_MASK_CHV
> >> REG_GENMASK(27, 16)
> >> #define DPINVGTT_EN_MASK_VLV
> >> REG_GENMASK(23, 16)
> >> @@ -1416,6 +1435,7 @@
> >>
> >> #define GEN12_DCPR_STATUS_1 _MMIO(0x46440)
> >> #define XELPDP_PMDEMAND_INFLIGHT_STATUS REG_BIT(26)
> >> +#define XE3P_UNDERRUN_PKGC REG_BIT(21)
> >>
> >> #define FUSE_STRAP _MMIO(0x42014)
> >> #define ILK_INTERNAL_GRAPHICS_DISABLE REG_BIT(31)
> >> diff --git a/drivers/gpu/drm/i915/display/intel_fbc_regs.h
> >> b/drivers/gpu/drm/i915/display/intel_fbc_regs.h
> >> index b1d0161a3196..272dba7f9695 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_fbc_regs.h
> >> +++ b/drivers/gpu/drm/i915/display/intel_fbc_regs.h
> >> @@ -88,6 +88,8 @@
> >> #define DPFC_FENCE_YOFF _MMIO(0x3218)
> >> #define ILK_DPFC_FENCE_YOFF(fbc_id) _MMIO_PIPE((fbc_id), 0x43218,
> >> 0x43258)
> >> #define DPFC_CHICKEN _MMIO(0x3224)
> >> +#define FBC_DEBUG_STATUS(pipe) _MMIO_PIPE(pipe, 0x43220,
> >> 0x43260)
> >
> >Is 'pipe' correct here? Most of the other FBC registers are
> >parameterized by FBC instance rather than pipe.
>
> Yeah, I just blindly copy/pasted the definition without realizing that
> the rest of the file uses fbc_id. I'll change it to use fbc_id as well.
>
> >
> >> +#define FBC_UNDERRUN_DECOMPRESSION REG_BIT(27)
> >> #define ILK_DPFC_CHICKEN(fbc_id) _MMIO_PIPE((fbc_id), 0x43224,
> >> 0x43264)
> >> #define DPFC_HT_MODIFY REG_BIT(31) /* pre-ivb */
> >> #define DPFC_NUKE_ON_ANY_MODIFICATION REG_BIT(23) /*
> >> bdw+ */
> >> diff --git a/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> >> b/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> >> index c2ce8461ac9e..43cf141a59ae 100644
> >> --- a/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> >> +++ b/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> >> @@ -25,6 +25,8 @@
> >> *
> >> */
> >>
> >> +#include <linux/seq_buf.h>
> >> +
> >> #include <drm/drm_print.h>
> >>
> >> #include "i915_reg.h"
> >> @@ -34,6 +36,7 @@
> >> #include "intel_display_trace.h"
> >> #include "intel_display_types.h"
> >> #include "intel_fbc.h"
> >> +#include "intel_fbc_regs.h"
> >> #include "intel_fifo_underrun.h"
> >> #include "intel_pch_display.h"
> >>
> >> @@ -352,6 +355,87 @@ bool intel_set_pch_fifo_underrun_reporting(struct
> >> intel_display *display,
> >> return old;
> >> }
> >>
> >> +#define UNDERRUN_DBG1_NUM_PLANES 6
> >> +
> >> +static void process_underrun_dbg1(struct intel_display *display,
> >> + enum pipe pipe)
> >> +{
> >> + struct {
> >> + u32 mask;
> >> + const char *info;
> >> + } info_masks[] = {
> >> + { UNDERRUN_DBUF_BLOCK_NOT_VALID_MASK, "DBUF block not
> >> valid" },
> >> + { UNDERRUN_DDB_EMPTY_MASK, "DDB empty" },
> >> + { UNDERRUN_DBUF_NOT_FILLED_MASK, "DBUF not completely
> >> filled" },
> >> + { UNDERRUN_BELOW_WM0_MASK, "DBUF below WM0" },
> >> + };
> >> + DECLARE_SEQ_BUF(planes_desc, 32);
> >> + u32 val;
> >> +
> >> + val = intel_de_read(display, UNDERRUN_DBG1(pipe));
> >> + intel_de_write(display, UNDERRUN_DBG1(pipe), val);
> >> +
> >> + for (int i = 0; i < ARRAY_SIZE(info_masks); i++) {
> >> + u32 plane_bits;
> >> +
> >> + plane_bits = val & info_masks[i].mask;
> >> +
> >> + if (!plane_bits)
> >> + continue;
> >> +
> >> + plane_bits >>= ffs(info_masks[i].mask) - 1;
> >
> >Nitpick: It seems like we're just open-coding REG_FIELD_GET here. Any
> >reason not to simplify down to something like this?
> >
> > u32 plane_bits = REG_FIELD_GET(info_masks[i].mask, val);
> >
> > if (!plane_bits)
> > continue;
>
> We can't use REG_FIELD_GET() because the mask parameter must be a
> constant expression. That's why I went with open-coded version.
Oh yeah, I always forget about that restriction. I'm fine with keeping
the open-coded version in that case, although you may want to move the
plane_bits assignment up onto the declaration line. And maybe use
__ffs() instead of ffs() to avoid the need to substract 1.
>
> We could change the current patch to use REG_FIELD_GET() with something
> like below. What do you think?
>
> |diff --git a/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> b/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> |index 43cf141a59ae..34faedb9799c 100644
> |--- a/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> |+++ b/drivers/gpu/drm/i915/display/intel_fifo_underrun.c
> |@@ -360,35 +360,28 @@ bool intel_set_pch_fifo_underrun_reporting(struct
> intel_display *display,
> | static void process_underrun_dbg1(struct intel_display *display,
> | enum pipe pipe)
> | {
> |+ u32 val = intel_de_read(display, UNDERRUN_DBG1(pipe));
> | struct {
> |- u32 mask;
> |+ u32 plane_mask;
> | const char *info;
> |- } info_masks[] = {
> |- { UNDERRUN_DBUF_BLOCK_NOT_VALID_MASK, "DBUF block not
> valid" },
> |- { UNDERRUN_DDB_EMPTY_MASK, "DDB empty" },
> |- { UNDERRUN_DBUF_NOT_FILLED_MASK, "DBUF not completely
> filled" },
> |- { UNDERRUN_BELOW_WM0_MASK, "DBUF below WM0" },
> |+ } masks[] = {
> |+ { REG_FIELD_GET(UNDERRUN_DBUF_BLOCK_NOT_VALID_MASK,
> val), "DBUF block not valid" },
> |+ { REG_FIELD_GET(UNDERRUN_DDB_EMPTY_MASK, val), "DDB
> empty" },
> |+ { REG_FIELD_GET(UNDERRUN_DBUF_NOT_FILLED_MASK, val),
> "DBUF not completely filled" },
> |+ { REG_FIELD_GET(UNDERRUN_BELOW_WM0_MASK, val), "DBUF
> below WM0" },
> | };
> | DECLARE_SEQ_BUF(planes_desc, 32);
> |- u32 val;
> |
> |- val = intel_de_read(display, UNDERRUN_DBG1(pipe));
> | intel_de_write(display, UNDERRUN_DBG1(pipe), val);
> |
> |- for (int i = 0; i < ARRAY_SIZE(info_masks); i++) {
> |- u32 plane_bits;
> |-
> |- plane_bits = val & info_masks[i].mask;
> |-
> |- if (!plane_bits)
> |+ for (int i = 0; i < ARRAY_SIZE(masks); i++) {
> |+ if (!masks[i].plane_mask)
> | continue;
> |
> |- plane_bits >>= ffs(info_masks[i].mask) - 1;
> |-
> | seq_buf_clear(&planes_desc);
> |
> | for (int j = 0; j < UNDERRUN_DBG1_NUM_PLANES; j++) {
> |- if (!(plane_bits & REG_BIT(j)))
> |+ if (!(masks[i].plane_mask & REG_BIT(j)))
> | continue;
> |
> | if (j == 0)
> |@@ -399,7 +392,7 @@ static void process_underrun_dbg1(struct
> intel_display *display,
> |
> | drm_err(display->drm,
> | "Pipe %c FIFO underrun info: %s on planes:
> %s\n",
> |- pipe_name(pipe), info_masks[i].info,
> seq_buf_str(&planes_desc));
> |+ pipe_name(pipe), masks[i].info,
> seq_buf_str(&planes_desc));
> |
> | drm_WARN_ON(display->drm,
> seq_buf_has_overflowed(&planes_desc));
> | }
>
> >
> >> +
> >> + seq_buf_clear(&planes_desc);
> >> +
> >> + for (int j = 0; j < UNDERRUN_DBG1_NUM_PLANES; j++) {
> >> + if (!(plane_bits & REG_BIT(j)))
> >> + continue;
> >> +
> >> + if (j == 0)
> >> + seq_buf_puts(&planes_desc, "[C]");
> >> + else
> >> + seq_buf_printf(&planes_desc, "[%d]", j);
> >> + }
> >> +
> >> + drm_err(display->drm,
> >> + "Pipe %c FIFO underrun info: %s on planes: %s\n",
> >> + pipe_name(pipe), info_masks[i].info,
> >> seq_buf_str(&planes_desc));
> >> +
> >> + drm_WARN_ON(display->drm,
> >> seq_buf_has_overflowed(&planes_desc));
> >> + }
> >> +}
> >> +
> >> +static void xe3p_lpd_log_underrun(struct intel_display *display,
> >> + enum pipe pipe)
> >> +{
> >> + u32 val;
> >> +
> >> + process_underrun_dbg1(display, pipe);
> >> +
> >> + val = intel_de_read(display, UNDERRUN_DBG2(pipe));
> >> + if (val & UNDERRUN_FRAME_LINE_COUNTERS_FROZEN) {
> >> + intel_de_write(display, UNDERRUN_DBG2(pipe),
> >> UNDERRUN_FRAME_LINE_COUNTERS_FROZEN);
> >> + drm_err(display->drm, "Pipe %c FIFO underrun info: Frame
> >> count: %u, Line count: %u\n",
> >> + pipe_name(pipe),
> >> + REG_FIELD_GET(UNDERRUN_PIPE_FRAME_COUNT_MASK,
> >> val),
> >> + REG_FIELD_GET(UNDERRUN_LINE_COUNT_MASK, val));
> >> + }
> >> +
> >> + val = intel_de_read(display, FBC_DEBUG_STATUS(pipe));
> >> + if (val & FBC_UNDERRUN_DECOMPRESSION) {
> >> + intel_de_write(display, FBC_DEBUG_STATUS(pipe),
> >> FBC_UNDERRUN_DECOMPRESSION);
> >> + drm_err(display->drm, "Pipe %c FIFO underrun info: FBC
> >> decompression\n",
> >> + pipe_name(pipe));
> >> + }
> >
> >As noted above, I'm not sure if 'pipe' is technically correct for this
> >register. I think it always winds up with a 1:1 relationship on current
> >platforms, but would it make more sense to just move this check and
> >print into intel_fbc_handle_fifo_underrun_irq() where we're already
> >handling the FBC stuff on a per-FBC unit basis?
>
> Yeah. We probably want to check if there is a valid FBC instance
> respective to this pipe and then read the register.
>
> I see complications with moving this to
> intel_fbc_handle_fifo_underrun_irq():
>
> 1) The function intel_fbc_handle_fifo_underrun_irq() is more about
> disabling the FBC on an underrun. I think reporting that the FBC
> was decompressing when the there was an underrun makes more sense
> to be grouped together with the other info related to FIFO
> underruns. It could even be useful if, due to some hw/sw bug, FBC
> is still doing something after we disabled it (or so we thought)
> due to a previous underrun.
>
> 2) Logging underrun debug info is currently guarded by
> intel_set_cpu_fifo_underrun_reporting(). So, we would need to
> complicate the implementation a bit to ensure that
> intel_fbc_handle_fifo_underrun_irq() would only report when
> reporting was enabled.
>
>
> So, I was thinking about defining a new function
> intel_fbc_fifo_underrun_log_info() and calling it from
> xe3p_lpd_log_underrun(). What do you think?
Yeah, that sounds fine to me.
Matt
>
> --
> Gustavo Sousa
> >
> >
> >Matt
> >
> >> +
> >> + val = intel_de_read(display, GEN12_DCPR_STATUS_1);
> >> + if (val & XE3P_UNDERRUN_PKGC) {
> >> + intel_de_write(display, GEN12_DCPR_STATUS_1,
> >> XE3P_UNDERRUN_PKGC);
> >> + drm_err(display->drm, "Pipe %c FIFO underrun info: Pkgc
> >> blocking memory\n",
> >> + pipe_name(pipe));
> >> + }
> >> +}
> >> +
> >> /**
> >> * intel_cpu_fifo_underrun_irq_handler - handle CPU fifo underrun
> >> interrupt
> >> * @display: display device instance
> >> @@ -379,6 +463,9 @@ void intel_cpu_fifo_underrun_irq_handler(struct
> >> intel_display *display,
> >> trace_intel_cpu_fifo_underrun(display, pipe);
> >>
> >> drm_err(display->drm, "CPU pipe %c FIFO underrun\n",
> >> pipe_name(pipe));
> >> +
> >> + if (DISPLAY_VER(display) >= 35)
> >> + xe3p_lpd_log_underrun(display, pipe);
> >> }
> >>
> >> intel_fbc_handle_fifo_underrun_irq(display);
> >>
> >> --
> >> 2.51.0
> >>
> >
> >--
> >Matt Roper
> >Graphics Software Engineer
> >Linux GPU Platform Enablement
> >Intel Corporation
--
Matt Roper
Graphics Software Engineer
Linux GPU Platform Enablement
Intel Corporation