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.

> +#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;

> +
> +             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?


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

Reply via email to