[PATCH v4 15/21] drm/i915/flipq: Provide the nuts and bolts code for flip queue

Shankar, Uma uma.shankar at intel.com
Mon Jun 23 19:54:47 UTC 2025



> -----Original Message-----
> From: Intel-xe <intel-xe-bounces at lists.freedesktop.org> On Behalf Of Ville
> Syrjala
> Sent: Monday, June 9, 2025 7:41 PM
> To: intel-gfx at lists.freedesktop.org
> Cc: intel-xe at lists.freedesktop.org
> Subject: [PATCH v4 15/21] drm/i915/flipq: Provide the nuts and bolts code for flip
> queue
> 
> From: Ville Syrjälä <ville.syrjala at linux.intel.com>
> 
> Provide the lower level code for PIPEDMC based flip queue.
> 
> We'll use the so called semi-full flip queue mode where the PIPEDMC will start the
> provided DSB on a scanline a little ahead of the vblank. We need to program the
> triggering scanline early enough so that the DSB has enough time to complete
> writing all the double buffered registers before they get latched (at start of vblank).
> 
> The firmware implements several queues:
> - 3 "plane queues" which execute a single DSB per entry
> - 1 "general queue" which can apparently execute 2 DSBs per entry
> - 1 vestigial "fast queue" that replaced the "simple flip queue"
>   on ADL+, but this isn't supposed to be used due to issues.
> 
> But we only need a single plane queue really, and we won't actually use it as a real
> queue because we don't allow queueing multiple commits ahead of time. So the
> whole thing is perhaps useless. I suppose there migth be some power saving
> benefits if we would get the flip scheduled by userspace early and then could keep
> some hardware powered off a bit longer until the DMC kicks off the flipq
> programming. But that is pure speculation at this time and needs to be proven.
> 
> The code to hook up the flip queue into the actual atomic commit path will follow
> later.
> 
> TODO: need to think how to do the "wait for DMC firmware load" nicely
>       need to think about VRR and PSR
>       etc.
> 
> v2: Don't write DMC_FQ_W2_PTS_CFG_SEL on pre-lnl
>     Don't oops at flipq init if there is no dmc
> v3: Adapt to PTL+ flipq changes (different queue entry
>     layout, different trigger event, need VRR TG)
>     Use the actual CDCLK frequency
>     Ask the DSB code how long things are expected to take
> v3: Adjust the cdclk rounding (docs are 100% vague, Windows
>     rounds like this)
>     Initialize some undocumented magic DMC variables on PTL
> v4: Use PIPEDMC_FQ_STATUS for busy check (the busy bit in
>     PIPEDMC_FQ_CTRL is apparently gone on LNL+)
>     Based the preempt timeout on the max exec time
>     Preempt before disabling the flip queue
>     Order the PIPEDMC_SCANLINECMP* writes a bit more carefully
>     Fix some typos

Changes look good to me.
Reviewed-by: Uma Shankar <uma.shankar at intel.com>

> Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
> ---
>  drivers/gpu/drm/i915/Makefile                 |   1 +
>  .../drm/i915/display/intel_display_driver.c   |   3 +
>  .../drm/i915/display/intel_display_types.h    |  17 +
>  drivers/gpu/drm/i915/display/intel_dmc.c      |  51 +++
>  drivers/gpu/drm/i915/display/intel_dmc.h      |  11 +
>  drivers/gpu/drm/i915/display/intel_dsb.c      |   1 +
>  drivers/gpu/drm/i915/display/intel_flipq.c    | 385 ++++++++++++++++++
>  drivers/gpu/drm/i915/display/intel_flipq.h    |  32 ++
>  drivers/gpu/drm/xe/Makefile                   |   1 +
>  9 files changed, 502 insertions(+)
>  create mode 100644 drivers/gpu/drm/i915/display/intel_flipq.c
>  create mode 100644 drivers/gpu/drm/i915/display/intel_flipq.h
> 
> diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index
> 7c6075bc483c..7545767e4ced 100644
> --- a/drivers/gpu/drm/i915/Makefile
> +++ b/drivers/gpu/drm/i915/Makefile
> @@ -265,6 +265,7 @@ i915-y += \
>  	display/intel_fbc.o \
>  	display/intel_fdi.o \
>  	display/intel_fifo_underrun.o \
> +	display/intel_flipq.o \
>  	display/intel_frontbuffer.o \
>  	display/intel_global_state.o \
>  	display/intel_hdcp.o \
> diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c
> b/drivers/gpu/drm/i915/display/intel_display_driver.c
> index ec799a1773e4..3ee3fd71bd25 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_driver.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
> @@ -44,6 +44,7 @@
>  #include "intel_fbc.h"
>  #include "intel_fbdev.h"
>  #include "intel_fdi.h"
> +#include "intel_flipq.h"
>  #include "intel_gmbus.h"
>  #include "intel_hdcp.h"
>  #include "intel_hotplug.h"
> @@ -535,6 +536,8 @@ int intel_display_driver_probe(struct intel_display
> *display)
>  	 */
>  	intel_hdcp_component_init(display);
> 
> +	intel_flipq_init(display);
> +
>  	/*
>  	 * Force all active planes to recompute their states. So that on
>  	 * mode_setcrtc after probe, all the intel_plane_state variables diff --git
> a/drivers/gpu/drm/i915/display/intel_display_types.h
> b/drivers/gpu/drm/i915/display/intel_display_types.h
> index ed4d743fc7c5..5b30b652e123 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_types.h
> +++ b/drivers/gpu/drm/i915/display/intel_display_types.h
> @@ -1365,6 +1365,21 @@ struct intel_pipe_crc {
>  	enum intel_pipe_crc_source source;
>  };
> 
> +enum intel_flipq_id {
> +	INTEL_FLIPQ_PLANE_1,
> +	INTEL_FLIPQ_PLANE_2,
> +	INTEL_FLIPQ_PLANE_3,
> +	INTEL_FLIPQ_GENERAL,
> +	INTEL_FLIPQ_FAST,
> +	MAX_INTEL_FLIPQ,
> +};
> +
> +struct intel_flipq {
> +	u32 start_mmioaddr;
> +	enum intel_flipq_id flipq_id;
> +	u8 tail;
> +};
> +
>  struct intel_crtc {
>  	struct drm_crtc base;
>  	enum pipe pipe;
> @@ -1396,6 +1411,8 @@ struct intel_crtc {
>  	bool cpu_fifo_underrun_disabled;
>  	bool pch_fifo_underrun_disabled;
> 
> +	struct intel_flipq flipq[MAX_INTEL_FLIPQ];
> +
>  	/* per-pipe watermark state */
>  	struct {
>  		/* watermarks currently being used  */ diff --git
> a/drivers/gpu/drm/i915/display/intel_dmc.c
> b/drivers/gpu/drm/i915/display/intel_dmc.c
> index 0896246c3982..e239e444eafe 100644
> --- a/drivers/gpu/drm/i915/display/intel_dmc.c
> +++ b/drivers/gpu/drm/i915/display/intel_dmc.c
> @@ -24,6 +24,7 @@
> 
>  #include <linux/debugfs.h>
>  #include <linux/firmware.h>
> +#include <drm/drm_vblank.h>
> 
>  #include "i915_drv.h"
>  #include "i915_reg.h"
> @@ -35,6 +36,7 @@
>  #include "intel_display_types.h"
>  #include "intel_dmc.h"
>  #include "intel_dmc_regs.h"
> +#include "intel_flipq.h"
>  #include "intel_step.h"
> 
>  /**
> @@ -640,6 +642,8 @@ void intel_dmc_enable_pipe(struct intel_display *display,
> enum pipe pipe)
>  	assert_dmc_loaded(display, dmc_id);
> 
>  	if (DISPLAY_VER(display) >= 20) {
> +		intel_flipq_reset(display, pipe);
> +
>  		intel_de_write(display, PIPEDMC_INTERRUPT(pipe),
> pipedmc_interrupt_mask(display));
>  		intel_de_write(display, PIPEDMC_INTERRUPT_MASK(pipe),
> ~pipedmc_interrupt_mask(display));
>  	}
> @@ -665,6 +669,8 @@ void intel_dmc_disable_pipe(struct intel_display *display,
> enum pipe pipe)
>  	if (DISPLAY_VER(display) >= 20) {
>  		intel_de_write(display, PIPEDMC_INTERRUPT_MASK(pipe), ~0);
>  		intel_de_write(display, PIPEDMC_INTERRUPT(pipe),
> pipedmc_interrupt_mask(display));
> +
> +		intel_flipq_reset(display, pipe);
>  	}
>  }
> 
> @@ -767,6 +773,13 @@ void intel_dmc_load_program(struct intel_display
> *display)
>  	for_each_dmc_id(dmc_id)
>  		intel_dmc_load_mmio(display, dmc_id);
> 
> +	if (DISPLAY_VER(display) >= 20)
> +		intel_de_write(display, DMC_FQ_W2_PTS_CFG_SEL,
> +			       PIPE_D_DMC_W2_PTS_CONFIG_SELECT(PIPE_D)
> |
> +			       PIPE_C_DMC_W2_PTS_CONFIG_SELECT(PIPE_C)
> |
> +			       PIPE_B_DMC_W2_PTS_CONFIG_SELECT(PIPE_B)
> |
> +
> PIPE_A_DMC_W2_PTS_CONFIG_SELECT(PIPE_A));
> +
>  	power_domains->dc_state = 0;
> 
>  	gen9_set_dc_state_debugmask(display);
> @@ -1281,6 +1294,17 @@ void intel_dmc_suspend(struct intel_display *display)
>  		intel_dmc_runtime_pm_put(display);
>  }
> 
> +void intel_dmc_wait_fw_load(struct intel_display *display) {
> +	struct intel_dmc *dmc = display_to_dmc(display);
> +
> +	if (!HAS_DMC(display))
> +		return;
> +
> +	if (dmc)
> +		flush_work(&dmc->work);
> +}
> +
>  /**
>   * intel_dmc_resume() - init DMC firmware during system resume
>   * @display: display instance
> @@ -1516,3 +1540,30 @@ void intel_pipedmc_irq_handler(struct intel_display
> *display, enum pipe pipe)
>  		drm_err(display->drm, "[CRTC:%d:%s]] PIPEDMC interrupt
> vector 0x%x\n",
>  			crtc->base.base.id, crtc->base.name, tmp);  }
> +
> +void intel_pipedmc_enable_event(struct intel_crtc *crtc,
> +				enum pipedmc_event_id event)
> +{
> +	struct intel_display *display = to_intel_display(crtc);
> +	enum intel_dmc_id dmc_id = PIPE_TO_DMC_ID(crtc->pipe);
> +
> +	dmc_configure_event(display, dmc_id, event, true); }
> +
> +void intel_pipedmc_disable_event(struct intel_crtc *crtc,
> +				 enum pipedmc_event_id event)
> +{
> +	struct intel_display *display = to_intel_display(crtc);
> +	enum intel_dmc_id dmc_id = PIPE_TO_DMC_ID(crtc->pipe);
> +
> +	dmc_configure_event(display, dmc_id, event, false); }
> +
> +u32 intel_pipedmc_start_mmioaddr(struct intel_crtc *crtc) {
> +	struct intel_display *display = to_intel_display(crtc);
> +	struct intel_dmc *dmc = display_to_dmc(display);
> +	enum intel_dmc_id dmc_id = PIPE_TO_DMC_ID(crtc->pipe);
> +
> +	return dmc ? dmc->dmc_info[dmc_id].start_mmioaddr : 0; }
> diff --git a/drivers/gpu/drm/i915/display/intel_dmc.h
> b/drivers/gpu/drm/i915/display/intel_dmc.h
> index a3792052078a..b74e6c32198b 100644
> --- a/drivers/gpu/drm/i915/display/intel_dmc.h
> +++ b/drivers/gpu/drm/i915/display/intel_dmc.h
> @@ -9,12 +9,15 @@
>  #include <linux/types.h>
> 
>  enum pipe;
> +enum pipedmc_event_id;
>  struct drm_printer;
> +struct intel_crtc;
>  struct intel_display;
>  struct intel_dmc_snapshot;
> 
>  void intel_dmc_init(struct intel_display *display);  void
> intel_dmc_load_program(struct intel_display *display);
> +void intel_dmc_wait_fw_load(struct intel_display *display);
>  void intel_dmc_disable_program(struct intel_display *display);  void
> intel_dmc_enable_pipe(struct intel_display *display, enum pipe pipe);  void
> intel_dmc_disable_pipe(struct intel_display *display, enum pipe pipe); @@ -36,4
> +39,12 @@ void assert_main_dmc_loaded(struct intel_display *display);
> 
>  void intel_pipedmc_irq_handler(struct intel_display *display, enum pipe pipe);
> 
> +u32 intel_pipedmc_start_mmioaddr(struct intel_crtc *crtc); void
> +intel_pipedmc_enable_event(struct intel_crtc *crtc,
> +				enum pipedmc_event_id event);
> +void intel_pipedmc_disable_event(struct intel_crtc *crtc,
> +				 enum pipedmc_event_id event);
> +
> +void intel_pipedmc_irq_handler(struct intel_display *display, enum pipe
> +pipe);
> +
>  #endif /* __INTEL_DMC_H__ */
> diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c
> b/drivers/gpu/drm/i915/display/intel_dsb.c
> index f60a6698419c..10d031020d1e 100644
> --- a/drivers/gpu/drm/i915/display/intel_dsb.c
> +++ b/drivers/gpu/drm/i915/display/intel_dsb.c
> @@ -14,6 +14,7 @@
>  #include "intel_display_regs.h"
>  #include "intel_display_rpm.h"
>  #include "intel_display_types.h"
> +#include "intel_dmc_regs.h"
>  #include "intel_dsb.h"
>  #include "intel_dsb_buffer.h"
>  #include "intel_dsb_regs.h"
> diff --git a/drivers/gpu/drm/i915/display/intel_flipq.c
> b/drivers/gpu/drm/i915/display/intel_flipq.c
> new file mode 100644
> index 000000000000..c9804cfe506a
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_flipq.c
> @@ -0,0 +1,385 @@
> +// SPDX-License-Identifier: MIT
> +/*
> + * Copyright © 2025 Intel Corporation
> + */
> +
> +#include <linux/pci.h>
> +
> +#include <drm/drm_print.h>
> +
> +#include "i915_utils.h"
> +#include "intel_step.h"
> +#include "intel_crtc.h"
> +#include "intel_de.h"
> +#include "intel_display_core.h"
> +#include "intel_display_types.h"
> +#include "intel_flipq.h"
> +#include "intel_dmc.h"
> +#include "intel_dmc_regs.h"
> +#include "intel_dsb.h"
> +#include "intel_vblank.h"
> +#include "intel_vrr.h"
> +
> +#define for_each_flipq(flipq_id) \
> +	for ((flipq_id) = INTEL_FLIPQ_PLANE_1; (flipq_id) < MAX_INTEL_FLIPQ;
> +(flipq_id)++)
> +
> +static int intel_flipq_offset(enum intel_flipq_id flipq_id) {
> +	switch (flipq_id) {
> +	case INTEL_FLIPQ_PLANE_1:
> +		return 0x008;
> +	case INTEL_FLIPQ_PLANE_2:
> +		return 0x108;
> +	case INTEL_FLIPQ_PLANE_3:
> +		return 0x208;
> +	case INTEL_FLIPQ_GENERAL:
> +		return 0x308;
> +	case INTEL_FLIPQ_FAST:
> +		return 0x3c8;
> +	default:
> +		MISSING_CASE(flipq_id);
> +		return 0;
> +	}
> +}
> +
> +static int intel_flipq_size_dw(enum intel_flipq_id flipq_id) {
> +	switch (flipq_id) {
> +	case INTEL_FLIPQ_PLANE_1:
> +	case INTEL_FLIPQ_PLANE_2:
> +	case INTEL_FLIPQ_PLANE_3:
> +		return 64;
> +	case INTEL_FLIPQ_GENERAL:
> +	case INTEL_FLIPQ_FAST:
> +		return 48;
> +	default:
> +		MISSING_CASE(flipq_id);
> +		return 0;
> +	}
> +}
> +
> +static int intel_flipq_elem_size_dw(enum intel_flipq_id flipq_id) {
> +	switch (flipq_id) {
> +	case INTEL_FLIPQ_PLANE_1:
> +	case INTEL_FLIPQ_PLANE_2:
> +	case INTEL_FLIPQ_PLANE_3:
> +		return 4;
> +	case INTEL_FLIPQ_GENERAL:
> +	case INTEL_FLIPQ_FAST:
> +		return 6;
> +	default:
> +		MISSING_CASE(flipq_id);
> +		return 0;
> +	}
> +}
> +
> +static int intel_flipq_size_entries(enum intel_flipq_id flipq_id) {
> +	return intel_flipq_size_dw(flipq_id) /
> +intel_flipq_elem_size_dw(flipq_id);
> +}
> +
> +static void intel_flipq_crtc_init(struct intel_crtc *crtc) {
> +	struct intel_display *display = to_intel_display(crtc);
> +	enum intel_flipq_id flipq_id;
> +
> +	for_each_flipq(flipq_id) {
> +		struct intel_flipq *flipq = &crtc->flipq[flipq_id];
> +
> +		flipq->start_mmioaddr = intel_pipedmc_start_mmioaddr(crtc) +
> intel_flipq_offset(flipq_id);
> +		flipq->flipq_id = flipq_id;
> +
> +		drm_dbg_kms(display->drm, "[CRTC:%d:%s] FQ %d: start
> 0x%x\n",
> +			    crtc->base.base.id, crtc->base.name,
> +			    flipq_id, flipq->start_mmioaddr);
> +	}
> +}
> +
> +bool intel_flipq_supported(struct intel_display *display) {
> +	if (!display->dmc.dmc)
> +		return false;
> +
> +	if (DISPLAY_VER(display) == 20)
> +		return true;
> +
> +	/* DMC firmware expects VRR timing generator to be used */
> +	return DISPLAY_VER(display) >= 30 &&
> +intel_vrr_always_use_vrr_tg(display);
> +}
> +
> +void intel_flipq_init(struct intel_display *display) {
> +	struct intel_crtc *crtc;
> +
> +	intel_dmc_wait_fw_load(display);
> +
> +	for_each_intel_crtc(display->drm, crtc)
> +		intel_flipq_crtc_init(crtc);
> +}
> +
> +static int cdclk_factor(struct intel_display *display) {
> +	if (DISPLAY_VER(display) >= 30)
> +		return 120;
> +	else
> +		return 280;
> +}
> +
> +static int intel_flipq_exec_time_us(struct intel_display *display) {
> +	return intel_dsb_exec_time_us() +
> +		DIV_ROUND_UP(display->cdclk.hw.cdclk * cdclk_factor(display),
> 540000) +
> +		display->sagv.block_time_us;
> +}
> +
> +static int intel_flipq_preempt_timeout_ms(struct intel_display
> +*display) {
> +	return DIV_ROUND_UP(intel_flipq_exec_time_us(display), 1000); }
> +
> +static void intel_flipq_preempt(struct intel_crtc *crtc, bool preempt)
> +{
> +	struct intel_display *display = to_intel_display(crtc);
> +
> +	intel_de_rmw(display, PIPEDMC_FQ_CTRL(crtc->pipe),
> +		     PIPEDMC_FQ_CTRL_PREEMPT, preempt ?
> PIPEDMC_FQ_CTRL_PREEMPT : 0);
> +
> +	if (preempt &&
> +	    intel_de_wait_for_clear(display,
> +				    PIPEDMC_FQ_STATUS(crtc->pipe),
> +				    PIPEDMC_FQ_STATUS_BUSY,
> +				    intel_flipq_preempt_timeout_ms(display)))
> +		drm_err(display->drm, "[CRTC:%d:%s] flip queue preempt
> timeout\n",
> +			crtc->base.base.id, crtc->base.name); }
> +
> +static int intel_flipq_current_head(struct intel_crtc *crtc, enum
> +intel_flipq_id flipq_id) {
> +	struct intel_display *display = to_intel_display(crtc);
> +
> +	return intel_de_read(display, PIPEDMC_FPQ_CHP(crtc->pipe, flipq_id));
> +}
> +
> +static void intel_flipq_write_tail(struct intel_crtc *crtc) {
> +	struct intel_display *display = to_intel_display(crtc);
> +
> +	intel_de_write(display, PIPEDMC_FPQ_ATOMIC_TP(crtc->pipe),
> +		       PIPEDMC_FPQ_PLANEQ_3_TP(crtc-
> >flipq[INTEL_FLIPQ_PLANE_3].tail) |
> +		       PIPEDMC_FPQ_PLANEQ_2_TP(crtc-
> >flipq[INTEL_FLIPQ_PLANE_2].tail) |
> +		       PIPEDMC_FPQ_PLANEQ_1_TP(crtc-
> >flipq[INTEL_FLIPQ_PLANE_1].tail) |
> +		       PIPEDMC_FPQ_FASTQ_TP(crtc-
> >flipq[INTEL_FLIPQ_FAST].tail) |
> +
> +PIPEDMC_FPQ_GENERALQ_TP(crtc->flipq[INTEL_FLIPQ_GENERAL].tail));
> +}
> +
> +static void intel_flipq_sw_dmc_wake(struct intel_crtc *crtc) {
> +	struct intel_display *display = to_intel_display(crtc);
> +
> +	intel_de_write(display, PIPEDMC_FPQ_CTL1(crtc->pipe),
> +PIPEDMC_SW_DMC_WAKE); }
> +
> +static int intel_flipq_exec_time_lines(const struct intel_crtc_state
> +*crtc_state) {
> +	struct intel_display *display = to_intel_display(crtc_state);
> +
> +	return intel_usecs_to_scanlines(&crtc_state->hw.adjusted_mode,
> +					intel_flipq_exec_time_us(display));
> +}
> +
> +void intel_flipq_reset(struct intel_display *display, enum pipe pipe) {
> +	struct intel_crtc *crtc = intel_crtc_for_pipe(display, pipe);
> +	enum intel_flipq_id flipq_id;
> +
> +	intel_de_write(display, PIPEDMC_FQ_CTRL(pipe), 0);
> +
> +	intel_de_write(display, PIPEDMC_SCANLINECMPLOWER(pipe), 0);
> +	intel_de_write(display, PIPEDMC_SCANLINECMPUPPER(pipe), 0);
> +
> +	for_each_flipq(flipq_id) {
> +		struct intel_flipq *flipq = &crtc->flipq[flipq_id];
> +
> +		intel_de_write(display, PIPEDMC_FPQ_HP(pipe, flipq_id), 0);
> +		intel_de_write(display, PIPEDMC_FPQ_CHP(pipe, flipq_id), 0);
> +
> +		flipq->tail = 0;
> +	}
> +
> +	intel_de_write(display, PIPEDMC_FPQ_ATOMIC_TP(pipe), 0); }
> +
> +static enum pipedmc_event_id flipq_event_id(struct intel_display
> +*display) {
> +	if (DISPLAY_VER(display) >= 30)
> +		return PIPEDMC_EVENT_FULL_FQ_WAKE_TRIGGER;
> +	else
> +		return PIPEDMC_EVENT_SCANLINE_INRANGE_FQ_TRIGGER;
> +}
> +
> +void intel_flipq_enable(const struct intel_crtc_state *crtc_state) {
> +	struct intel_display *display = to_intel_display(crtc_state);
> +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
> +	/* FIXME what to do with VRR? */
> +	int scanline = intel_mode_vblank_start(&crtc_state->hw.adjusted_mode) -
> +		intel_flipq_exec_time_lines(crtc_state);
> +
> +	if (DISPLAY_VER(display) >= 30) {
> +		u32 start_mmioaddr = intel_pipedmc_start_mmioaddr(crtc);
> +
> +		/* undocumented magic DMC variables */
> +		intel_de_write(display,
> PTL_PIPEDMC_EXEC_TIME_LINES(start_mmioaddr),
> +			       intel_flipq_exec_time_lines(crtc_state));
> +		intel_de_write(display,
> PTL_PIPEDMC_END_OF_EXEC_GB(start_mmioaddr),
> +			       100);
> +	}
> +
> +	intel_de_write(display, PIPEDMC_SCANLINECMPUPPER(crtc->pipe),
> +		       PIPEDMC_SCANLINE_UPPER(scanline));
> +	intel_de_write(display, PIPEDMC_SCANLINECMPLOWER(crtc->pipe),
> +		       PIPEDMC_SCANLINEINRANGECMP_EN |
> +		       PIPEDMC_SCANLINE_LOWER(scanline - 2));
> +
> +	intel_pipedmc_enable_event(crtc, flipq_event_id(display));
> +
> +	intel_de_write(display, PIPEDMC_FQ_CTRL(crtc->pipe),
> +PIPEDMC_FQ_CTRL_ENABLE); }
> +
> +void intel_flipq_disable(const struct intel_crtc_state *crtc_state) {
> +	struct intel_display *display = to_intel_display(crtc_state);
> +	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
> +
> +	intel_flipq_preempt(crtc, true);
> +
> +	intel_de_write(display, PIPEDMC_FQ_CTRL(crtc->pipe), 0);
> +
> +	intel_pipedmc_disable_event(crtc, flipq_event_id(display));
> +
> +	intel_de_write(display, PIPEDMC_SCANLINECMPLOWER(crtc->pipe), 0);
> +	intel_de_write(display, PIPEDMC_SCANLINECMPUPPER(crtc->pipe), 0);
> }
> +
> +static bool assert_flipq_has_room(struct intel_crtc *crtc,
> +				  enum intel_flipq_id flipq_id)
> +{
> +	struct intel_display *display = to_intel_display(crtc);
> +	struct intel_flipq *flipq = &crtc->flipq[flipq_id];
> +	int head, size = intel_flipq_size_entries(flipq_id);
> +
> +	head = intel_flipq_current_head(crtc, flipq_id);
> +
> +	return !drm_WARN(display->drm,
> +			 (flipq->tail + size - head) % size >= size - 1,
> +			 "[CRTC:%d:%s] FQ %d overflow (head %d, tail %d, size
> %d)\n",
> +			 crtc->base.base.id, crtc->base.name, flipq_id,
> +			 head, flipq->tail, size);
> +}
> +
> +static void intel_flipq_write(struct intel_display *display,
> +			      struct intel_flipq *flipq, u32 data, int i) {
> +	intel_de_write(display, PIPEDMC_FQ_RAM(flipq->start_mmioaddr, flipq-
> >tail *
> +					       intel_flipq_elem_size_dw(flipq-
> >flipq_id) + i), data); }
> +
> +static void lnl_flipq_add(struct intel_display *display,
> +			  struct intel_flipq *flipq,
> +			  unsigned int pts,
> +			  enum intel_dsb_id dsb_id,
> +			  struct intel_dsb *dsb)
> +{
> +	int i = 0;
> +
> +	switch (flipq->flipq_id) {
> +	case INTEL_FLIPQ_GENERAL:
> +		intel_flipq_write(display, flipq, pts, i++);
> +		intel_flipq_write(display, flipq, intel_dsb_head(dsb), i++);
> +		intel_flipq_write(display, flipq, LNL_FQ_INTERRUPT |
> +				  LNL_FQ_DSB_ID(dsb_id) |
> +				  LNL_FQ_DSB_SIZE(intel_dsb_size(dsb) / 64),
> i++);
> +		intel_flipq_write(display, flipq, 0, i++);
> +		intel_flipq_write(display, flipq, 0, i++); /* head for second DSB */
> +		intel_flipq_write(display, flipq, 0, i++); /* DSB engine + size for
> second DSB */
> +		break;
> +	case INTEL_FLIPQ_PLANE_1:
> +	case INTEL_FLIPQ_PLANE_2:
> +	case INTEL_FLIPQ_PLANE_3:
> +		intel_flipq_write(display, flipq, pts, i++);
> +		intel_flipq_write(display, flipq, intel_dsb_head(dsb), i++);
> +		intel_flipq_write(display, flipq, LNL_FQ_INTERRUPT |
> +				  LNL_FQ_DSB_ID(dsb_id) |
> +				  LNL_FQ_DSB_SIZE(intel_dsb_size(dsb) / 64),
> i++);
> +		intel_flipq_write(display, flipq, 0, i++);
> +		break;
> +	default:
> +		MISSING_CASE(flipq->flipq_id);
> +		return;
> +	}
> +}
> +
> +static void ptl_flipq_add(struct intel_display *display,
> +			  struct intel_flipq *flipq,
> +			  unsigned int pts,
> +			  enum intel_dsb_id dsb_id,
> +			  struct intel_dsb *dsb)
> +{
> +	int i = 0;
> +
> +	switch (flipq->flipq_id) {
> +	case INTEL_FLIPQ_GENERAL:
> +		intel_flipq_write(display, flipq, pts, i++);
> +		intel_flipq_write(display, flipq, 0, i++);
> +		intel_flipq_write(display, flipq, PTL_FQ_INTERRUPT |
> +				  PTL_FQ_DSB_ID(dsb_id) |
> +				  PTL_FQ_DSB_SIZE(intel_dsb_size(dsb) / 64),
> i++);
> +		intel_flipq_write(display, flipq, intel_dsb_head(dsb), i++);
> +		intel_flipq_write(display, flipq, 0, i++); /* DSB engine + size for
> second DSB */
> +		intel_flipq_write(display, flipq, 0, i++); /* head for second DSB */
> +		break;
> +	case INTEL_FLIPQ_PLANE_1:
> +	case INTEL_FLIPQ_PLANE_2:
> +	case INTEL_FLIPQ_PLANE_3:
> +		intel_flipq_write(display, flipq, pts, i++);
> +		intel_flipq_write(display, flipq, 0, i++);
> +		intel_flipq_write(display, flipq, PTL_FQ_INTERRUPT |
> +				  PTL_FQ_DSB_ID(dsb_id) |
> +				  PTL_FQ_DSB_SIZE(intel_dsb_size(dsb) / 64),
> i++);
> +		intel_flipq_write(display, flipq, intel_dsb_head(dsb), i++);
> +		break;
> +	default:
> +		MISSING_CASE(flipq->flipq_id);
> +		return;
> +	}
> +}
> +
> +void intel_flipq_add(struct intel_crtc *crtc,
> +		     enum intel_flipq_id flipq_id,
> +		     unsigned int pts,
> +		     enum intel_dsb_id dsb_id,
> +		     struct intel_dsb *dsb)
> +{
> +	struct intel_display *display = to_intel_display(crtc);
> +	struct intel_flipq *flipq = &crtc->flipq[flipq_id];
> +
> +	if (!assert_flipq_has_room(crtc, flipq_id))
> +		return;
> +
> +	pts += intel_de_read(display, PIPEDMC_FPQ_TS(crtc->pipe));
> +
> +	intel_flipq_preempt(crtc, true);
> +
> +	if (DISPLAY_VER(display) >= 30)
> +		ptl_flipq_add(display, flipq,  pts, dsb_id, dsb);
> +	else
> +		lnl_flipq_add(display, flipq,  pts, dsb_id, dsb);
> +
> +	flipq->tail = (flipq->tail + 1) % intel_flipq_size_entries(flipq->flipq_id);
> +	intel_flipq_write_tail(crtc);
> +
> +	intel_flipq_preempt(crtc, false);
> +
> +	intel_flipq_sw_dmc_wake(crtc);
> +}
> diff --git a/drivers/gpu/drm/i915/display/intel_flipq.h
> b/drivers/gpu/drm/i915/display/intel_flipq.h
> new file mode 100644
> index 000000000000..64d3c2a5bb7b
> --- /dev/null
> +++ b/drivers/gpu/drm/i915/display/intel_flipq.h
> @@ -0,0 +1,32 @@
> +/* SPDX-License-Identifier: MIT */
> +/*
> + * Copyright © 2025 Intel Corporation
> + */
> +
> +#ifndef __INTEL_FLIPQ_H__
> +#define __INTEL_FLIPQ_H__
> +
> +#include <linux/types.h>
> +
> +enum intel_dsb_id;
> +enum intel_flipq_id;
> +enum pipe;
> +struct intel_crtc;
> +struct intel_crtc_state;
> +struct intel_display;
> +struct intel_dsb;
> +
> +bool intel_flipq_supported(struct intel_display *display); void
> +intel_flipq_init(struct intel_display *display); void
> +intel_flipq_reset(struct intel_display *display, enum pipe pipe);
> +
> +void intel_flipq_enable(const struct intel_crtc_state *crtc_state);
> +void intel_flipq_disable(const struct intel_crtc_state
> +*old_crtc_state);
> +
> +void intel_flipq_add(struct intel_crtc *crtc,
> +		     enum intel_flipq_id flip_queue_id,
> +		     unsigned int pts,
> +		     enum intel_dsb_id dsb_id,
> +		     struct intel_dsb *dsb);
> +
> +#endif /* __INTEL_FLIPQ_H__ */
> diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index
> f5f5775acdc0..b3bd683b4267 100644
> --- a/drivers/gpu/drm/xe/Makefile
> +++ b/drivers/gpu/drm/xe/Makefile
> @@ -255,6 +255,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \
>  	i915-display/intel_fbc.o \
>  	i915-display/intel_fdi.o \
>  	i915-display/intel_fifo_underrun.o \
> +	i915-display/intel_flipq.o \
>  	i915-display/intel_frontbuffer.o \
>  	i915-display/intel_global_state.o \
>  	i915-display/intel_gmbus.o \
> --
> 2.49.0



More information about the Intel-xe mailing list