> -----Original Message----- > From: Intel-xe <intel-xe-boun...@lists.freedesktop.org> On Behalf Of Ville > Syrjala > Sent: Monday, June 9, 2025 7:41 PM > To: intel-gfx@lists.freedesktop.org > Cc: intel...@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.syrj...@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.shan...@intel.com> > Signed-off-by: Ville Syrjälä <ville.syrj...@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