Commit 470022b5 authored by Ville Syrjälä's avatar Ville Syrjälä
Browse files

drm/i915/flipq: Provide the nuts and bolts code for flip queue



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
v5: Try to deal with some clang-20 div-by-zero false positive (Nathan)
    Add some docs (Jani)

Cc: Nathan Chancellor <nathan@kernel.org>
Reviewed-by: default avatarUma Shankar <uma.shankar@intel.com>
Signed-off-by: default avatarVille Syrjälä <ville.syrjala@linux.intel.com>

epr
Link: https://patchwork.freedesktop.org/patch/msgid/20250624170049.27284-5-ville.syrjala@linux.intel.com
parent 141b954c
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -204,6 +204,12 @@ DMC Firmware Support
.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dmc.c
   :internal:

DMC Flip Queue
--------------------

.. kernel-doc:: drivers/gpu/drm/i915/display/intel_flipq.c
   :doc: DMC Flip Queue

DMC wakelock support
--------------------

+1 −0
Original line number Diff line number Diff line
@@ -264,6 +264,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 \
+3 −0
Original line number Diff line number Diff line
@@ -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"
@@ -537,6 +538,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
+17 −0
Original line number Diff line number Diff line
@@ -1371,6 +1371,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;
@@ -1402,6 +1417,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  */
+51 −0
Original line number Diff line number Diff line
@@ -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"

/**
@@ -737,6 +739,8 @@ void intel_dmc_enable_pipe(const struct intel_crtc_state *crtc_state)
	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));
	}
@@ -765,6 +769,8 @@ void intel_dmc_disable_pipe(const struct intel_crtc_state *crtc_state)
	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);
	}
}

@@ -853,6 +859,13 @@ void intel_dmc_load_program(struct intel_display *display)
		assert_dmc_loaded(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);
@@ -1371,6 +1384,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
@@ -1606,3 +1630,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;
}
Loading