Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL
@ 2026-01-15 16:54 Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 01/10] drm/i915/display: Implement passive initialization for splash screen preservation Juasheem Sultan
                   ` (10 more replies)
  0 siblings, 11 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

This RFC patch series proposes an implementation of seamless boot (fastboot)
support for Panther Lake (PTL) platforms within the Xe driver, mirroring
functionality already present in i915. The primary focus is on devices using
MSO (Multi-Segment Operation) panels and EDP panels.

The goal of this series is to achieve a flicker-free transition from the
bootloader (BIOS/UEFI) to the kernel driver by strictly adhering to the
hardware state established by the firmware.

Key areas addressed in this revision:
1.  **Boot State Preservation**: Where necessary, make amendments to initial
	hardware readouts and ensure that the framebuffer state as established
	by firmware is correctly passed into the driver as a memory object.
2.  **Atomic State Handoff**: Adjusting the atomic commit path to prevent
    unnecessary pipe disables or power well toggles when inheriting a valid
    boot state.
3.  **Passive Probing**: Introducing a "passive" initialization path that
    builds the DRM software state without triggering hardware resets.

This is an initial implementation subject to discussion. 

Change-Id: I5cd3bdd4f6f687f007e91f6d0afbfeecfc06762b
Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
Juasheem Sultan (10):
      drm/i915/display: Implement passive initialization for splash screen preservation
      drm/xe/display: Implement seamless boot state reconstruction for PTL
      drm/i915/display: Implement aggressive boot state preservation for PTL
      drm/xe/display: Fix initial plane reconstruction and stolen memory handling
      drm/i915/display: Enable early PLL readout and robustify modeset setup
      drm/i915/display: Fix vblank timestamps and update logic for fastboot
      drm/i915/display: Implement seamless fastboot for MSO panels on PTL
      drm/i915/display: Robustify fastboot and power init for seamless boot
      drm/i915/display: Fix initial plane config readout and MSO stride for PTL
      drm/i915/display: Refactor initial plane readout and harden boot handover

 drivers/gpu/drm/i915/display/intel_bios.c          |  40 +-
 drivers/gpu/drm/i915/display/intel_cdclk.c         |  36 -
 drivers/gpu/drm/i915/display/intel_cdclk.h         |  41 +-
 drivers/gpu/drm/i915/display/intel_connector.c     |   9 +
 drivers/gpu/drm/i915/display/intel_connector.h     |   1 +
 drivers/gpu/drm/i915/display/intel_crtc.c          |  62 +-
 drivers/gpu/drm/i915/display/intel_ddi.c           | 205 +++++-
 drivers/gpu/drm/i915/display/intel_ddi.h           |   2 +
 drivers/gpu/drm/i915/display/intel_display.c       | 814 +++++++++++++++++++--
 drivers/gpu/drm/i915/display/intel_display.h       |   6 +
 drivers/gpu/drm/i915/display/intel_display_core.h  |   2 +
 .../gpu/drm/i915/display/intel_display_driver.c    | 329 ++++++++-
 drivers/gpu/drm/i915/display/intel_display_power.c |  16 +-
 drivers/gpu/drm/i915/display/intel_display_types.h |   8 +
 drivers/gpu/drm/i915/display/intel_dp.c            | 372 ++++++++--
 drivers/gpu/drm/i915/display/intel_dp.h            |   5 +
 drivers/gpu/drm/i915/display/intel_dpll_mgr.c      |  16 +-
 drivers/gpu/drm/i915/display/intel_dpll_mgr.h      |   1 +
 drivers/gpu/drm/i915/display/intel_hdmi.c          |  39 +
 drivers/gpu/drm/i915/display/intel_hdmi.h          |   2 +
 drivers/gpu/drm/i915/display/intel_modeset_setup.c |   7 +
 .../gpu/drm/i915/display/intel_modeset_verify.c    |  14 +-
 drivers/gpu/drm/i915/display/intel_panel.c         |  41 +-
 drivers/gpu/drm/i915/display/intel_panel.h         |   3 +
 drivers/gpu/drm/i915/display/intel_plane_initial.c |  53 ++
 drivers/gpu/drm/i915/display/skl_universal_plane.c |  32 +-
 drivers/gpu/drm/xe/display/xe_display.c            | 309 +++++++-
 drivers/gpu/drm/xe/display/xe_display.h            |   2 +
 drivers/gpu/drm/xe/display/xe_plane_initial.c      |  28 +-
 drivers/gpu/drm/xe/xe_device.c                     |   2 +-
 drivers/gpu/drm/xe/xe_ggtt.h                       |   3 +
 drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c             |   2 +
 32 files changed, 2302 insertions(+), 200 deletions(-)
---
base-commit: 812e4b8966d421afd4df8f794bf15f1a1a3ec7b6
change-id: 20260115-upstream-prep-b29156970a75

Best regards,
-- 
Juasheem Sultan <jdsultan@google.com>


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH RFC 01/10] drm/i915/display: Implement passive initialization for splash screen preservation
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 02/10] drm/xe/display: Implement seamless boot state reconstruction for PTL Juasheem Sultan
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

To achieve a seamless transition from the bootloader to the kernel driver
(fastboot) on Panther Lake (PTL), especially with MSO panels, we need to
avoid resetting the hardware state during initialization.

Introduce a "passive" initialization path (`intel_ddi_init_encoder_early`)
that constructs the necessary DRM software objects (encoders, connectors)
to match the hardware state without issuing any hardware-resetting commands
(like DP link training or full modesets).

Key changes include:
1. Early encoder/connector initialization that skips GMBUS/AUX activity
   where unsafe.
2. MSO-specific fixes:
   * Hardcoded link parameters for 2256x1504 MSO mode.
   * Forced positive sync polarity (required by hardware/panel).
   * Manual handling of splitter state in `get_config`.
3. Improved DSC DPCD reading with retry logic to handle slow-to-wake panels.

This prevents the splash screen from flickering or disappearing during boot.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_connector.c |   9 +
 drivers/gpu/drm/i915/display/intel_connector.h |   1 +
 drivers/gpu/drm/i915/display/intel_ddi.c       | 205 ++++++++++++-
 drivers/gpu/drm/i915/display/intel_ddi.h       |   2 +
 drivers/gpu/drm/i915/display/intel_dp.c        | 385 ++++++++++++++++++++++---
 drivers/gpu/drm/i915/display/intel_dp.h        |   5 +
 drivers/gpu/drm/i915/display/intel_hdmi.c      |  39 +++
 drivers/gpu/drm/i915/display/intel_hdmi.h      |   2 +
 drivers/gpu/drm/i915/display/intel_panel.c     |  41 ++-
 drivers/gpu/drm/i915/display/intel_panel.h     |   3 +
 10 files changed, 639 insertions(+), 53 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_connector.c b/drivers/gpu/drm/i915/display/intel_connector.c
index 6a55854db5b68f33408ad6e8a24062293308d32c..b7189f840758e60b601870c78d5076316c5ce05e 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.c
+++ b/drivers/gpu/drm/i915/display/intel_connector.c
@@ -118,6 +118,15 @@ struct intel_connector *intel_connector_alloc(void)
 	return connector;
 }
 
+
+void intel_connector_destroy_early(struct drm_connector *connector)
+{
+	struct intel_connector *intel_connector = to_intel_connector(connector);
+
+	intel_panel_fini(intel_connector);
+	kfree(connector);
+}
+
 /*
  * Free the bits allocated by intel_connector_alloc.
  * This should only be used after intel_connector_alloc has returned
diff --git a/drivers/gpu/drm/i915/display/intel_connector.h b/drivers/gpu/drm/i915/display/intel_connector.h
index 0aa86626e6463327dd96fac7405eae0f3c0e1b83..ef1a18f78a9d8b9a151f877b54aa779b4e0ca5e4 100644
--- a/drivers/gpu/drm/i915/display/intel_connector.h
+++ b/drivers/gpu/drm/i915/display/intel_connector.h
@@ -17,6 +17,7 @@ struct intel_encoder;
 struct intel_connector *intel_connector_alloc(void);
 void intel_connector_free(struct intel_connector *connector);
 void intel_connector_destroy(struct drm_connector *connector);
+void intel_connector_destroy_early(struct drm_connector *connector);
 int intel_connector_register(struct drm_connector *connector);
 void intel_connector_unregister(struct drm_connector *connector);
 void intel_connector_attach_encoder(struct intel_connector *connector,
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c
index c09aa759f4d4f41b861ad0bb67882cb4c5140cc6..0fa6e0258d9108cafd3991f1d2d5de099ab6aef8 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.c
+++ b/drivers/gpu/drm/i915/display/intel_ddi.c
@@ -2499,8 +2499,15 @@ static void intel_ddi_mso_get_config(struct intel_encoder *encoder,
 	dss1 = intel_de_read(display, ICL_PIPE_DSS_CTL1(pipe));
 
 	pipe_config->splitter.enable = dss1 & SPLITTER_ENABLE;
-	if (!pipe_config->splitter.enable)
+	if (!pipe_config->splitter.enable) {
+		if (intel_is_boot_mso_pipe(pipe)) {
+			pipe_config->splitter.enable = true;
+			pipe_config->splitter.link_count = 2;
+			pipe_config->splitter.pixel_overlap = 0;
+			return;
+		}
 		return;
+	}
 
 	if (drm_WARN_ON(display->drm, !(intel_ddi_splitter_pipe_mask(display) & BIT(pipe)))) {
 		pipe_config->splitter.enable = false;
@@ -4251,6 +4258,9 @@ static void mtl_ddi_get_config(struct intel_encoder *encoder,
 		crtc_state->port_clock = intel_cx0pll_calc_port_clock(encoder, &crtc_state->dpll_hw_state.cx0pll);
 
 	intel_ddi_get_config(encoder, crtc_state);
+
+	/* FIX: Read out DSC state so we don't lose it during takeover */
+	intel_dsc_get_config(crtc_state);
 }
 
 static void dg2_ddi_get_config(struct intel_encoder *encoder,
@@ -4586,6 +4596,18 @@ static int intel_ddi_compute_config_late(struct intel_encoder *encoder,
 	return 0;
 }
 
+static void intel_ddi_encoder_destroy_early(struct drm_encoder *encoder)
+{
+	struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder));
+
+	/*
+	 * Our early encoders are not registered, so we must not call
+	 * drm_encoder_cleanup(), which would try to unregister them.
+	 */
+
+	kfree(dig_port);
+}
+
 static void intel_ddi_encoder_destroy(struct drm_encoder *encoder)
 {
 	struct intel_display *display = to_intel_display(encoder->dev);
@@ -4624,12 +4646,61 @@ static int intel_ddi_encoder_late_register(struct drm_encoder *_encoder)
 	return 0;
 }
 
+static const struct drm_encoder_funcs intel_ddi_funcs_early = {
+	.reset = intel_ddi_encoder_reset,
+	.destroy = intel_ddi_encoder_destroy_early,
+	.late_register = NULL,
+};
+
 static const struct drm_encoder_funcs intel_ddi_funcs = {
 	.reset = intel_ddi_encoder_reset,
 	.destroy = intel_ddi_encoder_destroy,
 	.late_register = intel_ddi_encoder_late_register,
 };
 
+static int intel_ddi_init_dp_connector_early(struct intel_digital_port *dig_port)
+{
+	struct intel_connector *connector;
+	struct intel_display *display = to_intel_display(dig_port->base.base.dev);
+	enum port port = dig_port->base.port;
+
+	connector = intel_connector_alloc();
+	if (!connector)
+		return -ENOMEM;
+
+	dig_port->dp.output_reg = DDI_BUF_CTL(port);
+	if (DISPLAY_VER(display) >= 14)
+		dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain;
+	else
+		dig_port->dp.prepare_link_retrain = intel_ddi_prepare_link_retrain;
+	dig_port->dp.set_link_train = intel_ddi_set_link_train;
+	dig_port->dp.set_idle_link_train = intel_ddi_set_idle_link_train;
+
+	dig_port->dp.voltage_max = intel_ddi_dp_voltage_max;
+	dig_port->dp.preemph_max = intel_ddi_dp_preemph_max;
+
+	if (!intel_dp_init_connector_early(dig_port, connector)) {
+		kfree(connector);
+		return -EINVAL;
+	}
+
+	if (dig_port->base.type == INTEL_OUTPUT_EDP) {
+		struct drm_device *dev = dig_port->base.base.dev;
+		struct drm_privacy_screen *privacy_screen;
+
+		privacy_screen = drm_privacy_screen_get(dev->dev, NULL);
+		if (!IS_ERR(privacy_screen)) {
+			drm_connector_attach_privacy_screen_provider(&connector->base,
+								     privacy_screen);
+		} else if (PTR_ERR(privacy_screen) != -ENODEV) {
+			drm_warn(dev, "Error getting privacy-screen\n");
+		}
+	}
+
+	return 0;
+}
+
+
 static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port)
 {
 	struct intel_display *display = to_intel_display(dig_port);
@@ -4834,6 +4905,24 @@ static bool bdw_digital_port_connected(struct intel_encoder *encoder)
 	return intel_de_read(display, GEN8_DE_PORT_ISR) & bit;
 }
 
+static int intel_ddi_init_hdmi_connector_early(struct intel_digital_port *dig_port)
+{
+	struct intel_connector *connector;
+	enum port port = dig_port->base.port;
+
+	connector = intel_connector_alloc();
+	if (!connector)
+		return -ENOMEM;
+
+	dig_port->hdmi.hdmi_reg = DDI_BUF_CTL(port);
+
+	/* In early init, we can't get a GMBUS adapter, so skip full init */
+	intel_hdmi_init_connector_early(dig_port, connector);
+
+	return 0;
+}
+
+
 static int intel_ddi_init_hdmi_connector(struct intel_digital_port *dig_port)
 {
 	struct intel_connector *connector;
@@ -5100,6 +5189,120 @@ static const char *intel_ddi_encoder_name(struct intel_display *display,
 	drm_WARN_ON(display->drm, seq_buf_has_overflowed(s));
 
 	return seq_buf_str(s);
+
+}
+
+void intel_ddi_init_encoder_early(struct intel_display *display,
+			const struct intel_bios_encoder_data *devdata)
+{
+	enum port port = intel_bios_encoder_port(devdata);
+	enum phy phy;
+	struct intel_digital_port *dig_port;
+	struct intel_encoder *encoder;
+	bool init_dp;
+
+
+	if (port_in_use(display, port)) {
+		return;
+	}
+
+	dig_port = kzalloc(sizeof(*dig_port), GFP_KERNEL);
+	if (!dig_port)
+		return;
+
+	encoder = &dig_port->base;
+	encoder->devdata = devdata;
+	encoder->get_hw_state = intel_ddi_get_hw_state;
+	encoder->port = port;
+	drm_encoder_init(display->drm, &encoder->base, &intel_ddi_funcs_early,
+					DRM_MODE_ENCODER_TMDS, "DDI %c", port_name(port));
+	dig_port->max_lanes = intel_ddi_max_lanes(dig_port);
+	encoder->base.possible_crtcs = (1 << INTEL_NUM_PIPES(display)) - 1;
+	intel_infoframe_init(dig_port);
+
+
+	phy = intel_port_to_phy(display, port);
+	init_dp = intel_bios_encoder_supports_dp(devdata);
+
+	if (intel_bios_encoder_is_lspcon(devdata)) {
+		/*
+		 * Lspcon device needs to be driven with DP connector
+		 * with special detection sequence. So make sure DP
+		 * is initialized before lspcon.
+		 */
+		init_dp = true;
+		drm_dbg_kms(display->drm, "VBT says port %c has lspcon\n",
+			    port_name(port));
+	}
+
+	if (intel_phy_is_snps(display, phy) &&
+	    display->snps.phy_failed_calibration & BIT(phy)) {
+		drm_dbg_kms(display->drm,
+			    "SNPS PHY %c failed to calibrate, proceeding anyway\n",
+			    phy_name(phy));
+	}
+
+	encoder->get_hw_state = intel_ddi_get_hw_state;
+
+	encoder->enable_clock = intel_mtl_pll_enable;
+	encoder->disable_clock = intel_mtl_pll_disable;
+	encoder->port_pll_type = intel_mtl_port_pll_type;
+	encoder->get_config = mtl_ddi_get_config;
+	encoder->set_signal_levels = intel_cx0_phy_set_signal_levels;
+	intel_ddi_buf_trans_init(encoder);
+	encoder->hpd_pin = xelpd_hpd_pin(display, port);
+
+	encoder->type = INTEL_OUTPUT_DDI;
+	encoder->power_domain = intel_display_power_ddi_lanes_domain(display, port);
+	dig_port->ddi_io_power_domain = intel_display_power_ddi_io_domain(display, port);
+	encoder->port = port;
+	encoder->initial_fastset_check = intel_ddi_initial_fastset_check;
+
+	if (intel_bios_encoder_supports_dp(devdata) ||
+			intel_bios_encoder_supports_edp(devdata)) {
+		dig_port->dp.output_reg = DDI_BUF_CTL(port);
+		dig_port->max_lanes = intel_bios_dp_max_lane_count(devdata);
+		dig_port->aux_ch = intel_bios_dp_aux_ch(devdata);
+	}
+
+
+	if (init_dp) {
+		if (intel_ddi_init_dp_connector_early(dig_port))
+
+		dig_port->hpd_pulse = intel_dp_hpd_pulse;
+
+		if (dig_port->dp.mso_link_count)
+			encoder->pipe_mask = intel_ddi_splitter_pipe_mask(display);
+	}
+	encoder->hotplug = intel_ddi_hotplug;
+	encoder->compute_output_type = intel_ddi_compute_output_type;
+	encoder->compute_config = intel_ddi_compute_config;
+	encoder->compute_config_late = intel_ddi_compute_config_late;
+	encoder->enable = intel_ddi_enable;
+	encoder->pre_pll_enable = intel_ddi_pre_pll_enable;
+	encoder->pre_enable = intel_ddi_pre_enable;
+	encoder->disable = intel_ddi_disable;
+	encoder->post_pll_disable = intel_ddi_post_pll_disable;
+	encoder->post_disable = intel_ddi_post_disable;
+	encoder->update_pipe = intel_ddi_update_pipe;
+	encoder->audio_enable = intel_audio_codec_enable;
+	encoder->audio_disable = intel_audio_codec_disable;
+	encoder->get_hw_state = intel_ddi_get_hw_state;
+	encoder->sync_state = intel_ddi_sync_state;
+	encoder->initial_fastset_check = intel_ddi_initial_fastset_check;
+	encoder->suspend = intel_ddi_encoder_suspend;
+	encoder->shutdown = intel_ddi_encoder_shutdown;
+	encoder->get_power_domains = intel_ddi_get_power_domains;
+	encoder->port = port;
+	encoder->cloneable = 0;
+	encoder->pipe_mask = ~0;
+
+	if (encoder->type != INTEL_OUTPUT_EDP && intel_bios_encoder_supports_hdmi(devdata)) {
+		if (intel_ddi_init_hdmi_connector_early(dig_port))
+			drm_err(display->drm, "Failed to initialize hdmi connector\n");
+	}
+
+	return;
 }
 
 void intel_ddi_init(struct intel_display *display,
diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h
index f6f511bb04314ca122df5cf69491b2ce828865be..d507eae289360517439473ff105fc43b944fa33a 100644
--- a/drivers/gpu/drm/i915/display/intel_ddi.h
+++ b/drivers/gpu/drm/i915/display/intel_ddi.h
@@ -56,6 +56,8 @@ void hsw_prepare_dp_ddi_buffers(struct intel_encoder *encoder,
 void intel_wait_ddi_buf_idle(struct intel_display *display, enum port port);
 void intel_ddi_init(struct intel_display *display,
 		    const struct intel_bios_encoder_data *devdata);
+void intel_ddi_init_encoder_early(struct intel_display *display,
+			const struct intel_bios_encoder_data *devdata);
 bool intel_ddi_get_hw_state(struct intel_encoder *encoder, enum pipe *pipe);
 void intel_ddi_config_transcoder_func(struct intel_encoder *encoder,
 				      const struct intel_crtc_state *crtc_state);
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 2eab591a8ef5681419e11b0fcc89c1a37e9d0db0..f73a7f9d2198b418cea06cc3c1636f09bb681dee 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -1416,13 +1416,20 @@ intel_dp_mode_valid(struct drm_connector *_connector,
 	struct intel_connector *connector = to_intel_connector(_connector);
 	struct intel_dp *intel_dp = intel_attached_dp(connector);
 	enum intel_output_format sink_format, output_format;
+	enum drm_mode_status status;
+
+	if (mode->hdisplay == 2256 && mode->vdisplay == 1504) {
+		/* Run a partial check to see what WOULD fail */
+		status = intel_cpu_transcoder_mode_valid(display, mode);
+		return MODE_OK;
+	}
+
 	const struct drm_display_mode *fixed_mode;
 	int target_clock = mode->clock;
 	int max_rate, mode_rate, max_lanes, max_link_clock;
 	int max_dotclk = display->cdclk.max_dotclk_freq;
 	u16 dsc_max_compressed_bpp = 0;
 	u8 dsc_slice_count = 0;
-	enum drm_mode_status status;
 	bool dsc = false;
 	int num_joined_pipes;
 
@@ -2660,13 +2667,16 @@ intel_dp_compute_link_config(struct intel_encoder *encoder,
 						     false,
 						     &limits);
 
+	/* HACK: Force max link settings for MSO panel */
+	limits.max_rate = 810000;
+	limits.max_lane_count = 4;
 	if (!dsc_needed) {
 		/*
 		 * Optimize for slow and wide for everything, because there are some
 		 * eDP 1.3 and 1.4 panels don't work well with fast and narrow.
 		 */
 		ret = intel_dp_compute_link_config_wide(intel_dp, pipe_config,
-							conn_state, &limits);
+						conn_state, &limits);
 		if (!ret && intel_dp_is_uhbr(pipe_config))
 			ret = intel_dp_mtp_tu_compute_config(intel_dp,
 							     pipe_config,
@@ -2711,9 +2721,8 @@ intel_dp_compute_link_config(struct intel_encoder *encoder,
 						pipe_config->port_clock,
 						pipe_config->lane_count));
 
-	return 0;
+	return ret;
 }
-
 bool intel_dp_limited_color_range(const struct intel_crtc_state *crtc_state,
 				  const struct drm_connector_state *conn_state)
 {
@@ -3267,6 +3276,12 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 	if (ret)
 		return ret;
 
+	/* [FB-FIX] Force 216000 link rate for MSO. Found 216000 in HW. Override compute result. */
+	if (pipe_config->hw.pipe_mode.hdisplay == 2256) {
+		pipe_config->lane_count = 4;
+		pipe_config->port_clock = 216000;
+	}
+
 	if ((intel_dp_is_edp(intel_dp) && fixed_mode) ||
 	    pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) {
 		ret = intel_pfit_compute_config(pipe_config, conn_state);
@@ -3299,9 +3314,14 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 		pipe_config->splitter.link_count = n;
 		pipe_config->splitter.pixel_overlap = overlap;
 
-		drm_dbg_kms(display->drm,
-			    "MSO link count %d, pixel overlap %d\n",
-			    n, overlap);
+		/*
+		 * [FB-FIX] Force Positive Sync for MSO panels.
+		 * If we fell through here (skipped restore_boot), we must ensure flags are correct.
+		 */
+		if ((adjusted_mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC |
+					     DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)) == 0) {
+			adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
+		}
 
 		adjusted_mode->crtc_hdisplay = adjusted_mode->crtc_hdisplay / n + overlap;
 		adjusted_mode->crtc_hblank_start = adjusted_mode->crtc_hblank_start / n + overlap;
@@ -3315,9 +3335,11 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 	intel_dp_audio_compute_config(encoder, pipe_config, conn_state);
 
 	if (!intel_dp_is_uhbr(pipe_config)) {
+		int mso_clock = adjusted_mode->crtc_clock;
+
 		intel_link_compute_m_n(link_bpp_x16,
 				       pipe_config->lane_count,
-				       adjusted_mode->crtc_clock,
+				       mso_clock,
 				       pipe_config->port_clock,
 				       intel_dp_bw_fec_overhead(pipe_config->fec_enable),
 				       &pipe_config->dp_m_n);
@@ -3743,11 +3765,8 @@ bool intel_dp_initial_fastset_check(struct intel_encoder *encoder,
 	 * Remove once we have readout for DSC.
 	 */
 	if (crtc_state->dsc.compression_enable) {
-		drm_dbg_kms(display->drm,
-			    "[ENCODER:%d:%s] Forcing full modeset due to DSC being enabled\n",
-			    encoder->base.base.id, encoder->base.name);
-		crtc_state->uapi.mode_changed = true;
-		fastset = false;
+		// crtc_state->uapi.mode_changed = true;
+		// fastset = false;
 	}
 
 	if (CAN_PANEL_REPLAY(intel_dp)) {
@@ -4156,17 +4175,34 @@ static bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp)
 static void intel_dp_read_dsc_dpcd(struct drm_dp_aux *aux,
 				   u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE])
 {
-	if (drm_dp_dpcd_read(aux, DP_DSC_SUPPORT, dsc_dpcd,
-			     DP_DSC_RECEIVER_CAP_SIZE) < 0) {
-		drm_err(aux->drm_dev,
-			"Failed to read DPCD register 0x%x\n",
-			DP_DSC_SUPPORT);
-		return;
-	}
+	int ret;
+	int i;
+
+	for (i = 0; i < 5; i++) {
+		if (i > 0)
+			usleep_range(1000, 2000);
+
+		ret = drm_dp_dpcd_read(aux, DP_DSC_SUPPORT, dsc_dpcd,
+			     DP_DSC_RECEIVER_CAP_SIZE);
+
+		/*
+		 * A successful read returning all zeros means the panel
+		 * is not ready yet. The first byte should have the DSC
+		 * version number.
+		 */
+		if (ret >= 0 && dsc_dpcd[0] != 0) {
+			drm_dbg_kms(aux->drm_dev,
+				    "DSC DPCD read successful after %d tries\n", i + 1);
+			drm_dbg_kms(aux->drm_dev, "DSC DPCD: %*ph\n",
+				    DP_DSC_RECEIVER_CAP_SIZE, dsc_dpcd);
+			return;
+		}
 
-	drm_dbg_kms(aux->drm_dev, "DSC DPCD: %*ph\n",
-		    DP_DSC_RECEIVER_CAP_SIZE,
-		    dsc_dpcd);
+		drm_dbg_kms(aux->drm_dev,
+			    "Failed to read DSC DPCD (ret=%d), retrying (%d/5)...\n",
+			    ret, i + 1);
+	}
+	/* Let the (likely zero) dsc_dpcd pass through; the caller will handle it. */
 }
 
 void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
@@ -4223,7 +4259,7 @@ intel_dp_detect_dsc_caps(struct intel_dp *intel_dp, struct intel_connector *conn
 					  connector);
 }
 
-static void intel_edp_mso_mode_fixup(struct intel_connector *connector,
+void intel_edp_mso_mode_fixup(struct intel_connector *connector,
 				     struct drm_display_mode *mode)
 {
 	struct intel_display *display = to_intel_display(connector);
@@ -4240,6 +4276,15 @@ static void intel_edp_mso_mode_fixup(struct intel_connector *connector,
 	mode->htotal = (mode->htotal - overlap) * n;
 	mode->clock *= n;
 
+	/*
+	 * HACK: If EDID doesn't specify sync polarity (Flags=0),
+	 * force Positive Sync (Flags=0xA) to match hardware expectation for MSO panels.
+	 */
+	if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC |
+			    DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)) == 0) {
+		mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
+	}
+
 	drm_mode_set_name(mode);
 
 	drm_dbg_kms(display->drm,
@@ -4275,42 +4320,59 @@ void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp)
 	}
 }
 
-static void intel_edp_mso_init(struct intel_dp *intel_dp)
+void intel_edp_mso_init(struct intel_dp *intel_dp)
 {
 	struct intel_display *display = to_intel_display(intel_dp);
 	struct intel_connector *connector = intel_dp->attached_connector;
 	struct drm_display_info *info = &connector->base.display_info;
-	u8 mso;
-
-	if (intel_dp->edp_dpcd[0] < DP_EDP_14)
-		return;
+	u8 mso_link_count = 0, mso_pixel_overlap = 0;
 
-	if (drm_dp_dpcd_readb(&intel_dp->aux, DP_EDP_MSO_LINK_CAPABILITIES, &mso) != 1) {
-		drm_err(display->drm, "Failed to read MSO cap\n");
+	if (intel_dp->edp_dpcd[0] < DP_EDP_14) {
+		drm_dbg_kms(display->drm,
+			    "[CONNECTOR:%d:%s] eDP < 1.4 not supported for MSO\n",
+			    connector->base.base.id, connector->base.name);
 		return;
 	}
 
-	/* Valid configurations are SST or MSO 2x1, 2x2, 4x1 */
-	mso &= DP_EDP_MSO_NUMBER_OF_LINKS_MASK;
-	if (mso % 2 || mso > drm_dp_max_lane_count(intel_dp->dpcd)) {
-		drm_err(display->drm, "Invalid MSO link count cap %u\n", mso);
-		mso = 0;
+	if (connector->panel.vbt.edp.mso_link_count) {
+		mso_link_count = connector->panel.vbt.edp.mso_link_count;
+		mso_pixel_overlap = connector->panel.vbt.edp.mso_pixel_overlap;
+
+		if (mso_link_count != 2 && mso_link_count != 4) {
+			drm_dbg_kms(display->drm,
+				    "[CONNECTOR:%d:%s] VBT has unsupported MSO link count %u\n",
+				    connector->base.base.id, connector->base.name,
+				    mso_link_count);
+			mso_link_count = 0;
+		}
+	} else if (info->mso_stream_count) {
+		mso_link_count = info->mso_stream_count;
+		mso_pixel_overlap = info->mso_pixel_overlap;
+
+		if (mso_link_count != 2 && mso_link_count != 4) {
+			drm_dbg_kms(display->drm,
+				    "[CONNECTOR:%d:%s] Sink has unsupported MSO link count %u\n",
+				    connector->base.base.id, connector->base.name,
+				    mso_link_count);
+			mso_link_count = 0;
+		}
 	}
 
-	if (mso) {
-		drm_dbg_kms(display->drm,
-			    "Sink MSO %ux%u configuration, pixel overlap %u\n",
-			    mso, drm_dp_max_lane_count(intel_dp->dpcd) / mso,
-			    info->mso_pixel_overlap);
-		if (!HAS_MSO(display)) {
-			drm_err(display->drm,
-				"No source MSO support, disabling\n");
-			mso = 0;
+	/*
+	 * To use MSO the panel must be VRR capable
+	 */
+	if (mso_link_count) {
+		if (!drm_dp_is_branch(intel_dp->dpcd) &&
+		    intel_dp->dpcd[DP_EDP_CONFIGURATION_CAP] == 0) {
+			drm_dbg_kms(display->drm,
+				    "[CONNECTOR:%d:%s] Sink doesn't support MSO\n",
+				    connector->base.base.id, connector->base.name);
+			mso_link_count = 0;
 		}
 	}
 
-	intel_dp->mso_link_count = mso;
-	intel_dp->mso_pixel_overlap = mso ? info->mso_pixel_overlap : 0;
+	intel_dp->mso_link_count = mso_link_count;
+	intel_dp->mso_pixel_overlap = mso_pixel_overlap;
 }
 
 static void
@@ -6043,8 +6105,9 @@ static int intel_dp_get_modes(struct drm_connector *_connector)
 	num_modes = drm_edid_connector_add_modes(&connector->base);
 
 	/* Also add fixed mode, which may or may not be present in EDID */
-	if (intel_dp_is_edp(intel_dp))
+	if (intel_dp_is_edp(intel_dp)) {
 		num_modes += intel_panel_get_modes(connector);
+	}
 
 	if (num_modes)
 		return num_modes;
@@ -6532,6 +6595,107 @@ static void intel_edp_backlight_setup(struct intel_dp *intel_dp,
 	intel_backlight_setup(connector, pipe);
 }
 
+static bool intel_edp_init_connector_early(struct intel_dp *intel_dp,
+				     struct intel_connector *connector)
+{
+	struct intel_display *display = to_intel_display(intel_dp);
+	struct drm_display_mode *fixed_mode;
+	struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base;
+	//bool has_dpcd;
+	const struct drm_edid *drm_edid = NULL;
+
+	if (!intel_dp_is_edp(intel_dp))
+		return true;
+
+	/*
+	 * On IBX/CPT we may get here with LVDS already registered. Since the
+	 * driver uses the only internal power sequencer available for both
+	 * eDP and LVDS bail out early in this case to prevent interfering
+	 * with an already powered-on LVDS power sequencer.
+	 */
+	if (intel_get_lvds_encoder(display)) {
+		drm_WARN_ON(display->drm,
+			    !(HAS_PCH_IBX(display) || HAS_PCH_CPT(display)));
+
+		return false;
+	}
+	intel_bios_init_panel_early(display, &connector->panel,
+				    encoder->devdata);
+
+	/*
+	 * VBT and straps are liars. Also check HPD as that seems
+	 * to be the most reliable piece of information available.
+	 *
+	 * ... expect on devices that forgot to hook HPD up for eDP
+	 * (eg. Acer Chromebook C710), so we'll check it only if multiple
+	 * ports are attempting to use the same AUX CH, according to VBT.
+	 */
+	if (intel_bios_dp_has_shared_aux_ch(encoder->devdata)) {
+		/*
+		 * If this fails, presume the DPCD answer came
+		 * from some other port using the same AUX CH.
+		 *
+		 * FIXME maybe cleaner to check this before the
+		 * DPCD read? Would need sort out the VDD handling...
+		 */
+		if (!intel_digital_port_connected(encoder)) {
+			goto out_vdd_off;
+		}
+
+		/*
+		 * Unfortunately even the HPD based detection fails on
+		 * eg. Asus B360M-A (CFL+CNP), so as a last resort fall
+		 * back to checking for a VGA branch device. Only do this
+		 * on known affected platforms to minimize false positives.
+		 */
+		if (DISPLAY_VER(display) == 9 && drm_dp_is_branch(intel_dp->dpcd) &&
+		    (intel_dp->dpcd[DP_DOWNSTREAMPORT_PRESENT] & DP_DWN_STRM_PORT_TYPE_MASK) ==
+		    DP_DWN_STRM_PORT_TYPE_ANALOG) {
+			goto out_vdd_off;
+		}
+	}
+	mutex_lock(&display->drm->mode_config.mutex);
+
+	intel_bios_init_panel_late(display, &connector->panel, encoder->devdata,
+				   IS_ERR(drm_edid) ? NULL : drm_edid);
+
+	intel_panel_add_edid_fixed_modes(connector, true);
+
+	/* MSO requires information from the EDID */
+	intel_edp_mso_init(intel_dp);
+
+	/* multiply the mode clock and horizontal timings for MSO */
+	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head)
+		intel_edp_mso_mode_fixup(connector, fixed_mode);
+
+	/* fallback to VBT if available for eDP */
+	if (!intel_panel_preferred_fixed_mode(connector))
+		intel_panel_add_vbt_lfp_fixed_mode(connector);
+
+	mutex_unlock(&display->drm->mode_config.mutex);
+
+	if (!intel_panel_preferred_fixed_mode(connector)) {
+		goto out_vdd_off;
+	}
+	intel_panel_init_early(connector, drm_edid);
+
+	//intel_edp_backlight_setup(intel_dp, connector);
+
+	//intel_edp_add_properties(intel_dp);
+
+	//intel_pps_init_late(intel_dp);
+
+	return true;
+
+out_vdd_off:
+	//intel_pps_vdd_off_sync(intel_dp);
+	intel_bios_fini_panel(&connector->panel);
+
+	return false;
+}
+
+
+
 static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 				     struct intel_connector *connector)
 {
@@ -6698,6 +6862,131 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp,
 	return false;
 }
 
+static void intel_dp_modeset_retry_work_fn(struct work_struct *work)
+{
+	struct intel_connector *connector = container_of(work, typeof(*connector),
+							 modeset_retry_work);
+	struct intel_display *display = to_intel_display(connector);
+
+	drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s]\n", connector->base.base.id,
+		    connector->base.name);
+
+	/* Grab the locks before changing connector property*/
+	mutex_lock(&display->drm->mode_config.mutex);
+	/* Set connector link status to BAD and send a Uevent to notify
+	 * userspace to do a modeset.
+	 */
+	drm_connector_set_link_status_property(&connector->base,
+					       DRM_MODE_LINK_STATUS_BAD);
+	mutex_unlock(&display->drm->mode_config.mutex);
+	/* Send Hotplug uevent so userspace can reprobe */
+	drm_kms_helper_connector_hotplug_event(&connector->base);
+
+	drm_connector_put(&connector->base);
+}
+
+void intel_dp_init_modeset_retry_work(struct intel_connector *connector)
+{
+	INIT_WORK(&connector->modeset_retry_work,
+		  intel_dp_modeset_retry_work_fn);
+}
+
+static void intel_dp_connector_reset_early(struct drm_connector *connector)
+{
+	drm_dbg_kms(to_intel_display(connector->dev)->drm,
+			"[CONNECTOR:%d:%s] skipping reset to preserve BIOS state\n",
+			connector->base.id, connector->name);
+}
+
+static const struct drm_connector_funcs intel_dp_connector_funcs_early = {
+	.reset = intel_dp_connector_reset_early,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.destroy = intel_connector_destroy_early,
+	.atomic_duplicate_state = intel_digital_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_set_property = intel_digital_connector_atomic_set_property,
+	.atomic_get_property = intel_digital_connector_atomic_get_property,
+	.late_register = intel_dp_connector_register,
+	.early_unregister = NULL,
+};
+
+
+bool
+
+intel_dp_init_connector_early(struct intel_digital_port *dig_port,
+
+			      struct intel_connector *connector)
+
+{
+	struct intel_dp *intel_dp = &dig_port->dp;
+	struct intel_encoder *encoder = &dig_port->base;
+	struct drm_device *dev = encoder->base.dev;
+	enum port port = encoder->port;
+	int type;
+	intel_dp->attached_connector = connector;
+
+	if (_intel_dp_is_port_edp(to_intel_display(dev), encoder->devdata, port)) {
+		type = DRM_MODE_CONNECTOR_eDP;
+		encoder->type = INTEL_OUTPUT_EDP;
+	} else {
+		type = DRM_MODE_CONNECTOR_DisplayPort;
+	}
+	/* This is the bare minimum: create the software object and link it. */
+	drm_connector_init(dev, &connector->base, &intel_dp_connector_funcs_early,
+			       type);
+	drm_connector_helper_add(&connector->base, &intel_dp_connector_helper_funcs);
+	/*
+	 * Do not assign the DDC adapter here. The AUX channel is not
+	 * fully initialized at this early stage, and attempting to
+	 * create the sysfs link for the DDC will fail, causing the
+	 * entire driver probe to abort. The DDC will be assigned
+	 * later during the full connector initialization.
+	 */
+
+	if (encoder->type == INTEL_OUTPUT_EDP) {
+		intel_dp_set_default_sink_rates(intel_dp);
+		intel_dp_set_default_max_sink_lane_count(intel_dp);
+		intel_dp_set_source_rates(intel_dp);
+		intel_dp_set_common_rates(intel_dp);
+		intel_dp_reset_link_params(intel_dp);
+	}
+	intel_connector_attach_encoder(connector, encoder);
+
+	connector->get_hw_state = intel_ddi_connector_get_hw_state;
+
+	/*
+	 * Skip TC port init during early probe. The full TC port init
+	 * depends on other parts of the display driver that are not yet
+	 * initialized, and attempting it here causes a kernel panic.
+	 * The TC port will be fully initialized later.
+	 */
+	/* if (!intel_dp_early_tc_port_init(dig_port)) {
+	   drm_connector_cleanup(&connector->base);
+	   return false;
+	   } */
+	struct intel_display *display = to_intel_display(dig_port->base.base.dev);
+
+	dig_port->ddi_power_wakeref = intel_display_power_get(display,
+			intel_display_power_ddi_lanes_domain(display, port));
+	intel_dp_aux_init(intel_dp);
+	connector->dp.dsc_decompression_aux = &intel_dp->aux;
+
+	/*
+	 * Call our minimal, passive eDP init function, butdo nothing else.
+	 * All othercalls (set_rates, psr_init, hdcp_init, etc.)
+	 * must be removed.
+	 */
+
+	if (!intel_edp_init_connector_early(intel_dp, connector)) {
+		intel_dp_aux_fini(intel_dp);
+		drm_connector_cleanup(&connector->base);
+		return false;
+
+	}
+
+	return true;
+}
+
 bool
 intel_dp_init_connector(struct intel_digital_port *dig_port,
 			struct intel_connector *connector)
diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h
index f90cfd1dbbd058ef0c2a13a391a6851faedb1683..c4bff87747ca22d484e66aeefa5f317e75fa566e 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.h
+++ b/drivers/gpu/drm/i915/display/intel_dp.h
@@ -47,6 +47,8 @@ intel_dp_queue_modeset_retry_for_link(struct intel_atomic_state *state,
 				      const struct intel_crtc_state *crtc_state);
 bool intel_dp_init_connector(struct intel_digital_port *dig_port,
 			     struct intel_connector *intel_connector);
+bool intel_dp_init_connector_early(struct intel_digital_port *dig_port,
+			     struct intel_connector *intel_connector);
 void intel_dp_connector_sync_state(struct intel_connector *connector,
 				   const struct intel_crtc_state *crtc_state);
 void intel_dp_set_link_params(struct intel_dp *intel_dp,
@@ -215,5 +217,8 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state,
 int intel_dp_dsc_bpp_step_x16(const struct intel_connector *connector);
 void intel_dp_dpcd_set_probe(struct intel_dp *intel_dp, bool force_on_external);
 bool intel_dp_in_hdr_mode(const struct drm_connector_state *conn_state);
+void intel_edp_mso_init(struct intel_dp *intel_dp);
+void intel_edp_mso_mode_fixup(struct intel_connector *connector,
+			      struct drm_display_mode *mode);
 
 #endif /* __INTEL_DP_H__ */
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c
index 4ab7e2e3bfd42c9ff77770a9759a28080d198483..4f0d5b85253cf2500dea05a4735dfdb9b800b789 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.c
@@ -3041,6 +3041,45 @@ void intel_infoframe_init(struct intel_digital_port *dig_port)
 	}
 }
 
+static void intel_hdmi_connector_reset_early(struct drm_connector *connector)
+{
+	drm_dbg_kms(to_intel_display(connector->dev)->drm,
+			"[CONNECTOR:%d:%s] skipping reset to preserve BIOS state\n",
+			connector->base.id, connector->name);
+}
+
+static const struct drm_connector_funcs intel_hdmi_connector_funcs_early = {
+	.reset = intel_hdmi_connector_reset_early,
+	.destroy = intel_connector_destroy_early,
+	.fill_modes = drm_helper_probe_single_connector_modes,
+	.atomic_duplicate_state = intel_digital_connector_duplicate_state,
+	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
+	.atomic_set_property = intel_digital_connector_atomic_set_property,
+	.atomic_get_property = intel_digital_connector_atomic_get_property,
+};
+
+bool intel_hdmi_init_connector_early(struct intel_digital_port *dig_port,
+					struct intel_connector *intel_connector)
+{
+	struct drm_device *dev = dig_port->base.base.dev;
+	struct drm_connector *connector = &intel_connector->base;
+	struct intel_encoder *intel_encoder = &dig_port->base;
+
+	drm_connector_init(dev, connector, &intel_hdmi_connector_funcs_early,
+		    DRM_MODE_CONNECTOR_HDMIA);
+	drm_connector_helper_add(connector, &intel_hdmi_connector_helper_funcs);
+
+	/* For early init, we only create the software objects.
+	 * Hardware state callbacks and other active setup are deferred.
+	*/
+	intel_connector_attach_encoder(intel_connector, intel_encoder);
+
+
+	return true;
+
+}
+
+
 bool intel_hdmi_init_connector(struct intel_digital_port *dig_port,
 			       struct intel_connector *intel_connector)
 {
diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h
index dec2ad7dd8a229d1035b974b4f989d25ffc5ae59..c39f825c320b423b3a94f2919413f1e2d8551711 100644
--- a/drivers/gpu/drm/i915/display/intel_hdmi.h
+++ b/drivers/gpu/drm/i915/display/intel_hdmi.h
@@ -23,6 +23,8 @@ union hdmi_infoframe;
 
 bool intel_hdmi_init_connector(struct intel_digital_port *dig_port,
 			       struct intel_connector *intel_connector);
+bool intel_hdmi_init_connector_early(struct intel_digital_port *dig_port,
+			       struct intel_connector *intel_connector);
 bool intel_hdmi_compute_has_hdmi_sink(struct intel_encoder *encoder,
 				      const struct intel_crtc_state *crtc_state,
 				      const struct drm_connector_state *conn_state);
diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c
index 2a20aaaaac39b788759d604976ebee3635205cea..c677d7908c1961c64e591aee07b04c20deda78b1 100644
--- a/drivers/gpu/drm/i915/display/intel_panel.c
+++ b/drivers/gpu/drm/i915/display/intel_panel.c
@@ -87,13 +87,26 @@ intel_panel_fixed_mode(struct intel_connector *connector,
 		       const struct drm_display_mode *mode)
 {
 	const struct drm_display_mode *fixed_mode, *best_mode = NULL;
-	int vrefresh = drm_mode_vrefresh(mode);
+	int vrefresh = 0;
+
+	if (list_empty(&connector->panel.fixed_modes)) {
+		return NULL;
+	}
+
+	if (mode)
+		vrefresh = drm_mode_vrefresh(mode);
 
 	list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
-		int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
+		int fixed_mode_vrefresh;
+
+		if (mode && fixed_mode->hdisplay < mode->hdisplay)
+			continue;
+		if (mode && fixed_mode->vdisplay < mode->vdisplay)
+			continue;
 
-		if (is_best_fixed_mode(connector, vrefresh,
-				       fixed_mode_vrefresh, best_mode))
+		fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode);
+
+		if (is_best_fixed_mode(connector, vrefresh, fixed_mode_vrefresh, best_mode))
 			best_mode = fixed_mode;
 	}
 
@@ -425,6 +438,26 @@ void intel_panel_init_alloc(struct intel_connector *connector)
 	INIT_LIST_HEAD(&panel->fixed_modes);
 }
 
+int intel_panel_init_early(struct intel_connector *connector,
+		     const struct drm_edid *fixed_edid)
+{
+	//struct intel_panel *panel = &connector->panel;
+
+	//panel->fixed_edid = fixed_edid;
+
+	//intel_backlight_init_funcs(panel);
+
+	if (!has_drrs_modes(connector))
+		connector->panel.vbt.drrs_type = DRRS_TYPE_NONE;
+
+	drm_dbg_kms(connector->base.dev,
+		    "[CONNECTOR:%d:%s] DRRS type: %s\n",
+		    connector->base.base.id, connector->base.name,
+		    intel_drrs_type_str(intel_panel_drrs_type(connector)));
+
+	return 0;
+}
+
 int intel_panel_init(struct intel_connector *connector,
 		     const struct drm_edid *fixed_edid)
 {
diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h
index 56a6412cf0fb1cff3be84b04b22d6673a0bf46f1..3fa90d978d9a541b694f5c70303eb2e637e80a46 100644
--- a/drivers/gpu/drm/i915/display/intel_panel.h
+++ b/drivers/gpu/drm/i915/display/intel_panel.h
@@ -22,6 +22,9 @@ struct intel_encoder;
 void intel_panel_init_alloc(struct intel_connector *connector);
 int intel_panel_init(struct intel_connector *connector,
 		     const struct drm_edid *fixed_edid);
+int intel_panel_init_early(struct intel_connector *connector,
+		     const struct drm_edid *fixed_edid);
+
 void intel_panel_fini(struct intel_connector *connector);
 int intel_panel_register(struct intel_connector *connector);
 void intel_panel_unregister(struct intel_connector *connector);

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 02/10] drm/xe/display: Implement seamless boot state reconstruction for PTL
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 01/10] drm/i915/display: Implement passive initialization for splash screen preservation Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 03/10] drm/i915/display: Implement aggressive boot state preservation " Juasheem Sultan
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

To achieve a flicker-free transition from bootloader to kernel (fastboot)
on Panther Lake (PTL), the driver must accurately reconstruct the
hardware state established by the BIOS before the first atomic commit.

Currently, the Xe driver initialization sequence may reset hardware or
fail to populate essential state (like MSO configurations or DP link
rates), leading to a blank screen or visual artifacts during boot.

This patch introduces a comprehensive state reconstruction path:

1.  **Passive Encoder Setup**: Adds `intel_setup_encoders` to discover
    ports and create encoder objects based on VBT data without triggering
    disruptive probing sequences.

2.  **BIOS State Readout**:
    *   Implements `xe_initial_pll_enable` to identify active pipes
        and ensure PLLs, PPS, and VDD are correctly tracked in software.
    *   Reconstructs `intel_dp` state (link rates, lane counts) using
        hardware readout, falling back to VBT values if DPCD reads fail
        (common during early init).

3.  **MSO & VBT Fixes**:
    *   Parses EDID early to fix up MSO (Multi-Segment Operation)
        timings and flags.
    *   Adds overrides for incorrect VBT link rate reports on PTL.
    *   Forces MSO configuration if detection fails but is required by
        the platform.

4.  **Power Management**:
    *   Grabs a persistent `POWER_DOMAIN_INIT` reference in
        `xe_display_register` to prevent the hardware from power-gating
        active pipes when the initial wakerefs are dropped.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_bios.c         |  40 ++-
 drivers/gpu/drm/i915/display/intel_cdclk.c        |  36 ---
 drivers/gpu/drm/i915/display/intel_cdclk.h        |  41 ++-
 drivers/gpu/drm/i915/display/intel_display.h      |   4 +
 drivers/gpu/drm/i915/display/intel_display_core.h |   2 +
 drivers/gpu/drm/xe/display/xe_display.c           | 309 +++++++++++++++++++++-
 drivers/gpu/drm/xe/display/xe_display.h           |   2 +
 drivers/gpu/drm/xe/xe_device.c                    |   2 +-
 8 files changed, 383 insertions(+), 53 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c
index 3596dce84c2863b85dd7c3e3baf0364e560b2777..fc6291e6b3ea6715283fc1cff80a175fdda34045 100644
--- a/drivers/gpu/drm/i915/display/intel_bios.c
+++ b/drivers/gpu/drm/i915/display/intel_bios.c
@@ -43,6 +43,8 @@
 #include "intel_display_types.h"
 #include "intel_gmbus.h"
 
+#include "intel_ddi.h"
+
 #define _INTEL_BIOS_PRIVATE
 #include "intel_vbt_defs.h"
 
@@ -1540,10 +1542,22 @@ parse_edp(struct intel_display *display,
 	panel->vbt.edp.drrs_msa_timing_delay =
 		panel_bits(edp->sdrrs_msa_timing_delay, panel_type, 2);
 
-	if (display->vbt.version >= 244)
-		panel->vbt.edp.max_link_rate =
-			edp->edp_max_port_link_rate[panel_type] * 20;
+	if (display->vbt.version >= 244) {
+	u8 edp_max_port_link_rate =
+	    edp->edp_max_port_link_rate[panel_type];
 
+	if (display->vbt.version >= 263) {
+			panel->vbt.edp.max_link_rate =
+		(edp_max_port_link_rate & 0x7) * 20;
+	} else {
+	    /*
+	     * HACK: The VBT is reporting a lower max link rate than supported.
+	     * Override to HBR3 (8.1 Gbps) as per direct VBT hexdump analysis.
+	     */
+	    panel->vbt.edp.max_link_rate = 810000; /* Force HBR3 (8.1 Gbps) */
+	}
+
+	}
 	if (display->vbt.version >= 251)
 		panel->vbt.edp.dsc_disable =
 			panel_bool(edp->edp_dsc_disable, panel_type);
@@ -3778,3 +3792,23 @@ void intel_bios_debugfs_register(struct intel_display *display)
 	debugfs_create_file("i915_vbt", 0444, display->drm->debugfs_root,
 			    display, &intel_bios_vbt_fops);
 }
+
+/**
+* intel setup_encoders - minimally initialize encoders to identify ports
+* @dev_priv: i915 device instance
+*
+* This functions is a stripped-down version of intel_setup_outputs(). Its sole purpose is to iterate over the VBT chidl devices to discover which physical prots exist and initialize the corresponding intel_encoder software objects.
+*
+* This is critical for the flicker-free boot path, where we need to identify
+* the active encoder by its port number before a full modeset has occurred.
+* This function populates the required encoder->port fields without creating
+* connectors or triggering a full output probe, which would cause flicker.
+*/
+void intel_setup_encoders(struct intel_display *display)
+{
+	if (HAS_DDI(display)) {
+		intel_bios_for_each_encoder(display, intel_ddi_init_encoder_early);
+	} else {
+		drm_dbg_kms(display->drm, "Non-DDI platform in minimal encoder setup, may need more\n");
+	}
+}
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c
index 9725eebe5706a8a737d3b8d3f1de65f0e7455343..61ec5d8b14e24e9edb5fb1e2b84d0c8d1519133e 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.c
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.c
@@ -116,42 +116,6 @@
  * dividers can be programmed correctly.
  */
 
-struct intel_cdclk_state {
-	struct intel_global_state base;
-
-	/*
-	 * Logical configuration of cdclk (used for all scaling,
-	 * watermark, etc. calculations and checks). This is
-	 * computed as if all enabled crtcs were active.
-	 */
-	struct intel_cdclk_config logical;
-
-	/*
-	 * Actual configuration of cdclk, can be different from the
-	 * logical configuration only when all crtc's are DPMS off.
-	 */
-	struct intel_cdclk_config actual;
-
-	/* minimum acceptable cdclk to satisfy bandwidth requirements */
-	int bw_min_cdclk;
-	/* minimum acceptable cdclk for each pipe */
-	int min_cdclk[I915_MAX_PIPES];
-	/* minimum acceptable voltage level for each pipe */
-	u8 min_voltage_level[I915_MAX_PIPES];
-
-	/* pipe to which cd2x update is synchronized */
-	enum pipe pipe;
-
-	/* forced minimum cdclk for glk+ audio w/a */
-	int force_min_cdclk;
-
-	/* bitmask of active pipes */
-	u8 active_pipes;
-
-	/* update cdclk with pipes disabled */
-	bool disable_pipes;
-};
-
 struct intel_cdclk_funcs {
 	void (*get_cdclk)(struct intel_display *display,
 			  struct intel_cdclk_config *cdclk_config);
diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.h b/drivers/gpu/drm/i915/display/intel_cdclk.h
index cacee598af0e2a8d0c379b011f65dcc610a7b9d7..07f8b184b5fef1b79959fd4257b1aeb94c81c524 100644
--- a/drivers/gpu/drm/i915/display/intel_cdclk.h
+++ b/drivers/gpu/drm/i915/display/intel_cdclk.h
@@ -8,9 +8,10 @@
 
 #include <linux/types.h>
 
-enum pipe;
+#include "intel_display_limits.h"
+#include "intel_global_state.h"
+
 struct intel_atomic_state;
-struct intel_cdclk_state;
 struct intel_crtc;
 struct intel_crtc_state;
 struct intel_display;
@@ -22,6 +23,42 @@ struct intel_cdclk_config {
 	bool joined_mbus;
 };
 
+struct intel_cdclk_state {
+	struct intel_global_state base;
+
+	/*
+	 * Logical configuration of cdclk (used for all scaling,
+	 * watermark, etc. calculations and checks). This is
+	 * computed as if all enabled crtcs were active.
+	 */
+	struct intel_cdclk_config logical;
+
+	/*
+	 * Actual configuration of cdclk, can be different from the
+	 * logical configuration only when all crtc's are DPMS off.
+	 */
+	struct intel_cdclk_config actual;
+
+	/* minimum acceptable cdclk to satisfy bandwidth requirements */
+	int bw_min_cdclk;
+	/* minimum acceptable cdclk for each pipe */
+	int min_cdclk[I915_MAX_PIPES];
+	/* minimum acceptable voltage level for each pipe */
+	u8 min_voltage_level[I915_MAX_PIPES];
+
+	/* pipe to which cd2x update is synchronized */
+	enum pipe pipe;
+
+	/* forced minimum cdclk for glk+ audio w/a */
+	int force_min_cdclk;
+
+	/* bitmask of active pipes */
+	u8 active_pipes;
+
+	/* update cdclk with pipes disabled */
+	bool disable_pipes;
+};
+
 void intel_cdclk_init_hw(struct intel_display *display);
 void intel_cdclk_uninit_hw(struct intel_display *display);
 void intel_init_cdclk_hooks(struct intel_display *display);
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 37e2ab301a80ef123bbdb4af9d4258b8fb6880ce..20e7ee02d57c28e1b4a67faecc3e82acc7d8cd02 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -529,6 +529,10 @@ void intel_setup_outputs(struct intel_display *display);
 int intel_initial_commit(struct intel_display *display);
 void intel_panel_sanitize_ssc(struct intel_display *display);
 void intel_update_czclk(struct intel_display *display);
+void intel_setup_encoders(struct intel_display *display);
+void intel_panel_sanitize_ssc(struct drm_i915_private *i915);
+void intel_update_czclk(struct drm_i915_private *i915);
+void intel_atomic_helper_free_state_worker(struct work_struct *work);
 enum drm_mode_status intel_mode_valid(struct drm_device *dev,
 				      const struct drm_display_mode *mode);
 int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *_state,
diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h
index 8c226406c5cd0a47bd5cd9ddf668e0825103c06f..becdee5109e20d0eb6d1bf80a6a021e25ad7fc1b 100644
--- a/drivers/gpu/drm/i915/display/intel_display_core.h
+++ b/drivers/gpu/drm/i915/display/intel_display_core.h
@@ -74,6 +74,8 @@ struct intel_display_funcs {
 	void (*crtc_disable)(struct intel_atomic_state *state,
 			     struct intel_crtc *crtc);
 	void (*commit_modeset_enables)(struct intel_atomic_state *state);
+	void (*post_crtc_init_hook)(struct intel_display *display);
+	void (*initial_plane_config)(struct intel_display *display);
 };
 
 /* functions used for watermark calcs for display. */
diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c
index 19e691fccf8ce8866cb2ea4e2139a8e5cb3e8bc9..8950880618c2314d62477437d069359cd835bd47 100644
--- a/drivers/gpu/drm/xe/display/xe_display.c
+++ b/drivers/gpu/drm/xe/display/xe_display.c
@@ -13,17 +13,25 @@
 #include <drm/drm_drv.h>
 #include <drm/drm_managed.h>
 #include <drm/drm_probe_helper.h>
+#include <drm/drm_atomic.h>
+#include <drm/drm_modeset_lock.h>
+#include <drm/drm_edid.h>
 #include <uapi/drm/xe_drm.h>
 
 #include "soc/intel_dram.h"
 #include "intel_acpi.h"
 #include "intel_audio.h"
 #include "intel_bw.h"
+#include "intel_cdclk.h"
 #include "intel_display.h"
 #include "intel_display_device.h"
 #include "intel_display_driver.h"
 #include "intel_display_irq.h"
 #include "intel_display_types.h"
+#include "intel_display_limits.h"
+#include "intel_crtc.h"
+#include "intel_de.h"
+#include "skl_universal_plane_regs.h"
 #include "intel_dmc.h"
 #include "intel_dmc_wl.h"
 #include "intel_dp.h"
@@ -32,7 +40,14 @@
 #include "intel_hdcp.h"
 #include "intel_hotplug.h"
 #include "intel_opregion.h"
+#include "intel_pch.h"
 #include "skl_watermark.h"
+#include "intel_plane_initial.h"
+#include "intel_pps.h"
+#include "intel_backlight.h"
+#include "intel_panel.h"
+#include "intel_cx0_phy.h"
+#include "intel_cx0_phy_regs.h"
 #include "xe_module.h"
 
 /* Xe device functions */
@@ -81,7 +96,7 @@ static void unset_display_features(struct xe_device *xe)
 	xe->drm.driver_features &= ~(DRIVER_MODESET | DRIVER_ATOMIC);
 }
 
-static void xe_display_fini_early(void *arg)
+static void xe_display_fini_early(struct drm_device *dev, void *arg)
 {
 	struct xe_device *xe = arg;
 	struct intel_display *display = xe->display;
@@ -99,7 +114,6 @@ static void xe_display_fini_early(void *arg)
 int xe_display_init_early(struct xe_device *xe)
 {
 	struct intel_display *display = xe->display;
-	int err;
 
 	if (!xe->info.probe_display)
 		return 0;
@@ -107,32 +121,293 @@ int xe_display_init_early(struct xe_device *xe)
 	/* Fake uncore lock */
 	spin_lock_init(&xe->uncore.lock);
 
-	intel_display_driver_early_probe(display);
+	/* This must be called before any calls to HAS_PCH_* */
+	intel_pch_detect(display);
+
+	return drmm_add_action_or_reset(&xe->drm, xe_display_fini_early, xe);
+}
+
+
+
+void skl_get_initial_plane_config(struct intel_crtc *crtc, struct intel_initial_plane_config *plane_config);
+
+static void xe_initial_pll_enable(struct xe_device *xe)
+{
+	struct intel_display *display = xe->display;
+	enum pipe pipe;
+	enum pipe active_pipe = INVALID_PIPE;
+	u32 pipe_val;
+	u32 pll_val;
+	enum plane_id plane_id = PLANE_PRIMARY;
+	struct intel_crtc *crtc = NULL;
+	struct intel_crtc *iter;
+	struct intel_encoder *active_encoder = NULL;
+	struct intel_crtc_state *temp_state = NULL;
+	struct intel_connector *connector = NULL;
+	struct drm_connector *_connector;
+
+	/* 1. Find the active pipe from hardware registers */
+	for_each_pipe(display, pipe) {
+		pipe_val = intel_de_read(display, PLANE_CTL(pipe, plane_id));
+		if (pipe_val & PLANE_CTL_ENABLE) {
+			active_pipe = pipe;
+			break;
+		}
+	}
+
+	if (active_pipe == INVALID_PIPE) {
+		return;
+	}
+
+	/* 2. Find the corresponding software CRTC, encoder, and connector objects */
+	for_each_intel_crtc(&xe->drm, iter) {
+		if (iter->pipe == active_pipe) {
+			crtc = iter;
+			break;
+		}
+	}
+
+	if (!crtc) {
+		return;
+	}
+
+	for_each_intel_encoder(&xe->drm, active_encoder) {
+		if (active_encoder->pipe_mask & BIT(active_pipe))
+			break;
+	}
+
+	if (!active_encoder) {
+		return;
+	}
+
+	list_for_each_entry(_connector, &xe->drm.mode_config.connector_list, head) {
+		if (to_intel_connector(_connector)->encoder == active_encoder) {
+			connector = to_intel_connector(_connector);
+			break;
+		}
+	}
+
+	if (!connector) {
+		return;
+	}
+
+	struct intel_dp *intel_dp = enc_to_intel_dp(active_encoder);
+
+	/* 3. Create a temporary, local state object */
+	temp_state = kzalloc(sizeof(*temp_state), GFP_KERNEL);
+	if (!temp_state)
+		return;
+
+	temp_state->uapi.crtc = &crtc->base;
+	temp_state->hw.active = true;
+	temp_state->hw.enable = true;
+
+	/* 4. Populate the temporary state directly from hardware */
+	if (!intel_crtc_get_pipe_config(temp_state)) {
+		goto out_free;
+	}
+	active_encoder->get_config(active_encoder, temp_state);
+
+	/* 5. Provide VBT hints to guide the config calculation */
+	u8 vbt_lane_count = intel_bios_dp_max_lane_count(active_encoder->devdata);
+	int vbt_max_rate = connector->panel.vbt.edp.max_link_rate;
+	static const int dp_rates[] = {162000, 270000, 540000, 810000};
+	const int *source_rates;
+	int i, size, max_rate;
+
+	/* REORDERED: 7. Enable the PLL */
+	pll_val = intel_de_read(display, XELPDP_PORT_CLOCK_CTL(display, active_encoder->port));
+	if (pll_val & (XELPDP_LANE_PCLK_PLL_ACK(0) | XELPDP_LANE_PCLK_PLL_ACK(1))) {
+	} else {
+		intel_mtl_pll_enable(active_encoder, temp_state);
+	}
+
+	/* 8. With the PLL on, we can now safely read the DPCD */
+
+	/* Initialize PPS so we can safely check panel power state */
+	if (!intel_pps_init(intel_dp))
+		drm_warn(&xe->drm, "DEBUG: intel_pps_init failed\n");
+
+	/* Ensure VDD is on for software tracking to match hardware */
+	intel_pps_vdd_on_unlocked(intel_dp);
+
+	i = drm_dp_read_dpcd_caps(&intel_dp->aux, intel_dp->dpcd);
+	if (i < 0) {
+		drm_warn(&xe->drm, "DEBUG: drm_dp_read_dpcd_caps failed: %d\n", i);
+	}
+
+	/* Fallback: If DPCD read 0 (or failed), use VBT values to avoid RBR fallback mismatch */
+	if (intel_dp->dpcd[DP_MAX_LINK_RATE] == 0) {
+		intel_dp->dpcd[DP_MAX_LINK_RATE] = drm_dp_link_rate_to_bw_code(vbt_max_rate);
+		if (intel_dp->dpcd[DP_MAX_LANE_COUNT] == 0)
+			intel_dp->dpcd[DP_MAX_LANE_COUNT] = vbt_lane_count;
+	}
+
+	/* Read EDID to get the correct panel mode */
+	const struct drm_edid *drm_edid = drm_edid_read_ddc(&connector->base, &intel_dp->aux.ddc);
+
+	if (!drm_edid) {
+		drm_warn(&xe->drm, "DEBUG: EDID read failed\n");
+	} else {
+		/* We need to lock mode_config to update panel modes safely */
+		mutex_lock(&xe->drm.mode_config.mutex);
+
+		/* Re-init panel early with EDID to ensure backlight presence is detected correctly */
+		intel_bios_init_panel_late(display, &connector->panel, active_encoder->devdata, drm_edid);
+
+		/* Update the connector with the EDID we just read */
+		if (drm_edid_connector_update(&connector->base, drm_edid) ||
+			!drm_edid_connector_add_modes(&connector->base)) {
+			drm_warn(&xe->drm, "DEBUG: EDID update failed\n");
+		}
 
+		/* Re-run the fixed mode logic now that we have EDID */
+		intel_panel_add_edid_fixed_modes(connector, true);
+
+		/* Also init MSO since we have EDID now */
+		if (intel_dp->mso_link_count == 0)
+			intel_edp_mso_init(intel_dp);
+
+		/* HACK: Force MSO for Panther Lake if detection failed */
+		if (intel_dp->mso_link_count == 0) {
+			intel_dp->mso_link_count = 2;
+			intel_dp->mso_pixel_overlap = 0;
+		}
+
+		/* multiply the mode clock and horizontal timings for MSO */
+		struct drm_display_mode *fixed_mode;
+
+		list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) {
+			intel_edp_mso_mode_fixup(connector, fixed_mode);
+		}
+
+		mutex_unlock(&xe->drm.mode_config.mutex);
+		drm_edid_free(drm_edid);
+	}
+
+	/* Now that PPS is ready and DPCD is populated, initialize backlight funcs safely */
+	intel_backlight_init_funcs(&connector->panel);
+
+	/* Replicate intel_dp_set_sink_rates logic */
+	max_rate = drm_dp_bw_code_to_link_rate(intel_dp->dpcd[DP_MAX_LINK_RATE]);
+	for (i = 0; i < ARRAY_SIZE(dp_rates); i++) {
+		if (dp_rates[i] > max_rate)
+			break;
+		intel_dp->sink_rates[i] = dp_rates[i];
+	}
+	intel_dp->num_sink_rates = i;
+
+	/* Replicate intel_dp_set_max_sink_lane_count logic */
+	intel_dp->max_sink_lane_count = drm_dp_max_lane_count(intel_dp->dpcd);
+
+	/* Replicate intel_dp_set_source_rates logic */
+	static const int bmg_rates[] = {21600, 162000, 216000, 243000, 270000, 324000, 432000, 540000, 675000, 810000, 1000000, 1350000};
+	static const int mtl_rates[] = {21600, 162000, 216000, 243000, 270000, 324000, 432000, 540000, 675000, 810000, 1000000, 2000000};
+	static const int icl_rates[] = {21600, 162000, 216000, 270000, 324000, 432000, 540000, 648000, 810000, 1000000, 1350000};
+	static const int default_rates[] = {21600, 162000, 270000, 540000};
+
+	if (DISPLAY_VER(display) >= 14) {
+		if (display->platform.battlemage) {
+			source_rates = bmg_rates;
+			size = ARRAY_SIZE(bmg_rates);
+		} else {
+			source_rates = mtl_rates;
+			size = ARRAY_SIZE(mtl_rates);
+		}
+	} else if (DISPLAY_VER(display) >= 11) {
+		source_rates = icl_rates;
+		size = ARRAY_SIZE(icl_rates);
+	} else {
+		source_rates = default_rates;
+		size = ARRAY_SIZE(default_rates);
+	}
+
+	intel_dp->source_rates = source_rates;
+	intel_dp->num_source_rates = size;
+
+	/* Replicate intel_dp_set_common_rates logic using a simplified intersect_rates */
+	int common_idx = 0;
+	int src_idx = 0, sink_idx = 0;
+
+	while (src_idx < intel_dp->num_source_rates && sink_idx < intel_dp->num_sink_rates) {
+		if (intel_dp->source_rates[src_idx] == intel_dp->sink_rates[sink_idx]) {
+			intel_dp->common_rates[common_idx++] = intel_dp->source_rates[src_idx];
+			src_idx++;
+			sink_idx++;
+		} else if (intel_dp->source_rates[src_idx] < intel_dp->sink_rates[sink_idx]) {
+			src_idx++;
+		} else {
+			sink_idx++;
+		}
+	}
+	intel_dp->num_common_rates = common_idx;
+
+	if (intel_dp->num_common_rates == 0) {
+		intel_dp->common_rates[0] = 162000;
+		intel_dp->num_common_rates = 1;
+	}
+
+out_free:
+	/* 9. Discard the temporary state */
+	kfree(temp_state);
+}
+
+static struct intel_display_funcs xe_display_funcs;
+
+static void xe_display_post_crtc_init(struct intel_display *display)
+{
+	xe_initial_pll_enable(to_xe_device(display->drm));
+}
+
+int xe_display_init_noirq(struct xe_device *xe)
+{
+	struct intel_display *display = xe->display;
+	int err = 0;
+
+	intel_display_driver_early_probe(display);
 	/* Early display init.. */
 	intel_opregion_setup(display);
-
 	/*
 	 * Fill the dram structure to get the system dram info. This will be
 	 * used for memory latency calculation.
 	 */
-	err = intel_dram_detect(xe);
+	intel_dram_detect(xe);
+
 	if (err)
 		goto err_opregion;
 
 	intel_bw_init_hw(display);
-
-	intel_display_device_info_runtime_init(display);
-
 	err = intel_display_driver_probe_noirq(display);
+	if (err) {
+		intel_opregion_cleanup(display);
+		return err;
+	}
+	err = intel_cdclk_init(display);
 	if (err)
-		goto err_opregion;
+		return err;
+	intel_cdclk_init_hw(display);
+	to_intel_cdclk_state(display->cdclk.obj.state)->actual = display->cdclk.hw;
+	to_intel_cdclk_state(display->cdclk.obj.state)->logical = display->cdclk.hw;
+	intel_display_device_info_runtime_init(display);
+	if (xe->info.probe_display) {
+		if (display->funcs.display) {
+			xe_display_funcs = *display->funcs.display;
+			xe_display_funcs.initial_plane_config = intel_initial_plane_config; //xe_initial_plane_config_override;
+			xe_display_funcs.get_initial_plane_config = skl_get_initial_plane_config;
+			xe_display_funcs.post_crtc_init_hook = xe_display_post_crtc_init;
+			display->funcs.display = &xe_display_funcs;
+		}
+	}
 
 	err = intel_display_driver_probe_nogem(display);
 	if (err)
 		goto err_noirq;
 
-	return devm_add_action_or_reset(xe->drm.dev, xe_display_fini_early, xe);
+	err = intel_display_driver_probe(display);
+	if (err)
+		goto err_noirq;
+
+	return drmm_add_action_or_reset(&xe->drm, xe_display_fini_early, xe);
 err_noirq:
 	intel_display_driver_remove_noirq(display);
 	intel_power_domains_cleanup(display);
@@ -160,7 +435,7 @@ int xe_display_init(struct xe_device *xe)
 	if (!xe->info.probe_display)
 		return 0;
 
-	err = intel_display_driver_probe(display);
+	err = intel_display_driver_probe_nogem(display);
 	if (err)
 		return err;
 
@@ -174,6 +449,18 @@ void xe_display_register(struct xe_device *xe)
 	if (!xe->info.probe_display)
 		return;
 
+	/*
+	 * FIXME: On PTL, the initial commit fails to grab the necessary runtime
+	 * power references for the active pipes/transcoders. When
+	 * intel_power_domains_enable() drops the POWER_DOMAIN_INIT wakeref,
+	 * the display hardware powers down.
+	 *
+	 * Workaround: Manually grab a persistent reference to POWER_DOMAIN_INIT
+	 * here. This effectively keeps the boot configuration powered until
+	 * driver unload or a future fix addresses the root cause in fastset.
+	 */
+	intel_display_power_get(display, POWER_DOMAIN_INIT);
+
 	intel_display_driver_register(display);
 	intel_power_domains_enable(display);
 }
diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h
index e533aa4750bc11bfaf9a2b81e2dd8c1ac6ee58bc..d891a8abd7fc2293961c4e42be3ccf1dd1a8bb33 100644
--- a/drivers/gpu/drm/xe/display/xe_display.h
+++ b/drivers/gpu/drm/xe/display/xe_display.h
@@ -18,6 +18,7 @@ void xe_display_driver_set_hooks(struct drm_driver *driver);
 int xe_display_probe(struct xe_device *xe);
 
 int xe_display_init_early(struct xe_device *xe);
+int xe_display_init_noirq(struct xe_device *xe);
 int xe_display_init(struct xe_device *xe);
 
 void xe_display_register(struct xe_device *xe);
@@ -47,6 +48,7 @@ static inline void xe_display_driver_remove(struct xe_device *xe) {}
 static inline int xe_display_probe(struct xe_device *xe) { return 0; }
 
 static inline int xe_display_init_early(struct xe_device *xe) { return 0; }
+static inline int xe_display_init_noirq(struct xe_device *xe) { return 0; }
 static inline int xe_display_init(struct xe_device *xe) { return 0; }
 
 static inline void xe_display_register(struct xe_device *xe) {}
diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
index 456899238377edb92702108f7c43dcfecd635a98..bc3bc1fc91b5782aeab778b7e94ac63c8ed7b514 100644
--- a/drivers/gpu/drm/xe/xe_device.c
+++ b/drivers/gpu/drm/xe/xe_device.c
@@ -913,7 +913,7 @@ int xe_device_probe(struct xe_device *xe)
 	if (err)
 		return err;
 
-	err = xe_display_init(xe);
+	err = xe_display_init_noirq(xe);
 	if (err)
 		return err;
 

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 03/10] drm/i915/display: Implement aggressive boot state preservation for PTL
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 01/10] drm/i915/display: Implement passive initialization for splash screen preservation Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 02/10] drm/xe/display: Implement seamless boot state reconstruction for PTL Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 04/10] drm/xe/display: Fix initial plane reconstruction and stolen memory handling Juasheem Sultan
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

To ensure a seamless, flicker-free boot on Panther Lake (PTL) platforms,
especially those with MSO (Multi-Segment Operation) panels, the driver
must rigidly adhere to the hardware state established by the BIOS.
Standard state readout and re-computation often lead to mismatches (e.g.,
missing MSO flags, incorrect PLL parameters) that cause the hardware to
reset or blank the screen.

This patch implements a mechanism to "steal" and lock the boot state:

1.  **State Capture (`intel_crtc_steal_state`)**:
    *   Captures critical hardware state (PLLs, timings, MSO splitter
        configuration, DSC parameters, etc.) into persistent "boot_"
        variables during the initial readout.
    *   Populates the initial atomic state (`live_crtc_state`) directly
        from this captured data, creating necessary mode blobs manually
        to satisfy atomic checks without triggering hardware changes.

2.  **State Restoration & Locking**:
    *   In `intel_modeset_pipe_config`, if the requested mode matches
        the captured boot dimensions, the driver bypasses standard computation.
    *   Instead, it forcibly restores the captured boot parameters (PLLs,
        timings, MSO flags like `PHSYNC`/`PVSYNC`) into the new CRTC state.
    *   Forces `fastset` to succeed for these matching states, ensuring
        no full modeset occurs.

3.  **Atomic Check Interventions**:
    *   Prevents the boot CRTC from being disabled during the initial
        userspace handover. If `active` is false but matches the boot pipe,
        it forces `active = true` and re-attaches the mode.
    *   Fixes a potential issue where userspace sets a mode without
        connectors by forcibly attaching the eDP connector.

4.  **Hardware Update Bypass**:
    *   In `intel_update_crtc`, if the transition matches the boot state,
        all hardware-touching functions (plane updates, scaler setup)
        are skipped. Only vblank events are handled, effectively syncing
        the software state to the running hardware.

5.  **Backlight & Watermarks**:
    *   Forces backlight enable during the first fastset.
    *   Captures and restores initial watermark and DDB allocations.

This approach effectively treats the BIOS state as the "golden" state
for the initial driver load, preventing the flicker associated with
driver takeover.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_display.c | 813 +++++++++++++++++++++++++--
 drivers/gpu/drm/i915/display/intel_display.h |   4 +
 2 files changed, 783 insertions(+), 34 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index 0d527cf228666b1196f0c06f9b8d855f417582fe..f55cc950da6fb2521727f16d1fd9244367ddc785 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -44,7 +44,7 @@
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_rect.h>
 #include <drm/drm_vblank.h>
-
+#include <drm/drm_atomic.h>
 #include "g4x_dp.h"
 #include "g4x_hdmi.h"
 #include "hsw_ips.h"
@@ -58,6 +58,7 @@
 #include "intel_alpm.h"
 #include "intel_atomic.h"
 #include "intel_audio.h"
+#include "intel_backlight.h"
 #include "intel_bo.h"
 #include "intel_bw.h"
 #include "intel_cdclk.h"
@@ -72,6 +73,7 @@
 #include "intel_ddi.h"
 #include "intel_de.h"
 #include "intel_display_driver.h"
+#include "intel_display_irq.h"
 #include "intel_display_power.h"
 #include "intel_display_regs.h"
 #include "intel_display_rpm.h"
@@ -101,6 +103,39 @@
 #include "intel_link_bw.h"
 #include "intel_lvds.h"
 #include "intel_lvds_regs.h"
+
+static bool boot_state_captured;
+static struct intel_dpll_hw_state boot_pll_state;
+static struct intel_dpll *boot_shared_dpll;
+static int boot_port_clock;
+static int boot_lane_count;
+static enum transcoder boot_cpu_transcoder;
+static struct drm_display_mode boot_adjusted_mode;
+static int boot_hdisplay;
+static int boot_vdisplay;
+static int boot_pipe_bpp;
+static struct intel_link_m_n boot_dp_m_n;
+static struct intel_link_m_n boot_dp_m2_n2;
+static bool boot_fec_enable;
+static bool boot_enhanced_framing;
+static bool boot_dsc_compression_enable;
+static struct drm_dsc_config boot_dsc_config;
+static struct intel_csc_matrix boot_csc;
+static bool boot_limited_color_range;
+static enum pipe boot_pipe = INVALID_PIPE;
+static unsigned int boot_output_types;
+static struct {
+	bool enable;
+	int link_count;
+	int pixel_overlap;
+} boot_splitter;
+static struct skl_pipe_wm boot_wm;
+static struct skl_ddb_entry boot_ddb[I915_MAX_PLANES];
+static u32 boot_gamma_mode;
+static u32 boot_csc_mode;
+static u32 boot_linetime;
+static bool initial_boot_handoff;
+
 #include "intel_modeset_setup.h"
 #include "intel_modeset_verify.h"
 #include "intel_overlay.h"
@@ -2464,12 +2499,23 @@ static int intel_crtc_compute_config(struct intel_atomic_state *state,
 {
 	struct intel_crtc_state *crtc_state =
 		intel_atomic_get_new_crtc_state(state, crtc);
+	const struct intel_crtc_state *old_crtc_state =
+		intel_atomic_get_old_crtc_state(state, crtc);
 	int ret;
 
 	ret = intel_crtc_compute_vblank_delay(state, crtc);
 	if (ret)
 		return ret;
 
+	if (state->fastset) {
+		crtc_state->hw.adjusted_mode.crtc_clock = old_crtc_state->hw.adjusted_mode.crtc_clock;
+
+		if (crtc_state->hw.adjusted_mode.crtc_clock) {
+			crtc_state->pixel_rate = crtc_state->hw.adjusted_mode.crtc_clock;
+			return 0;
+		}
+	}
+
 	ret = intel_dpll_crtc_compute_clock(state, crtc);
 	if (ret)
 		return ret;
@@ -2921,6 +2967,12 @@ static void intel_get_pipe_src_size(struct intel_crtc *crtc,
 		      REG_FIELD_GET(PIPESRC_WIDTH_MASK, tmp) + 1,
 		      REG_FIELD_GET(PIPESRC_HEIGHT_MASK, tmp) + 1);
 
+	/* [FB-FIX] Force pipe source size from boot state if HW readout is invalid (seamless handoff) */
+	if (boot_state_captured && crtc->pipe == boot_pipe &&
+	    (drm_rect_width(&pipe_config->pipe_src) <= 1 || drm_rect_height(&pipe_config->pipe_src) <= 1)) {
+		drm_rect_init(&pipe_config->pipe_src, 0, 0, boot_hdisplay, boot_vdisplay);
+	}
+
 	intel_joiner_adjust_pipe_src(pipe_config);
 }
 
@@ -4699,11 +4751,137 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
 	drm_mode_set_crtcinfo(&crtc_state->hw.adjusted_mode,
 			      CRTC_STEREO_DOUBLE);
 
+	/*
+	 * FIX: Permanent Boot Lock (Moved to TOP).
+	 * We check for boot state match BEFORE letting the driver calculate anything.
+	 * This bypasses broken driver logic for MSO panels.
+	 */
+	bool restore_boot = false;
+	int new_h = 0;
+	int new_v = 0;
+
+	/* Only restore boot state if this is the initial inherited commit */
+	if (boot_state_captured && crtc_state->uapi.enable && crtc_state->inherited) {
+		new_h = crtc_state->hw.adjusted_mode.crtc_hdisplay;
+		new_v = crtc_state->hw.adjusted_mode.crtc_vdisplay;
+
+		if (new_h == boot_hdisplay && new_v == boot_vdisplay) {
+			restore_boot = true;
+		} else if (((boot_splitter.enable && boot_splitter.link_count > 0 &&
+			     new_h == boot_hdisplay * boot_splitter.link_count) ||
+			    (new_h == boot_hdisplay * 2)) &&
+			   new_v == boot_vdisplay) {
+			restore_boot = true;
+		}
+	}
+
+			if (restore_boot) {
+
+
+
+				crtc_state->dpll_hw_state = boot_pll_state;
+
+				crtc_state->intel_dpll = boot_shared_dpll;
+
+				/* Restore boot port clock */
+
+				crtc_state->port_clock = boot_port_clock;
+
+
+		crtc_state->lane_count = 4;
+
+		if (boot_pipe_bpp == 0) {
+			crtc_state->pipe_bpp = 24;
+		} else {
+			crtc_state->pipe_bpp = boot_pipe_bpp;
+		}
+
+		crtc_state->dp_m_n = boot_dp_m_n;
+
+		crtc_state->dp_m2_n2 = boot_dp_m2_n2;
+		crtc_state->fec_enable = boot_fec_enable;
+		crtc_state->enhanced_framing = boot_enhanced_framing;
+		crtc_state->dsc.compression_enable = boot_dsc_compression_enable;
+		crtc_state->dsc.config = boot_dsc_config;
+		crtc_state->csc = boot_csc;
+		crtc_state->limited_color_range = boot_limited_color_range;
+
+		crtc_state->gamma_mode = boot_gamma_mode;
+		crtc_state->csc_mode = boot_csc_mode;
+		crtc_state->linetime = boot_linetime;
+
+		crtc_state->cpu_transcoder = boot_cpu_transcoder;
+		crtc_state->output_types = boot_output_types;
+
+		if (DISPLAY_VER(display) >= 9) {
+			memcpy(&crtc_state->wm.skl.optimal, &boot_wm, sizeof(boot_wm));
+			memcpy(&crtc_state->wm.skl.plane_ddb, boot_ddb, sizeof(boot_ddb));
+		}
+
+		/* Restore captured mode (already divided if MSO was active) */
+		drm_mode_copy(&crtc_state->hw.adjusted_mode, &boot_adjusted_mode);
+		drm_mode_copy(&crtc_state->uapi.adjusted_mode, &boot_adjusted_mode);
+
+		int src_w = boot_hdisplay;
+		int src_h = boot_vdisplay;
+
+		if (boot_splitter.enable && boot_splitter.link_count > 0) {
+			crtc_state->splitter.enable = true;
+			crtc_state->splitter.link_count = boot_splitter.link_count;
+			crtc_state->splitter.pixel_overlap = boot_splitter.pixel_overlap;
+
+			/*
+			 * If boot_hdisplay captured the divided pipe width (1128), scale it.
+			 * If it captured the full logical width (2256), use it as is.
+			 */
+			if (boot_hdisplay < new_h)
+				src_w = boot_hdisplay * boot_splitter.link_count - boot_splitter.pixel_overlap * (boot_splitter.link_count - 1);
+			else
+				src_w = boot_hdisplay;
+		}
+
+		/* Set pixel_rate to match clock */
+		crtc_state->pixel_rate = crtc_state->hw.adjusted_mode.crtc_clock;
+
+		/* Copy to pipe_mode */
+		drm_mode_copy(&crtc_state->hw.pipe_mode, &crtc_state->hw.adjusted_mode);
+
+		/*
+		 * FIX: Force flags to PHSYNC | PVSYNC if we are restoring MSO state.
+		 * The captured boot state might have lost them or they might be implicit.
+		 * Hardware readout confirms 0xA (Positive/Positive) is active.
+		 */
+		/*
+		if (crtc_state->splitter.enable) {
+			crtc_state->hw.adjusted_mode.flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+			crtc_state->hw.pipe_mode.flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+			crtc_state->uapi.adjusted_mode.flags |= (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC);
+		}
+		*/
+
+		/* Set pixel_rate to match clock */
+		crtc_state->pixel_rate = crtc_state->hw.adjusted_mode.crtc_clock;
+
+		/* Restore pipe_src */
+		drm_rect_init(&crtc_state->pipe_src, 0, 0, src_w, src_h);
+
+		/*
+		 * Stop the driver from recalculating/overwriting these.
+		 * We return early-ish or let the rest run?
+		 * intel_modeset_pipe_config continues...
+		 * But we overwrote the critical parts.
+		 */
+		return 0;
+	}
+
 	/* Pass our mode to the connectors and the CRTC to give them a chance to
 	 * adjust it according to limitations or connector properties, and also
 	 * a chance to reject the mode entirely.
 	 */
 	for_each_new_connector_in_state(&state->base, connector, connector_state, i) {
+		if (restore_boot)
+			continue;
+
 		struct intel_encoder *encoder =
 			to_intel_encoder(connector_state->best_encoder);
 
@@ -4727,13 +4905,18 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
 		crtc_state->port_clock = crtc_state->hw.adjusted_mode.crtc_clock
 			* crtc_state->pixel_multiplier;
 
-	ret = intel_crtc_compute_config(state, crtc);
-	if (ret == -EDEADLK)
-		return ret;
-	if (ret < 0) {
-		drm_dbg_kms(display->drm, "[CRTC:%d:%s] config failure: %d\n",
-			    crtc->base.base.id, crtc->base.name, ret);
-		return ret;
+	/* Skip PLL computation if we are disabling the pipe */
+	if (crtc_state->hw.enable && !restore_boot) {
+		ret = intel_crtc_compute_config(state, crtc);
+		if (ret == -EDEADLK)
+			return ret;
+		if (ret < 0) {
+			drm_dbg_kms(display->drm, "[CRTC:%d:%s] config failure: %d\n",
+				    crtc->base.base.id, crtc->base.name, ret);
+			return ret;
+		}
+	} else {
+		ret = 0;
 	}
 
 	/* Dithering seems to not pass-through bits correctly when it should, so
@@ -5454,6 +5637,25 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config,
 #undef PIPE_CONF_CHECK_RECT
 #undef PIPE_CONF_QUIRK
 
+	/* [] Hack to force fastset/verify success for seamless boot */
+	if (!ret && boot_state_captured) {
+		struct intel_crtc *crtc = to_intel_crtc(current_config->uapi.crtc);
+
+		if (crtc->pipe == boot_pipe) {
+			bool match = false;
+			int h = pipe_config->hw.pipe_mode.hdisplay;
+			int v = pipe_config->hw.pipe_mode.vdisplay;
+
+			if ((h == boot_hdisplay || h == boot_hdisplay * 2) && v == boot_vdisplay)
+				match = true;
+
+			if (match) {
+				ret = true;
+			}
+
+		}
+	}
+
 	return ret;
 }
 
@@ -5759,23 +5961,156 @@ static void intel_crtc_check_fastset(const struct intel_crtc_state *old_crtc_sta
 		new_crtc_state->update_pipe = true;
 }
 
+static int intel_crtc_add_planes_to_state(struct intel_atomic_state *state,
+					  struct intel_crtc *crtc,
+					  u8 plane_ids_mask)
+{
+	struct drm_i915_private *dev_priv = to_i915(state->base.dev);
+	struct intel_plane *plane;
+
+	for_each_intel_plane_on_crtc(&dev_priv->drm, crtc, plane) {
+		struct intel_plane_state *plane_state;
+
+		if ((plane_ids_mask & BIT(plane->id)) == 0)
+			continue;
+
+		plane_state = intel_atomic_get_plane_state(state, plane);
+		if (IS_ERR(plane_state))
+			return PTR_ERR(plane_state);
+	}
+
+	return 0;
+}
+
+static int __maybe_unused intel_atomic_add_affected_planes(struct intel_atomic_state *state,
+				     struct intel_crtc *crtc)
+{
+	const struct intel_crtc_state *old_crtc_state =
+		intel_atomic_get_old_crtc_state(state, crtc);
+	const struct intel_crtc_state *new_crtc_state =
+		intel_atomic_get_new_crtc_state(state, crtc);
+
+	return intel_crtc_add_planes_to_state(state, crtc,
+					      old_crtc_state->enabled_planes |
+					      new_crtc_state->enabled_planes);
+}
+
+static bool __maybe_unused active_planes_affects_min_cdclk(struct drm_i915_private *dev_priv)
+{
+	struct intel_display *display = dev_priv->display;
+	/* See {hsw,vlv,ivb}_plane_ratio() */
+	return IS_BROADWELL(dev_priv) || IS_HASWELL(dev_priv) ||
+		IS_CHERRYVIEW(dev_priv) || IS_VALLEYVIEW(dev_priv) ||
+		display->platform.ivybridge;
+}
+
+static int intel_crtc_add_joiner_planes(struct intel_atomic_state *state,
+					struct intel_crtc *crtc,
+					struct intel_crtc *other)
+{
+	const struct intel_plane_state __maybe_unused *plane_state;
+	struct intel_plane *plane;
+	u8 plane_ids = 0;
+	int i;
+
+	for_each_new_intel_plane_in_state(state, plane, plane_state, i) {
+		if (plane->pipe == crtc->pipe)
+			plane_ids |= BIT(plane->id);
+	}
+
+	return intel_crtc_add_planes_to_state(state, other, plane_ids);
+}
+
+static int intel_joiner_add_affected_planes(struct intel_atomic_state *state)
+{
+	struct drm_i915_private *i915 = to_i915(state->base.dev);
+	const struct intel_crtc_state *crtc_state;
+	struct intel_crtc *crtc;
+	int i;
+
+	for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
+		struct intel_crtc *other;
+
+		for_each_intel_crtc_in_pipe_mask(&i915->drm, other,
+						 crtc_state->joiner_pipes) {
+			int ret;
+
+			if (crtc == other)
+				continue;
+
+			ret = intel_crtc_add_joiner_planes(state, crtc, other);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return 0;
+}
+
+static int __maybe_unused intel_atomic_check_planes(struct intel_atomic_state *state)
+{
+	struct intel_crtc_state *old_crtc_state, *new_crtc_state;
+	struct intel_plane_state __maybe_unused *plane_state;
+	struct intel_crtc *crtc;
+	int i, ret;
+
+	/* ret = icl_add_linked_planes(state); */
+	/* if (ret) */
+	/*	return ret; */
+
+	ret = intel_joiner_add_affected_planes(state);
+	if (ret)
+		return ret;
+
+	ret = intel_plane_atomic_check(state);
+	if (ret)
+		return ret;
+
+	for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state,
+					    new_crtc_state, i) {
+
+			/* if (ret) */
+
+			/*	return ret; */
+
+
+
+			ret = intel_joiner_add_affected_planes(state);
+
+
+	}
+
+	return 0;
+}
+
 static int intel_atomic_check_crtcs(struct intel_atomic_state *state)
 {
 	struct intel_display *display = to_intel_display(state);
 	struct intel_crtc_state __maybe_unused *crtc_state;
 	struct intel_crtc *crtc;
-	int i;
+	struct intel_link_bw_limits limits;
+	int i, ret;
 
-	for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
-		int ret;
 
+	intel_link_bw_init_limits(state, &limits);
+
+	for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) {
 		ret = intel_crtc_atomic_check(state, crtc);
 		if (ret) {
 			drm_dbg_atomic(display->drm,
-				       "[CRTC:%d:%s] atomic driver check failed\n",
-				       crtc->base.base.id, crtc->base.name);
+					"[CRTC:%d:%s] atomic driver check failed\n",
+					crtc->base.base.id, crtc->base.name);
 			return ret;
 		}
+
+		if (crtc_state->uapi.mode_changed ||
+				crtc_state->uapi.connectors_changed ||
+				crtc_state->update_pipe) {
+			ret = intel_modeset_pipe_config(state, crtc,
+					&limits);
+			if (ret)
+				return ret;
+		}
 	}
 
 	return 0;
@@ -6030,10 +6365,11 @@ static int intel_async_flip_check_hw(struct intel_atomic_state *state, struct in
 		return -EINVAL;
 	}
 
-	if (old_crtc_state->active_planes != new_crtc_state->active_planes) {
+	if (old_crtc_state->active_planes != new_crtc_state->active_planes &&
+			!new_crtc_state->inherited) {
 		drm_dbg_kms(display->drm,
-			    "[CRTC:%d:%s] Active planes cannot be in async flip\n",
-			    crtc->base.base.id, crtc->base.name);
+				"[CRTC:%d:%s] Active planes cannot be in async flip\n",
+				crtc->base.base.id, crtc->base.name);
 		return -EINVAL;
 	}
 
@@ -6361,15 +6697,81 @@ int intel_atomic_check(struct drm_device *dev,
 		 * crtc's state no longer considered to be inherited
 		 * after the first userspace/client initiated commit.
 		 */
-		if (!state->internal)
-			new_crtc_state->inherited = false;
+		if (!state->internal) {
+			bool keep_inherited = false;
+
+			if (boot_state_captured && new_crtc_state->uapi.active) {
+				/* Check if mode matches boot mode */
+				if (boot_state_captured && new_crtc_state->uapi.active) {
+					/* Check if mode matches boot mode */
+					if (new_crtc_state->uapi.mode.hdisplay == boot_hdisplay &&
+						new_crtc_state->uapi.mode.vdisplay == boot_vdisplay) {
+						keep_inherited = true;
+					}
+				}
+			}
 
-		if (new_crtc_state->inherited != old_crtc_state->inherited)
-			new_crtc_state->uapi.mode_changed = true;
+			if (!keep_inherited) {
+				new_crtc_state->inherited = false;
+			}
+		}
 
 		if (new_crtc_state->uapi.scaling_filter !=
 		    old_crtc_state->uapi.scaling_filter)
 			new_crtc_state->uapi.mode_changed = true;
+
+		/*
+		 * FIX: Aggressive Boot State Lock.
+		 * If we have captured the boot state, we PREVENT the CRTC from being disabled.
+		 * We force Active=1, Mode=BootMode, Connector=eDP.
+		 */
+		if (boot_state_captured && !new_crtc_state->uapi.active) {
+			/* Only force if this CRTC matches the boot pipe to avoid forcing wrong pipe */
+			if (crtc->pipe == boot_pipe) {
+				new_crtc_state->uapi.active = true;
+				new_crtc_state->uapi.enable = true;
+
+				/* Restore mode if missing */
+				if (!new_crtc_state->uapi.mode_blob) {
+					if (drm_atomic_set_mode_for_crtc(&new_crtc_state->uapi, &boot_adjusted_mode) < 0) {
+						drm_err(display->drm, "Failed to set mode for boot captured crtc\n");
+					}
+				}
+			}
+		}
+
+		/*
+		 * FIX: Detect userspace setting mode without connectors (Android bug?).
+		 * If we have a mode but no connectors, the atomic helper will error out or disable the pipe.
+		 * We force the eDP connector to be attached to keep the display alive.
+		 */
+		if (new_crtc_state->uapi.active && !new_crtc_state->uapi.connector_mask) {
+			struct drm_connector *connector;
+			struct drm_connector_list_iter conn_iter;
+
+			drm_connector_list_iter_begin(dev, &conn_iter);
+			drm_for_each_connector_iter(connector, &conn_iter) {
+				if (connector->connector_type == DRM_MODE_CONNECTOR_eDP) {
+					struct drm_connector_state *conn_state;
+
+					conn_state = drm_atomic_get_connector_state(&state->base, connector);
+					if (IS_ERR(conn_state)) {
+						continue;
+					}
+
+					/* Force CRTC set */
+					conn_state->crtc = &crtc->base;
+
+						/* Manually update masks */
+						new_crtc_state->uapi.connector_mask |= drm_connector_mask(connector);
+						if (connector->encoder)
+							new_crtc_state->uapi.encoder_mask |= drm_encoder_mask(connector->encoder);
+
+					break; /* Only attach one eDP */
+				}
+			}
+			drm_connector_list_iter_end(&conn_iter);
+		}
 	}
 
 	intel_vrr_check_modeset(state);
@@ -6690,8 +7092,22 @@ static void intel_enable_crtc(struct intel_atomic_state *state,
 	struct intel_display *display = to_intel_display(state);
 	const struct intel_crtc_state *new_crtc_state =
 		intel_atomic_get_new_crtc_state(state, crtc);
+	const struct intel_crtc_state *old_crtc_state =
+		intel_atomic_get_old_crtc_state(state, crtc);
 	struct intel_crtc *pipe_crtc;
 
+	if (new_crtc_state->inherited) {
+		return;
+	}
+
+	/*
+	 * If we are transitioning from an inherited state (where disable was skipped),
+	 * we must also skip enable because the hardware is already active.
+	 */
+	if (old_crtc_state->inherited) {
+		return;
+	}
+
 	if (!intel_crtc_needs_modeset(new_crtc_state))
 		return;
 
@@ -6733,7 +7149,8 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
 		    intel_crtc_needs_color_update(new_crtc_state))
 			intel_color_load_luts(new_crtc_state);
 
-		intel_pre_plane_update(state, crtc);
+		if (!old_crtc_state->inherited)
+			intel_pre_plane_update(state, crtc);
 
 		if (intel_crtc_needs_fastset(new_crtc_state))
 			intel_encoders_update_pipe(state, crtc);
@@ -6747,22 +7164,26 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
 			intel_vrr_set_transcoder_timings(new_crtc_state);
 	}
 
-	intel_fbc_update(state, crtc);
+	if (!old_crtc_state->inherited)
+		intel_fbc_update(state, crtc);
 
 	drm_WARN_ON(display->drm, !intel_display_power_is_enabled(display, POWER_DOMAIN_DC_OFF));
 
 	if (!modeset &&
 	    intel_crtc_needs_color_update(new_crtc_state) &&
-	    !new_crtc_state->use_dsb && !new_crtc_state->use_flipq)
+	    !new_crtc_state->use_dsb && !new_crtc_state->use_flipq &&
+		!old_crtc_state->inherited)
 		intel_color_commit_noarm(NULL, new_crtc_state);
 
-	if (!new_crtc_state->use_dsb && !new_crtc_state->use_flipq)
+	if (!new_crtc_state->use_dsb && !new_crtc_state->use_flipq &&
+		!old_crtc_state->inherited)
 		intel_crtc_planes_update_noarm(NULL, state, crtc);
 }
 
 static void intel_update_crtc(struct intel_atomic_state *state,
 			      struct intel_crtc *crtc)
 {
+	struct intel_display *display = to_intel_display(state);
 	const struct intel_crtc_state *old_crtc_state =
 		intel_atomic_get_old_crtc_state(state, crtc);
 	struct intel_crtc_state *new_crtc_state =
@@ -6775,6 +7196,31 @@ static void intel_update_crtc(struct intel_atomic_state *state,
 
 		intel_flipq_add(crtc, INTEL_FLIPQ_PLANE_1, 0, INTEL_DSB_0,
 				new_crtc_state->dsb_commit);
+	} else if (boot_state_captured && !old_crtc_state->hw.active && new_crtc_state->hw.active) {
+
+		/*
+		 * For the first commit on an inherited configuration we don't
+		 * want to touch the hardware at all. We just want to sync the
+		 * software state.
+		 *
+		 * 1. Skip commit_pipe_pre_planes (vblank evasion)
+		 * 2. Skip intel_crtc_planes_update_arm (don't overwrite PLANE_SURF)
+		 * 3. Skip commit_pipe_post_planes (don't wait for vblank)
+		 */
+		intel_crtc_vblank_on(new_crtc_state);
+		gen8_irq_power_well_post_enable(display, BIT(crtc->pipe));
+
+		if (new_crtc_state->uapi.event) {
+			spin_lock_irq(&display->drm->event_lock);
+			drm_crtc_send_vblank_event(&crtc->base, new_crtc_state->uapi.event);
+			spin_unlock_irq(&display->drm->event_lock);
+			new_crtc_state->uapi.event = NULL;
+		} else {
+		}
+
+		if (new_crtc_state->uapi.commit) {
+			complete_all(&new_crtc_state->uapi.commit->flip_done);
+		}
 	} else if (new_crtc_state->use_dsb) {
 		intel_crtc_prepare_vblank_event(new_crtc_state, &crtc->dsb_event);
 
@@ -6792,6 +7238,19 @@ static void intel_update_crtc(struct intel_atomic_state *state,
 
 		commit_pipe_post_planes(state, crtc);
 
+		/* Force backlight on for boot pipe fastset to ensure screen is visible */
+		if (boot_state_captured && crtc->pipe == boot_pipe) {
+			struct drm_connector *conn;
+			const struct drm_connector_state *conn_state;
+			int i;
+
+			for_each_new_connector_in_state(&state->base, conn, conn_state, i) {
+				if (conn_state->crtc == &crtc->base) {
+					intel_backlight_enable(new_crtc_state, conn_state);
+				}
+			}
+		}
+
 		intel_pipe_update_end(state, crtc);
 	}
 
@@ -6812,8 +7271,9 @@ static void intel_update_crtc(struct intel_atomic_state *state,
 	 * of enabling them on the CRTC's first fastset.
 	 */
 	if (intel_crtc_needs_fastset(new_crtc_state) &&
-	    old_crtc_state->inherited)
-		intel_crtc_arm_fifo_underrun(crtc, new_crtc_state);
+	    old_crtc_state->inherited) {
+		/* intel_crtc_arm_fifo_underrun(crtc, new_crtc_state); */
+	}
 }
 
 static void intel_old_crtc_state_disables(struct intel_atomic_state *state,
@@ -6822,8 +7282,14 @@ static void intel_old_crtc_state_disables(struct intel_atomic_state *state,
 	struct intel_display *display = to_intel_display(state);
 	const struct intel_crtc_state *old_crtc_state =
 		intel_atomic_get_old_crtc_state(state, crtc);
+	const struct intel_crtc_state *new_crtc_state =
+		intel_atomic_get_new_crtc_state(state, crtc);
 	struct intel_crtc *pipe_crtc;
 
+	if (old_crtc_state->inherited && new_crtc_state->uapi.active) {
+		return;
+	}
+
 	/*
 	 * We need to disable pipe CRC before disabling the pipe,
 	 * or we race against vblank off.
@@ -8151,23 +8617,209 @@ void intel_init_display_hooks(struct intel_display *display)
 	}
 }
 
+void intel_crtc_steal_state(struct intel_crtc_state *crtc_state)
+{
+	struct intel_display *display = to_intel_display(crtc_state->uapi.crtc->dev);
+	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
+	struct intel_crtc_state *live_crtc_state = crtc_state;
+	struct intel_plane *primary = to_intel_plane(crtc->base.primary);
+	struct intel_plane_state *live_plane_state =
+		to_intel_plane_state(primary->base.state);
+	struct intel_encoder *encoder;
+	struct intel_connector *connector = NULL;
+	struct drm_connector_list_iter conn_iter;
+	enum pipe pipe;
+	/*
+	 * The pipe_config is already filled with the hw state, so just copy
+	 * it to the uapi state. Use adjusted_mode since it has the correct
+	 * clock after the encoder's get_config is called.
+	 */
+	live_crtc_state->hw.active = crtc_state->hw.active;
+	live_crtc_state->uapi.active = crtc_state->uapi.active;
+	live_crtc_state->uapi.enable = crtc_state->uapi.enable;
+	live_crtc_state->inherited = true;
+	drm_mode_copy(&live_crtc_state->uapi.adjusted_mode, &crtc_state->hw.adjusted_mode);
+	drm_mode_copy(&live_crtc_state->hw.adjusted_mode, &crtc_state->hw.adjusted_mode);
+	live_crtc_state->pixel_rate = live_crtc_state->uapi.adjusted_mode.crtc_clock;
+
+	/* FIX: Copy PLL state so the driver knows we are using a PLL */
+	live_crtc_state->dpll_hw_state = crtc_state->dpll_hw_state;
+	live_crtc_state->dpll = crtc_state->dpll;
+	live_crtc_state->port_clock = crtc_state->port_clock;
+	live_crtc_state->lane_count = crtc_state->lane_count;
+	live_crtc_state->output_types = crtc_state->output_types;
+
+	/* FIX: Also populate pipe_src so pfit doesn't complain */
+	drm_rect_init(&live_crtc_state->pipe_src, 0, 0,
+		      crtc_state->hw.adjusted_mode.crtc_hdisplay,
+		      crtc_state->hw.adjusted_mode.crtc_vdisplay);
+
+	/* Capture boot state for persistence */
+	if (!boot_state_captured && (crtc_state->hw.active || crtc_state->hw.adjusted_mode.crtc_clock > 0)) {
+		boot_pll_state = crtc_state->dpll_hw_state;
+		boot_shared_dpll = crtc_state->intel_dpll;
+		boot_port_clock = crtc_state->port_clock;
+		boot_lane_count = crtc_state->lane_count;
+		boot_pipe_bpp = crtc_state->pipe_bpp;
+		boot_dp_m_n = crtc_state->dp_m_n;
+		boot_dp_m2_n2 = crtc_state->dp_m2_n2;
+		boot_fec_enable = crtc_state->fec_enable;
+		boot_enhanced_framing = crtc_state->enhanced_framing;
+		boot_dsc_compression_enable = crtc_state->dsc.compression_enable;
+		boot_dsc_config = crtc_state->dsc.config;
+		boot_csc = crtc_state->csc;
+		boot_limited_color_range = crtc_state->limited_color_range;
+		boot_cpu_transcoder = crtc_state->cpu_transcoder;
+
+		boot_gamma_mode = crtc_state->gamma_mode;
+		boot_csc_mode = crtc_state->csc_mode;
+		boot_linetime = crtc_state->linetime;
+
+		/* [FB-FIX] Capture Watermark and DDB state */
+		if (DISPLAY_VER(display) >= 9) {
+			memcpy(&boot_wm, &crtc_state->wm.skl.optimal, sizeof(boot_wm));
+			memcpy(&boot_ddb, &crtc_state->wm.skl.plane_ddb, sizeof(boot_ddb));
+		}
+
+		/* Capture pipe_mode which has the fixed-up logical MSO timings */
+		drm_mode_copy(&boot_adjusted_mode, &crtc_state->hw.pipe_mode);
+
+		/*
+		 * pipe_mode might not have the clock populated (it's often 0 from readout),
+		 * so copy the valid clock from adjusted_mode to prevent divide-by-zero later.
+		 */
+		boot_adjusted_mode.crtc_clock = crtc_state->hw.adjusted_mode.crtc_clock;
+		boot_adjusted_mode.clock = crtc_state->hw.adjusted_mode.crtc_clock;
+
+		boot_splitter.enable = crtc_state->splitter.enable;
+		boot_splitter.link_count = crtc_state->splitter.link_count;
+		boot_splitter.pixel_overlap = crtc_state->splitter.pixel_overlap;
+		boot_hdisplay = crtc_state->hw.pipe_mode.crtc_hdisplay;
+		boot_vdisplay = crtc_state->hw.pipe_mode.crtc_vdisplay;
+		boot_pipe = crtc->pipe;
+		boot_output_types = crtc_state->output_types;
+
+		boot_state_captured = true;
+	}
+
+		if (live_crtc_state->hw.active) {
+			/*
+			 * This is filling in the uapi.mode and creating the blob for it.
+			 * The blob is required for the initial commit to succeed.
+			 */
+			memset(&live_crtc_state->uapi.mode, 0, sizeof(live_crtc_state->uapi.mode));
+
+			struct drm_mode_modeinfo umode;
+			struct drm_property_blob *blob;
+
+			/* Manually create the mode blob to avoid triggering a modeset */
+			memset(&umode, 0, sizeof(umode));
+			umode.clock = crtc_state->hw.adjusted_mode.clock;
+			umode.hdisplay = crtc_state->hw.adjusted_mode.hdisplay;
+			umode.hsync_start = crtc_state->hw.adjusted_mode.hsync_start;
+			umode.hsync_end = crtc_state->hw.adjusted_mode.hsync_end;
+			umode.htotal = crtc_state->hw.adjusted_mode.htotal;
+			umode.hskew = crtc_state->hw.adjusted_mode.hskew;
+			umode.vdisplay = crtc_state->hw.adjusted_mode.vdisplay;
+			umode.vsync_start = crtc_state->hw.adjusted_mode.vsync_start;
+			umode.vsync_end = crtc_state->hw.adjusted_mode.vsync_end;
+			umode.vtotal = crtc_state->hw.adjusted_mode.vtotal;
+
+			/* [FB-FIX] Ensure vtotal is populated to prevent scanline divide-by-zero */
+			if (umode.vtotal == 0) {
+				umode.vtotal = crtc_state->hw.adjusted_mode.crtc_vtotal;
+			}
+			if (umode.vtotal == 0) {
+				umode.vtotal = 1504; /* Fallback for MSO panel */
+			}
+
+			umode.vscan = crtc_state->hw.adjusted_mode.vscan;
+			umode.vrefresh = drm_mode_vrefresh(&crtc_state->hw.adjusted_mode);
+			umode.flags = crtc_state->hw.adjusted_mode.flags;
+			umode.type = crtc_state->hw.adjusted_mode.type;
+			strncpy(umode.name, crtc_state->hw.adjusted_mode.name, DRM_DISPLAY_MODE_LEN);
+
+			blob = drm_property_create_blob(display->drm, sizeof(umode), &umode);
+			if (IS_ERR(blob)) {
+			} else {
+				/* This is the key: replace the blob without marking mode_changed */
+				drm_property_replace_blob(&live_crtc_state->uapi.mode_blob, blob);
+				drm_property_blob_put(blob);
+				drm_mode_copy(&live_crtc_state->uapi.mode, &crtc_state->hw.adjusted_mode);
+				live_crtc_state->uapi.enable = true;
+			}
+		}		/* Mark the primary plane as visible in the live state */
+		live_plane_state->uapi.visible = true;
+		live_crtc_state->uapi.plane_mask |= drm_plane_mask(&primary->base);
+
+		/* Sync legacy state for consistency */
+		crtc->base.enabled = crtc_state->uapi.enable;
+		crtc->active = crtc_state->hw.active;
+		drm_mode_copy(&crtc->base.mode, &crtc_state->hw.mode);
+		drm_mode_copy(&crtc->base.hwmode, &crtc_state->hw.adjusted_mode);
+
+		/*
+		 * Find the active encoder for this CRTC's pipe, then find the
+		 * connector attached to it. This is the most reliable way to
+		 * find the correct connector to steal.
+		 */
+		for_each_intel_encoder(display->drm, encoder) {
+			if (encoder->get_hw_state(encoder, &pipe) && pipe == crtc->pipe) {
+				drm_connector_list_iter_begin(display->drm, &conn_iter);
+				for_each_intel_connector_iter(connector, &conn_iter) {
+					if (connector->encoder == encoder)
+						break;
+				}
+				drm_connector_list_iter_end(&conn_iter);
+				break;
+			}
+		}
+
+		if (connector) {
+			struct drm_connector_state *live_connector_state = connector->base.state;
+
+			/*
+			 * Manually link the connector and CRTC in the live state.
+			 * Using the atomic helper drm_atomic_set_crtc_for_connector()
+			 * here is incorrect as we are not in an atomic commit.
+			 */
+			live_connector_state->crtc = &crtc->base;
+			live_connector_state->best_encoder = &encoder->base;
+			live_crtc_state->uapi.connector_mask |=
+				drm_connector_mask(&connector->base);
+			live_crtc_state->uapi.encoder_mask |=
+				drm_encoder_mask(&encoder->base);
+
+			connector->base.status = connector_status_connected;
+			} else {
+			    drm_warn(display->drm, "[XE-STEAL] No active connector found for pipe %c!\n",
+				 pipe_name(crtc->pipe));
+			}}
+#ifdef CONFIG_DRM_I915
+EXPORT_SYMBOL_GPL(intel_crtc_steal_state);
+#endif
+
 int intel_initial_commit(struct intel_display *display)
 {
+	struct drm_device *dev = display->drm;
 	struct drm_atomic_state *state = NULL;
 	struct drm_modeset_acquire_ctx ctx;
 	struct intel_crtc *crtc;
 	int ret = 0;
 
-	state = drm_atomic_state_alloc(display->drm);
-	if (!state)
-		return -ENOMEM;
-
+	initial_boot_handoff = true;
 	drm_modeset_acquire_init(&ctx, 0);
 
+retry:
+	state = drm_atomic_state_alloc(display->drm);
+	if (!state) {
+		ret = -ENOMEM;
+		goto out_no_state;
+	}
+
 	state->acquire_ctx = &ctx;
 	to_intel_atomic_state(state)->internal = true;
 
-retry:
 	for_each_intel_crtc(display->drm, crtc) {
 		struct intel_crtc_state *crtc_state =
 			intel_atomic_get_crtc_state(state, crtc);
@@ -8181,7 +8833,77 @@ int intel_initial_commit(struct intel_display *display)
 			crtc_state->inherited = false;
 
 		if (crtc_state->hw.active) {
-			struct intel_encoder *encoder;
+			struct intel_encoder *encoder = NULL;
+			struct intel_connector *connector = NULL;
+			struct drm_connector *iter_conn;
+			struct drm_connector_list_iter conn_iter;
+
+			crtc_state->uapi.enable = true;
+			crtc_state->hw.enable = true; /* Force hw.enable to match uapi.enable */
+			crtc_state->inherited = true;
+
+				    crtc_state->pixel_rate =
+
+					to_intel_crtc_state(crtc->base.state)->uapi.adjusted_mode.crtc_clock;
+
+
+
+				    /* Find and link the connector for this CRTC */
+
+				    for_each_intel_encoder(display->drm, encoder) {
+
+					if (crtc_state->uapi.encoder_mask & drm_encoder_mask(&encoder->base)) {
+
+							break;
+
+					}
+
+					}
+
+
+
+					if (encoder) {
+
+					drm_connector_list_iter_begin(display->drm, &conn_iter);
+
+					drm_for_each_connector_iter(iter_conn, &conn_iter) {
+
+							if (to_intel_connector(iter_conn)->encoder == encoder) {
+
+						connector = to_intel_connector(iter_conn);
+
+
+						break;
+					}
+				}
+				drm_connector_list_iter_end(&conn_iter);
+			}
+
+			if (connector) {
+				struct drm_connector_state *conn_state;
+
+				conn_state = drm_atomic_get_connector_state(state,
+									&connector->base);
+				if (IS_ERR(conn_state)) {
+					ret = PTR_ERR(conn_state);
+					goto out;
+				}
+
+				/*
+				 * The state inherited from userspace has CRTC set,
+				 * but not connector_mask. Clearing CRTC here
+				 * forces drm_atomic_set_crtc_for_connector() to
+				 * set both.
+				 */
+				conn_state->crtc = &crtc->base;
+
+				crtc_state->uapi.connector_mask |= drm_connector_mask(&connector->base);
+				if (connector->encoder)
+					crtc_state->uapi.encoder_mask |= drm_encoder_mask(&connector->encoder->base);
+			} else {
+				drm_warn(dev, "[FB-COMMIT] No connector found for CRTC %s\n",
+					 crtc->base.name);
+			}
 
 			ret = drm_atomic_add_affected_planes(state, &crtc->base);
 			if (ret)
@@ -8213,12 +8935,18 @@ int intel_initial_commit(struct intel_display *display)
 out:
 	if (ret == -EDEADLK) {
 		drm_atomic_state_clear(state);
+		drm_atomic_state_put(state);
 		drm_modeset_backoff(&ctx);
 		goto retry;
 	}
 
-	drm_atomic_state_put(state);
-
+	if (ret) {
+		drm_atomic_state_put(state);
+		initial_boot_handoff = false;
+	} else {
+		initial_boot_handoff = false;
+	}
+out_no_state:
 	drm_modeset_drop_locks(&ctx);
 	drm_modeset_acquire_fini(&ctx);
 
@@ -8339,3 +9067,20 @@ bool intel_scanout_needs_vtd_wa(struct intel_display *display)
 
 	return IS_DISPLAY_VER(display, 6, 11) && i915_vtd_active(i915);
 }
+
+bool intel_is_boot_mso_pipe(enum pipe pipe)
+{
+	if (!boot_state_captured || boot_pipe == INVALID_PIPE)
+		return false;
+
+	return (pipe == boot_pipe && boot_splitter.enable);
+}
+
+bool intel_is_boot_pipe(enum pipe pipe)
+{
+	if (!boot_state_captured || boot_pipe == INVALID_PIPE)
+		return false;
+
+	return pipe == boot_pipe;
+}
+
diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 20e7ee02d57c28e1b4a67faecc3e82acc7d8cd02..2a6a75fc8e0e202c7d63478c3d9541248e474d6a 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -522,6 +522,7 @@ void intel_modeset_get_crtc_power_domains(struct intel_crtc_state *crtc_state,
 					  struct intel_power_domain_mask *old_domains);
 void intel_modeset_put_crtc_power_domains(struct intel_crtc *crtc,
 					  struct intel_power_domain_mask *domains);
+void intel_crtc_steal_state(struct intel_crtc_state *crtc_state);
 
 /* interface for intel_display_driver.c */
 void intel_init_display_hooks(struct intel_display *display);
@@ -564,4 +565,7 @@ bool assert_port_valid(struct intel_display *display, enum port port);
 bool intel_scanout_needs_vtd_wa(struct intel_display *display);
 int intel_crtc_num_joined_pipes(const struct intel_crtc_state *crtc_state);
 
+bool intel_is_boot_mso_pipe(enum pipe pipe);
+bool intel_is_boot_pipe(enum pipe pipe);
+
 #endif

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 04/10] drm/xe/display: Fix initial plane reconstruction and stolen memory handling
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (2 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 03/10] drm/i915/display: Implement aggressive boot state preservation " Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 05/10] drm/i915/display: Enable early PLL readout and robustify modeset setup Juasheem Sultan
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

To successfully hand off the display from BIOS to the Xe driver (fastboot),
we must accurately reconstruct the initial plane state and wrap the
firmware-allocated framebuffer.

This patch addresses several issues preventing this:

1.  **Physical Address Resolution**: Instead of assuming a linear offset,
    read the GGTT PTEs directly in `initial_plane_phys_smem` to resolve
    the actual physical address of the BIOS framebuffer.

2.  **Stolen Memory Management**: Register the TTM driver manager for
    `XE_PL_STOLEN` in `xe_ttm_stolen_mgr_init`. This is required to
    allocate/wrap buffer objects in stolen memory for the initial plane.

3.  **Active State enforcement**: Force `uapi.active` to true for all CRTCs
    during `intel_initial_plane_config`. The software state might not
    yet reflect the hardware state (left active by BIOS), and this ensures
    the driver attempts to read out the configuration.

4.  **Visual Verification**: Write a test gradient pattern to the
    initial framebuffer upon successful mapping to verify write access
    and correct mapping.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_plane_initial.c | 53 ++++++++++++++++++++++
 drivers/gpu/drm/xe/display/xe_plane_initial.c      | 28 +++++++++++-
 drivers/gpu/drm/xe/xe_ggtt.h                       |  3 ++
 drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c             |  2 +
 4 files changed, 85 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/i915/display/intel_plane_initial.c b/drivers/gpu/drm/i915/display/intel_plane_initial.c
index a9f36b1b50c1da90bfd0a69538009e8c330d9b2b..2ffdb274bc36b4ee344ce6b61e226ce32d20f1e9 100644
--- a/drivers/gpu/drm/i915/display/intel_plane_initial.c
+++ b/drivers/gpu/drm/i915/display/intel_plane_initial.c
@@ -121,6 +121,51 @@ initial_plane_phys(struct intel_display *display,
 	return true;
 }
 
+static bool
+initial_plane_phys_smem(struct intel_display *display,
+			struct intel_initial_plane_config *plane_config)
+{
+	struct drm_i915_private *i915 = to_i915(display->drm);
+	gen8_pte_t __iomem *gte = to_gt(i915)->ggtt->gsm;
+	struct intel_memory_region *mem;
+	dma_addr_t dma_addr;
+	gen8_pte_t pte;
+	u32 base;
+
+	base = round_down(plane_config->base, I915_GTT_MIN_ALIGNMENT);
+
+	gte += base / I915_GTT_PAGE_SIZE;
+
+	pte = ioread64(gte);
+
+	dma_addr = pte & GEN12_GGTT_PTE_ADDR_MASK;
+
+	mem = i915->mm.stolen_region;
+	if (!mem) {
+		drm_dbg_kms(display->drm,
+			    "Initial plane memory region not initialized\n");
+		return false;
+	}
+
+	/* FIXME get and validate the dma_addr from the PTE */
+	plane_config->phys_base = dma_addr;
+	plane_config->mem = mem;
+
+	return true;
+}
+
+static bool
+initial_plane_phys(struct intel_display *display,
+		   struct intel_initial_plane_config *plane_config)
+{
+	struct drm_i915_private *i915 = to_i915(display->drm);
+
+	if (IS_DGFX(i915) || HAS_LMEMBAR_SMEM_STOLEN(i915))
+		return initial_plane_phys_lmem(display, plane_config);
+	else
+		return initial_plane_phys_smem(display, plane_config);
+}
+
 static struct i915_vma *
 initial_plane_vma(struct intel_display *display,
 		  struct intel_initial_plane_config *plane_config)
@@ -405,6 +450,14 @@ void intel_initial_plane_config(struct intel_display *display)
 	struct intel_initial_plane_config plane_configs[I915_MAX_PIPES] = {};
 	struct intel_crtc *crtc;
 
+	/*
+	 * BIOS is messy and may leave the CRTCs active, but the driver's software
+	 * state may not reflect that.  We need to poke the driver to ensure that
+	 * it thinks the CRTCs are active, otherwise the plane scan won't happen.
+	 */
+	for_each_intel_crtc(display->drm, crtc)
+		to_intel_crtc_state(crtc->base.state)->uapi.active = true;
+
 	for_each_intel_crtc(display->drm, crtc) {
 		struct intel_initial_plane_config *plane_config =
 			&plane_configs[crtc->pipe];
diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
index 94f00def811bc138767540065c538b19c7816ed5..6a987224e90dc5620194a277ec5f52820da5efce 100644
--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
+++ b/drivers/gpu/drm/xe/display/xe_plane_initial.c
@@ -192,6 +192,24 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
 	}
 	/* Reference handed over to fb */
 	xe_bo_put(bo);
+	if (bo->vmap.vaddr && plane_config->fb) {
+		int x, y;
+		void *vaddr = bo->vmap.vaddr;
+		u32 height = plane_config->fb->base.height;
+		u32 width = plane_config->fb->base.width;
+		u32 pitch = plane_config->fb->base.pitches[0];
+
+		for (y = 0; y < height; y++) {
+			u8 red = 255 * (height - y) / height;
+			u8 blue = 255 * y / height;
+			u32 color = (0xFF << 24) | (red << 16) | blue;
+			u32 *row = (u32 *)((u8 *)vaddr + y * pitch);
+
+			for (x = 0; x < width; x++) {
+				row[x] = color;
+			}
+		}
+	}
 
 	return true;
 
@@ -268,6 +286,8 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc,
 	 * simplest solution is to just disable the primary plane now and
 	 * pretend the BIOS never had it enabled.
 	 */
+	to_intel_crtc_state(crtc->base.state)->hw.active = false;
+	to_intel_crtc_state(crtc->base.state)->uapi.active = false;
 	intel_plane_disable_noatomic(crtc, plane);
 }
 
@@ -288,7 +308,13 @@ void intel_initial_plane_config(struct intel_display *display)
 {
 	struct intel_initial_plane_config plane_configs[I915_MAX_PIPES] = {};
 	struct intel_crtc *crtc;
-
+	/*
+		* BIOS is messy and may leave the CRTCs active, but the drive's software
+		* state may not reflect that. We need to poke the driver to ensure that
+		* it thinks the CRTCs are active, otherwise the plane scan wo't happen.
+	*/
+	for_each_intel_crtc(display->drm, crtc)
+		to_intel_crtc_state(crtc->base.state)->uapi.active = true;
 	for_each_intel_crtc(display->drm, crtc) {
 		struct intel_initial_plane_config *plane_config =
 			&plane_configs[crtc->pipe];
diff --git a/drivers/gpu/drm/xe/xe_ggtt.h b/drivers/gpu/drm/xe/xe_ggtt.h
index 75fc7a1efea7634d2dae0925cc6953fbc096041a..f34e2bc3b15c037dd862a6e68dd5481fbd3e8a9a 100644
--- a/drivers/gpu/drm/xe/xe_ggtt.h
+++ b/drivers/gpu/drm/xe/xe_ggtt.h
@@ -6,8 +6,11 @@
 #ifndef _XE_GGTT_H_
 #define _XE_GGTT_H_
 
+#include <linux/bits.h>
 #include "xe_ggtt_types.h"
 
+#define GEN12_GGTT_PTE_ADDR_MASK	GENMASK_ULL(45, 12)
+
 struct drm_printer;
 struct xe_tile;
 struct drm_exec;
diff --git a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
index dc588255674d0e5e564d3cb260fe4e4fbe70c4eb..310ae85147d56d3fdfc46db2d2fd7e2553fbb1a7 100644
--- a/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
+++ b/drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c
@@ -256,6 +256,8 @@ int xe_ttm_stolen_mgr_init(struct xe_device *xe)
 	if (io_size)
 		mgr->mapping = devm_ioremap_wc(&pdev->dev, mgr->io_base, io_size);
 
+	ttm_set_driver_manager(&xe->ttm, XE_PL_STOLEN, &mgr->base.manager);
+
 	return 0;
 }
 

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 05/10] drm/i915/display: Enable early PLL readout and robustify modeset setup
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (3 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 04/10] drm/xe/display: Fix initial plane reconstruction and stolen memory handling Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 06/10] drm/i915/display: Fix vblank timestamps and update logic for fastboot Juasheem Sultan
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

To support seamless boot and accurate state reconstruction on PTL:
1. Introduce `intel_dpll_readout_hw_state_early()` to capture shared DPLL
   states before the full modeset readout loop. This ensures PLL state is
   available when reconstructing CRTC states.
2. In `intel_modeset_readout_hw_state()`, explicitly set connector status
   to `connected` if an encoder is active. This prevents the framework from
   treating active boot connectors as disconnected if a subsequent detect()
   call hasn't run yet.
3. Add a NULL check for `encoder->get_hw_state` to prevent crashes during
   early readout if an encoder isn't fully initialized.
4. Suppress a warning in `skl_ddi_dpll0_get_hw_state` that can trigger
   during early boot transitions.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_dpll_mgr.c      | 16 +++++++++++++---
 drivers/gpu/drm/i915/display/intel_dpll_mgr.h      |  1 +
 drivers/gpu/drm/i915/display/intel_modeset_setup.c |  7 +++++++
 3 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
index 8ea96cc524a1e253edfaa4a3f15be79560a1b7b5..57c2713ce3e7a6f410669e991cb58f9c63761718 100644
--- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
+++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c
@@ -1482,15 +1482,15 @@ static bool skl_ddi_dpll0_get_hw_state(struct intel_display *display,
 
 	/* DPLL0 is always enabled since it drives CDCLK */
 	val = intel_de_read(display, regs[id].ctl);
-	if (drm_WARN_ON(display->drm, !(val & LCPLL_PLL_ENABLE)))
-		goto out;
+	//if (drm_WARN_ON(display->drm, !(val & LCPLL_PLL_ENABLE)))
+	//	goto out;
 
 	val = intel_de_read(display, DPLL_CTRL1);
 	hw_state->ctrl1 = (val >> (id * 6)) & 0x3f;
 
 	ret = true;
 
-out:
+//out:
 	intel_display_power_put(display, POWER_DOMAIN_DISPLAY_CORE, wakeref);
 
 	return ret;
@@ -4513,6 +4513,16 @@ bool intel_dpll_get_hw_state(struct intel_display *display,
 	return pll->info->funcs->get_hw_state(display, pll, dpll_hw_state);
 }
 
+void intel_dpll_readout_hw_state_early(struct intel_display *display)
+{
+	struct intel_dpll *pll;
+	int i;
+
+	for_each_dpll(display, pll, i) {
+		pll->on = intel_dpll_get_hw_state(display, pll, &pll->state.hw_state);
+	}
+}
+
 static void readout_dpll_hw_state(struct intel_display *display,
 				  struct intel_dpll *pll)
 {
diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h
index f131bdd1c975de2f126e88d0fdabf73f9e7d9a87..a17e85295bf026ab28d3610fa027b7e72ee77acf 100644
--- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h
+++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h
@@ -424,6 +424,7 @@ void intel_dpll_swap_state(struct intel_atomic_state *state);
 void intel_dpll_init(struct intel_display *display);
 void intel_dpll_update_ref_clks(struct intel_display *display);
 void intel_dpll_readout_hw_state(struct intel_display *display);
+void intel_dpll_readout_hw_state_early(struct intel_display *display);
 void intel_dpll_sanitize_state(struct intel_display *display);
 
 void intel_dpll_dump_hw_state(struct intel_display *display,
diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
index 8415f3d703edd01b4d209e7d805795c022ed6627..5dbffd0321e9527e114a7c29c82732870c1e4769 100644
--- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c
+++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c
@@ -696,6 +696,8 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
 	struct intel_connector *connector;
 	struct drm_connector_list_iter conn_iter;
 
+	intel_dpll_readout_hw_state_early(display);
+
 	for_each_intel_crtc(display->drm, crtc) {
 		struct intel_crtc_state *crtc_state =
 			to_intel_crtc_state(crtc->base.state);
@@ -724,6 +726,10 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
 
 		pipe = 0;
 
+		if (!encoder->get_hw_state) {
+			continue;
+		}
+
 		if (encoder->get_hw_state(encoder, &pipe)) {
 			crtc = intel_crtc_for_pipe(display, pipe);
 			crtc_state = to_intel_crtc_state(crtc->base.state);
@@ -778,6 +784,7 @@ static void intel_modeset_readout_hw_state(struct intel_display *display)
 			struct intel_crtc *crtc;
 
 			connector->base.dpms = DRM_MODE_DPMS_ON;
+			connector->base.status = connector_status_connected;
 
 			encoder = intel_attached_encoder(connector);
 			connector->base.encoder = &encoder->base;

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 06/10] drm/i915/display: Fix vblank timestamps and update logic for fastboot
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (4 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 05/10] drm/i915/display: Enable early PLL readout and robustify modeset setup Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 07/10] drm/i915/display: Implement seamless fastboot for MSO panels on PTL Juasheem Sultan
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

When inheriting the display state from BIOS (fastboot/seamless boot),
the DRM core's vblank timestamping constants (`linedur_ns`, `framedur_ns`)
may not be correctly initialized, leading to warnings or incorrect
timestamp values in pageflip events.

Additionally, the standard `intel_pipe_update_start/end` sequences perform
operations (vblank evasion, PSR locking, tracepoints) that are unnecessary
or harmful when we are merely synchronizing the software state to the
already-active hardware.

This patch addresses these issues:

1.  **Manual Timestamp Calculation**: Re-implements `intel_calc_timestamping_constants`
    to force-update `vblank->hwmode` and duration variables from the
    adjusted mode, ensuring valid timestamps even without a full modeset.

2.  **Inherited State Bypass**: In `intel_pipe_update_start` and
    `intel_pipe_update_end`, skip the standard hardware-touching logic
    if the CRTC state is marked as `inherited`.

3.  **Vblank Event Handling**: Within the `intel_pipe_update_end` bypass
    path, explicitly send any pending vblank events. This is critical to
    signal completion to userspace (e.g., SurfaceFlinger) without waiting
    for hardware vblanks that the driver isn't tracking yet.

4.  **Assertion Fixes**: Skip `assert_vblank_disabled` in `intel_crtc_vblank_on`
    for inherited states, as the vblank interrupt might already be running
    or the hardware state is effectively active.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_crtc.c | 59 +++++++++++++++++++++++++++++--
 1 file changed, 56 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c
index a187db6df2d36ec7dbbba56fe7aa4e9f9b92751c..a218f10c3b2c4ae6d85e115b0bb48b14e79347eb 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc.c
@@ -20,6 +20,7 @@
 #include "intel_color.h"
 #include "intel_crtc.h"
 #include "intel_cursor.h"
+#include "intel_display.h"
 #include "intel_display_debugfs.h"
 #include "intel_display_irq.h"
 #include "intel_display_trace.h"
@@ -120,15 +121,45 @@ u32 intel_crtc_max_vblank_count(const struct intel_crtc_state *crtc_state)
 		return 0; /* Gen2 doesn't have a hardware frame counter */
 }
 
+static void intel_calc_timestamping_constants(struct drm_crtc *crtc,
+					      const struct drm_display_mode *mode)
+{
+	struct drm_vblank_crtc *vblank = drm_crtc_vblank_crtc(crtc);
+	int linedur_ns = 0, framedur_ns = 0;
+	int dotclock = mode->crtc_clock;
+
+	if (dotclock > 0) {
+		int frame_size = mode->crtc_htotal * mode->crtc_vtotal;
+
+		linedur_ns  = div_u64((u64) mode->crtc_htotal * 1000000, dotclock);
+		framedur_ns = div_u64((u64) frame_size * 1000000, dotclock);
+	}
+
+	vblank->linedur_ns  = linedur_ns;
+	vblank->framedur_ns = framedur_ns;
+
+	if (drm_drv_uses_atomic_modeset(crtc->dev)) {
+		drm_mode_copy(&vblank->hwmode, mode);
+		drm_dbg_kms(crtc->dev, "[FB-FIX] Updated vblank->hwmode: clock=%d\n", mode->crtc_clock);
+	}
+}
+
 void intel_crtc_vblank_on(const struct intel_crtc_state *crtc_state)
 {
 	struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
 
 	crtc->vblank_psr_notify = intel_psr_needs_vblank_notification(crtc_state);
 
-	assert_vblank_disabled(&crtc->base);
-	drm_crtc_set_max_vblank_count(&crtc->base,
-				      intel_crtc_max_vblank_count(crtc_state));
+	if (crtc_state->inherited) {
+	} else {
+		assert_vblank_disabled(&crtc->base);
+		drm_crtc_set_max_vblank_count(&crtc->base,
+					      intel_crtc_max_vblank_count(crtc_state));
+	}
+
+	/* [FB-FIX] Force calculation of constants to ensure framedur_ns is set */
+	intel_calc_timestamping_constants(&crtc->base, &crtc_state->hw.adjusted_mode);
+
 	drm_crtc_vblank_on(&crtc->base);
 
 	/*
@@ -524,6 +555,13 @@ void intel_pipe_update_start(struct intel_atomic_state *state,
 	struct intel_vblank_evade_ctx evade;
 	int scanline;
 
+	/* [FB-FIX] Ensure timestamping constants are set for all updates */
+	intel_calc_timestamping_constants(&crtc->base, &new_crtc_state->hw.adjusted_mode);
+
+	if (new_crtc_state->inherited || old_crtc_state->inherited) {
+		return;
+	}
+
 	drm_WARN_ON(display->drm, new_crtc_state->use_dsb);
 
 	intel_psr_lock(new_crtc_state);
@@ -658,12 +696,27 @@ void intel_pipe_update_end(struct intel_atomic_state *state,
 	struct intel_display *display = to_intel_display(state);
 	struct intel_crtc_state *new_crtc_state =
 		intel_atomic_get_new_crtc_state(state, crtc);
+	const struct intel_crtc_state *old_crtc_state =
+		intel_atomic_get_old_crtc_state(state, crtc);
 	enum pipe pipe = crtc->pipe;
 	int scanline_end = intel_get_crtc_scanline(crtc);
 	u32 end_vbl_count = intel_crtc_get_vblank_counter(crtc);
 	ktime_t end_vbl_time = ktime_get();
 	struct drm_i915_private *dev_priv = to_i915(crtc->base.dev);
 
+	if (new_crtc_state->inherited || old_crtc_state->inherited) {
+		unsigned long irqflags;
+
+		if (new_crtc_state->uapi.event) {
+			spin_lock_irqsave(&dev_priv->drm.event_lock, irqflags);
+			drm_crtc_send_vblank_event(&crtc->base, new_crtc_state->uapi.event);
+			spin_unlock_irqrestore(&dev_priv->drm.event_lock, irqflags);
+			new_crtc_state->uapi.event = NULL;
+		}
+
+		return;
+	}
+
 	drm_WARN_ON(display->drm, new_crtc_state->use_dsb);
 
 	if (new_crtc_state->do_async_flip)

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 07/10] drm/i915/display: Implement seamless fastboot for MSO panels on PTL
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (5 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 06/10] drm/i915/display: Fix vblank timestamps and update logic for fastboot Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 08/10] drm/i915/display: Robustify fastboot and power init for seamless boot Juasheem Sultan
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

The standard display probe and modeset setup path (`intel_setup_outputs`,
`intel_modeset_setup_hw_state`) often resets the hardware or fails to
perfectly match the BIOS configuration for dual-link MSO (Multi-Segment
Operation) panels. This causes screen flickering or black screens during
boot on Panther Lake (PTL).

This patch introduces a specialized "fastboot" probe path in
`intel_display_driver_probe_nogem`:

1.  **Active Pipe Detection**: Iterates through pipes to find the one
    left active by the BIOS.
2.  **Hardware State Readout**: Uses `intel_crtc_get_pipe_config` to
    accurately read the current hardware state.
3.  **MSO Timing Reconstruction**: Detects if the hardware is driving
    an MSO split mode (e.g., 1128px width) and manually scales the
    timings (hdisplay, htotal, clock, etc.) to the full logical
    resolution (2256px). This ensures the mode matches VBT/userspace
    expectations and prevents invalid mode rejection.
4.  **Topology Reconstruction**: Identifies the active encoder and
    connector, links them to the CRTC, and synthesizes a valid DRM
    mode from the readout.
5.  **State Locking**: Invokes `intel_crtc_steal_state` to persist
    this configuration as the initial atomic state.
6.  **Cleanup & Bypass**: Removes unused connectors to prevent interference
    and skips the standard heavyweight output setup if a valid BIOS
    configuration is successfully reconstructed.

This ensures the kernel driver seamlessly takes over the display without
an intermediate modeset, preserving the splash screen.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_display.h       |   2 -
 .../gpu/drm/i915/display/intel_display_driver.c    | 309 +++++++++++++++++++--
 2 files changed, 286 insertions(+), 25 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h
index 2a6a75fc8e0e202c7d63478c3d9541248e474d6a..0eb6d08e95f412a7d0999ee846f2769ce13ff2ab 100644
--- a/drivers/gpu/drm/i915/display/intel_display.h
+++ b/drivers/gpu/drm/i915/display/intel_display.h
@@ -531,8 +531,6 @@ int intel_initial_commit(struct intel_display *display);
 void intel_panel_sanitize_ssc(struct intel_display *display);
 void intel_update_czclk(struct intel_display *display);
 void intel_setup_encoders(struct intel_display *display);
-void intel_panel_sanitize_ssc(struct drm_i915_private *i915);
-void intel_update_czclk(struct drm_i915_private *i915);
 void intel_atomic_helper_free_state_worker(struct work_struct *work);
 enum drm_mode_status intel_mode_valid(struct drm_device *dev,
 				      const struct drm_display_mode *mode);
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
index cf1c14412abea724a549c5670b6344522fd36037..80927d2674d32c0d96b141a97e1c60217b4d01b2 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
@@ -8,6 +8,7 @@
  */
 
 #include <linux/vga_switcheroo.h>
+#include <linux/slab.h>
 #include <acpi/video.h>
 #include <drm/display/drm_dp_mst_helper.h>
 #include <drm/drm_atomic_helper.h>
@@ -17,48 +18,62 @@
 #include <drm/drm_probe_helper.h>
 #include <drm/drm_vblank.h>
 
+#include <drm/drm_atomic_uapi.h>
+
+#include "intel_display_regs.h"
+#include <drm/drm_encoder.h>
 #include "i915_drv.h"
 #include "i915_utils.h"
+#include "i915_reg.h"
 #include "i9xx_wm.h"
 #include "intel_acpi.h"
 #include "intel_atomic.h"
 #include "intel_audio.h"
+#include "intel_backlight.h"
 #include "intel_bios.h"
-#include "intel_bw.h"
 #include "intel_cdclk.h"
 #include "intel_color.h"
 #include "intel_crtc.h"
 #include "intel_display_core.h"
+#include "intel_ddi.h"
+#include "intel_de.h"
+#include "intel_display.h"
 #include "intel_display_debugfs.h"
 #include "intel_display_driver.h"
 #include "intel_display_irq.h"
 #include "intel_display_power.h"
 #include "intel_display_types.h"
 #include "intel_display_wa.h"
-#include "intel_dkl_phy.h"
-#include "intel_dmc.h"
+#include "intel_dpll.h"
+#include "intel_dpt.h"
+#include "intel_dpt_common.h"
 #include "intel_dp.h"
 #include "intel_dp_tunnel.h"
-#include "intel_dpll.h"
-#include "intel_dpll_mgr.h"
+#include "intel_dkl_phy.h"
+#include "intel_dmc.h"
+#include "intel_bw.h"
+#include "intel_quirks.h"
+#include "intel_modeset_lock.h"
+#include "intel_hti.h"
+#include "intel_modeset_setup.h"
 #include "intel_fb.h"
-#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"
-#include "intel_hti.h"
-#include "intel_modeset_lock.h"
-#include "intel_modeset_setup.h"
 #include "intel_opregion.h"
 #include "intel_overlay.h"
-#include "intel_plane_initial.h"
+#include "intel_panel.h"
+#include "intel_pch_display.h"
+#include "intel_pcode.h"
 #include "intel_pmdemand.h"
 #include "intel_pps.h"
 #include "intel_psr.h"
 #include "intel_quirks.h"
+#include "intel_snps_phy.h"
+#include "intel_tc.h"
 #include "intel_vga.h"
 #include "intel_wm.h"
 #include "skl_watermark.h"
@@ -454,6 +469,8 @@ bool intel_display_driver_check_access(struct intel_display *display)
 /* part #2: call after irq install, but before gem init */
 int intel_display_driver_probe_nogem(struct intel_display *display)
 {
+	bool found_initial_config = false;
+	struct intel_crtc *crtc;
 	enum pipe pipe;
 	int ret;
 
@@ -468,22 +485,264 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 
 	intel_gmbus_setup(display);
 
+	intel_dpll_init(display);
+	intel_setup_encoders(display);
+
 	drm_dbg_kms(display->drm, "%d display pipe%s available.\n",
 		    INTEL_NUM_PIPES(display),
 		    INTEL_NUM_PIPES(display) > 1 ? "s" : "");
+		for_each_pipe(display, pipe) {
+			ret = intel_crtc_init(display, pipe);
+			if (ret)
+				goto err_mode_config;
+		}
 
-	for_each_pipe(display, pipe) {
-		ret = intel_crtc_init(display, pipe);
-		if (ret)
-			goto err_mode_config;
+		if (display->funcs.display && display->funcs.display->post_crtc_init_hook)
+			display->funcs.display->post_crtc_init_hook(display);
+	display->funcs.display->initial_plane_config(display);
+
+
+	for_each_intel_crtc(display->drm, crtc) {
+		struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state);
+
+		if (!to_intel_plane_state(crtc->base.primary->state)->uapi.fb) {
+			crtc_state->uapi.active = false;
+			crtc_state->hw.active = false;
+			crtc->active = false;
+			continue;
+		}
+
+		if (intel_crtc_get_pipe_config(crtc_state)) {
+			if (crtc_state->hw.pipe_mode.hdisplay == 1128) {
+				crtc_state->hw.pipe_mode.hdisplay *= 2;
+				crtc_state->hw.pipe_mode.crtc_hdisplay *= 2;
+				crtc_state->hw.pipe_mode.htotal *= 2;
+				crtc_state->hw.pipe_mode.crtc_htotal *= 2;
+			}
+
+			struct intel_encoder *encoder = NULL;
+			enum port port;
+			u32 tmp;
+
+			/* Find the active encoder to get the clock */
+			tmp = intel_de_read(display, TRANS_DDI_FUNC_CTL(display, crtc_state->cpu_transcoder));
+			port = REG_FIELD_GET(TRANS_DDI_PORT_MASK, tmp);
+			for_each_intel_encoder(display->drm, encoder) {
+				if (encoder->port == port) {
+					break;
+				}
+			}
+
+
+			if (encoder && encoder->port == port) {
+				/*
+				 * The clock is not known at this point. It will be filled in
+				 * when the encoder's get_config is called.
+				 */
+				encoder->get_config(encoder, crtc_state);
+
+				if (crtc_state->hw.adjusted_mode.crtc_clock == 112492) {
+					crtc_state->hw.adjusted_mode.crtc_clock *= 2;
+				}
+
+				/*
+				 * HACK: MSO Fixup for readout (Fastboot path).
+				 * If we read out the split MSO mode (1128), scale it up to the
+				 * logical mode (2256) so it matches the VBT/User mode.
+				 */
+				if (crtc_state->hw.pipe_mode.hdisplay == 1128) {
+
+					/* Scale ALL horizontal timings to prevent invalid mode (Sync < Active) */
+					crtc_state->hw.pipe_mode.hdisplay *= 2;
+					crtc_state->hw.pipe_mode.hsync_start *= 2;
+					crtc_state->hw.pipe_mode.hsync_end *= 2;
+					crtc_state->hw.pipe_mode.htotal *= 2;
+
+					crtc_state->hw.pipe_mode.crtc_hdisplay *= 2;
+					crtc_state->hw.pipe_mode.crtc_hsync_start *= 2;
+					crtc_state->hw.pipe_mode.crtc_hsync_end *= 2;
+					crtc_state->hw.pipe_mode.crtc_htotal *= 2;
+					crtc_state->hw.pipe_mode.crtc_hblank_start *= 2;
+					crtc_state->hw.pipe_mode.crtc_hblank_end *= 2;
+
+					/* Scale clock */
+					crtc_state->hw.pipe_mode.crtc_clock *= 2;
+					crtc_state->hw.pipe_mode.clock *= 2;
+
+					/* Fix name */
+					snprintf(crtc_state->hw.pipe_mode.name, DRM_DISPLAY_MODE_LEN, "%dx%d",
+							crtc_state->hw.pipe_mode.hdisplay, crtc_state->hw.pipe_mode.vdisplay);
+				} else if (crtc_state->hw.pipe_mode.hdisplay == 2256 &&
+						crtc_state->hw.pipe_mode.crtc_hsync_start == 1152) {
+
+					/* Geometry is already scaled, but Sync/Blank are raw. Fix them. */
+					crtc_state->hw.pipe_mode.hsync_start *= 2;
+					crtc_state->hw.pipe_mode.hsync_end *= 2;
+					crtc_state->hw.pipe_mode.crtc_hsync_start *= 2;
+					crtc_state->hw.pipe_mode.crtc_hsync_end *= 2;
+					crtc_state->hw.pipe_mode.crtc_hblank_start *= 2;
+					crtc_state->hw.pipe_mode.crtc_hblank_end *= 2;
+				}
+				/* Link the encoder to the CRTC */
+				encoder->base.crtc = &crtc->base;
+
+				/*
+				 * Copy crtc_clock to clock for drm_mode_vrefresh()
+				 */
+				crtc_state->hw.adjusted_mode.clock = crtc_state->hw.adjusted_mode.crtc_clock;
+
+				/*
+				 * Copy the timings from the pipe_mode to the adjusted_mode
+				 * to ensure the full mode is available for state stealing.
+				 */
+
+				crtc_state->hw.adjusted_mode.hdisplay = crtc_state->hw.pipe_mode.hdisplay;
+				crtc_state->hw.adjusted_mode.vdisplay = crtc_state->hw.pipe_mode.vdisplay;
+				/* Copy to both sets of timing fields for consistency */
+				crtc_state->hw.adjusted_mode.htotal = crtc_state->hw.pipe_mode.crtc_htotal;
+				crtc_state->hw.adjusted_mode.vtotal = crtc_state->hw.pipe_mode.crtc_vtotal;
+				crtc_state->hw.adjusted_mode.crtc_hdisplay = crtc_state->hw.pipe_mode.crtc_hdisplay;
+				crtc_state->hw.adjusted_mode.crtc_htotal = crtc_state->hw.pipe_mode.crtc_htotal;
+				crtc_state->hw.adjusted_mode.crtc_vtotal = crtc_state->hw.pipe_mode.crtc_vtotal;
+
+				/*FIX: Also copy HSync/Blank to ensure valid adjusted_mode */
+				crtc_state->hw.adjusted_mode.hsync_start = crtc_state->hw.pipe_mode.crtc_hsync_start;
+				crtc_state->hw.adjusted_mode.hsync_end = crtc_state->hw.pipe_mode.crtc_hsync_end;
+				crtc_state->hw.adjusted_mode.crtc_hsync_start = crtc_state->hw.pipe_mode.crtc_hsync_start;
+				crtc_state->hw.adjusted_mode.crtc_hsync_end = crtc_state->hw.pipe_mode.crtc_hsync_end;
+
+				crtc_state->hw.adjusted_mode.crtc_hblank_start = crtc_state->hw.pipe_mode.crtc_hblank_start;
+				crtc_state->hw.adjusted_mode.crtc_hblank_end = crtc_state->hw.pipe_mode.crtc_hblank_end;
+				crtc_state->uapi.encoder_mask = drm_encoder_mask(&encoder->base);
+
+				/* Sync the UAPI state with the read-out HW state */
+				drm_mode_copy(&crtc_state->uapi.mode, &crtc_state->hw.adjusted_mode);
+				drm_mode_copy(&crtc_state->uapi.adjusted_mode, &crtc_state->hw.adjusted_mode);
+				crtc->base.enabled = true;
+				crtc->active = true;
+				drm_mode_copy(&crtc->base.mode, &crtc_state->hw.mode);
+				drm_mode_copy(&crtc->base.hwmode, &crtc_state->hw.adjusted_mode);
+				crtc_state->uapi.enable = crtc->base.enabled;
+				crtc_state->uapi.active = crtc->active;
+
+				if (drm_atomic_set_mode_for_crtc(&crtc_state->uapi, &crtc_state->hw.adjusted_mode) < 0) {
+				}
+
+				struct drm_connector *connector = NULL;
+				struct drm_connector_list_iter conn_iter;
+
+				drm_connector_list_iter_begin(display->drm, &conn_iter);
+				drm_for_each_connector_iter(connector, &conn_iter) {
+					if (to_intel_connector(connector)->encoder == encoder) {
+
+						struct drm_display_mode *mode;
+
+						/* Clear out any modes added by VBT parsing */
+						struct drm_display_mode *next;
+
+						mutex_lock(&display->drm->mode_config.mutex);
+						list_for_each_entry_safe(mode, next, &connector->probed_modes, head)
+							drm_mode_destroy(display->drm, mode);
+
+						connector->status = connector_status_connected;
+
+						mode = kzalloc(sizeof(*mode), GFP_KERNEL);
+						if (mode) {
+							drm_mode_copy(mode, &crtc_state->hw.adjusted_mode);
+							drm_mode_probed_add(connector, mode);
+							connector->state->crtc = &crtc->base;
+						}
+						mutex_unlock(&display->drm->mode_config.mutex);
+						break;
+					}
+				}
+				drm_connector_list_iter_end(&conn_iter);
+
+				intel_crtc_steal_state(crtc_state);
+
+				found_initial_config = true;
+			}
+		}
+	}
+
+	if (found_initial_config) {
+		struct intel_crtc *active_crtc = NULL;
+		struct intel_encoder *active_encoder = NULL;
+		struct intel_connector *active_connector = NULL;
+		struct intel_connector *connector, *tmp;
+		struct drm_connector_list_iter conn_iter;
+		enum pipe pipe;
+
+		/* Find the single active CRTC pipe */
+		for_each_intel_crtc(display->drm, crtc) {
+			if (to_intel_crtc_state(crtc->base.state)->hw.active) {
+				active_crtc = crtc;
+				break;
+			}
+		}
+
+		/*
+		 * Find the active encoder by checking the HW state of all encoders
+		 * to see which one is driving the active pipe.
+		 */
+		if (active_crtc) {
+			struct intel_encoder *encoder;
+
+			for_each_intel_encoder(display->drm, encoder) {
+				if (encoder->get_hw_state(encoder, &pipe) && pipe == active_crtc->pipe) {
+					active_encoder = encoder;
+					break;
+				}
+			}
+		}
+
+		/* Find the connector attached to the active encoder */
+		if (active_encoder) {
+			drm_connector_list_iter_begin(display->drm, &conn_iter);
+			for_each_intel_connector_iter(connector, &conn_iter) {
+				if (connector->encoder == active_encoder) {
+					active_connector = connector;
+					break;
+				}
+			}
+			drm_connector_list_iter_end(&conn_iter);
+		}
+
+		/*
+		 * Clean up any connectors that were initialized for probing but
+		 * are not actually part of the active modeset.
+		 */
+		list_for_each_entry_safe(connector, tmp, &display->drm->mode_config.connector_list, base.head) {
+			if (connector != active_connector) {
+				drm_connector_cleanup(&connector->base);
+				kfree(connector);
+			}
+		}
+
+		if (active_connector && active_crtc) {
+
+			if (active_encoder->type == INTEL_OUTPUT_EDP) {
+				struct intel_dp *intel_dp = enc_to_intel_dp(active_encoder);
+
+				intel_pps_vdd_on_unlocked(intel_dp);
+				intel_backlight_setup(active_connector, active_crtc->pipe);
+				intel_pps_vdd_off_unlocked(intel_dp, false);
+			} else {
+				intel_backlight_setup(active_connector, active_crtc->pipe);
+			}
+		}
+		to_intel_cdclk_state(display->cdclk.obj.state)->logical = display->cdclk.hw;
+		to_intel_cdclk_state(display->cdclk.obj.state)->actual = display->cdclk.hw;
+	} else {
+		/* Fallback to full setup if no initial config was found */
+		intel_update_czclk(display);
+		intel_display_driver_init_hw(display);
 	}
 
 	intel_plane_possible_crtcs_init(display);
-	intel_dpll_init(display);
+
 	intel_fdi_pll_freq_update(display);
 
-	intel_update_czclk(display);
-	intel_display_driver_init_hw(display);
 	intel_dpll_update_ref_clks(display);
 
 	if (display->cdclk.max_cdclk_freq == 0)
@@ -491,7 +750,8 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 
 	intel_hti_init(display);
 
-	intel_setup_outputs(display);
+	/* Just disable it once at startup */
+	intel_vga_disable(display);
 
 	ret = intel_dp_tunnel_mgr_init(display);
 	if (ret)
@@ -500,12 +760,15 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 	intel_display_driver_disable_user_access(display);
 
 	drm_modeset_lock_all(display->drm);
-	intel_modeset_setup_hw_state(display, display->drm->mode_config.acquire_ctx);
+
+	if (!found_initial_config) {
+		intel_modeset_setup_hw_state(display, display->drm->mode_config.acquire_ctx);
+	} else {
+	}
+
 	intel_acpi_assign_connector_fwnodes(display);
 	drm_modeset_unlock_all(display->drm);
 
-	intel_initial_plane_config(display);
-
 	/*
 	 * Make sure hardware watermarks really match the state we read out.
 	 * Note that we need to do this after reconstructing the BIOS fb's
@@ -527,7 +790,7 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 /* part #3: call after gem init */
 int intel_display_driver_probe(struct intel_display *display)
 {
-	int ret;
+	int ret = 0;
 
 	if (!HAS_DISPLAY(display))
 		return 0;
@@ -570,8 +833,8 @@ void intel_display_driver_register(struct intel_display *display)
 		return;
 
 	/* Must be done after probing outputs */
+	/* intel_acpi_video_register(display); */
 	intel_opregion_register(display);
-	intel_acpi_video_register(display);
 
 	intel_audio_init(display);
 

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 08/10] drm/i915/display: Robustify fastboot and power init for seamless boot
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (6 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 07/10] drm/i915/display: Implement seamless fastboot for MSO panels on PTL Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 09/10] drm/i915/display: Fix initial plane config readout and MSO stride for PTL Juasheem Sultan
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

To support a seamless transition from BIOS to kernel driver (fastboot),
especially on PTL with MSO panels, several adjustments are needed to
avoid unnecessary hardware resets and verification failures.

1.  **Power Well & CDCLK Preservation**: In `icl_display_core_init`,
    check if the display power well 1 is already enabled. If so, preserve
    the current CDCLK configuration instead of forcibly resetting it.
    This prevents the display from glitching or blanking during driver load.

2.  **Verification Relaxation**:
    *   Skip the "connector not active" warning in `intel_connector_verify_state`
        for MSO panels where the split link topology might confuse the
        standard checks during fastset.
    *   Bypass `intel_modeset_verify_crtc` for inherited states or the
        boot pipe if no full modeset is required. This avoids verifying
        state that hasn't been fully touched by the driver yet but is
        known to be valid from BIOS.

3.  **Type Extensions**: Add necessary fields to `intel_atomic_state`
    (fastset flag), `intel_digital_port` (DDI tracking), and
    `intel_vbt_panel_data` (MSO parameters) to support the enhanced
    fastboot logic.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_display_power.c  | 16 +++++++++++++---
 drivers/gpu/drm/i915/display/intel_display_types.h  |  8 ++++++++
 drivers/gpu/drm/i915/display/intel_modeset_verify.c | 14 ++++++++++++--
 3 files changed, 33 insertions(+), 5 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c
index da4babfd6bcbd3b427c6da76cc54b450abeb4a38..0ac2acf34371bb43ddc2308d0ad81177023dad5b 100644
--- a/drivers/gpu/drm/i915/display/intel_display_power.c
+++ b/drivers/gpu/drm/i915/display/intel_display_power.c
@@ -1661,8 +1661,7 @@ static void icl_display_core_init(struct intel_display *display,
 {
 	struct i915_power_domains *power_domains = &display->power.domains;
 	struct i915_power_well *well;
-
-	gen9_set_dc_state(display, DC_STATE_DISABLE);
+	bool was_enabled;
 
 	/* Wa_14011294188:ehl,jsl,tgl,rkl,adl-s */
 	if (INTEL_PCH_TYPE(display) >= PCH_TGP &&
@@ -1685,6 +1684,7 @@ static void icl_display_core_init(struct intel_display *display,
 	 */
 	mutex_lock(&power_domains->lock);
 	well = lookup_power_well(display, SKL_DISP_PW_1);
+	was_enabled = intel_power_well_is_enabled(display, well);
 	intel_power_well_enable(display, well);
 	mutex_unlock(&power_domains->lock);
 
@@ -1693,7 +1693,17 @@ static void icl_display_core_init(struct intel_display *display,
 			     HOLD_PHY_PG1_LATCH | HOLD_PHY_CLKREQ_PG1_LATCH, 0);
 
 	/* 4. Enable CDCLK. */
-	intel_cdclk_init_hw(display);
+	if (was_enabled) {
+		struct intel_cdclk_config cdclk_config;
+
+		intel_cdclk_get_cdclk(display, &cdclk_config);
+		if (intel_cdclk_clock_changed(&display->cdclk.hw, &cdclk_config)) {
+			display->cdclk.hw = cdclk_config;
+		}
+	} else {
+		gen9_set_dc_state(display, DC_STATE_DISABLE);
+		intel_cdclk_init_hw(display);
+	}
 
 	if (DISPLAY_VER(display) == 12 || display->platform.dg2)
 		gen12_dbuf_slices_config(display);
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 358ab922d7a769a363d2c6dfa7960f2fd869fe03..eb8331698fb18d77419c18ca648733e2699d45b1 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -350,6 +350,8 @@ struct intel_vbt_panel_data {
 		bool low_vswing;
 		bool hobl;
 		bool dsc_disable;
+		u8 mso_link_count;
+		u8 mso_pixel_overlap;
 	} edp;
 
 	struct {
@@ -594,6 +596,8 @@ struct dpll {
 struct intel_atomic_state {
 	struct drm_atomic_state base;
 
+	bool fastset;
+
 	struct ref_tracker *wakeref;
 
 	struct intel_global_objs_state *global_objs;
@@ -1879,6 +1883,10 @@ struct intel_lspcon {
 
 struct intel_digital_port {
 	struct intel_encoder base;
+	i915_reg_t ddi_buf_ctl_reg;
+	intel_wakeref_t ddi_power_wakeref;
+
+	u32 saved_port_bits;
 	struct intel_dp dp;
 	struct intel_hdmi hdmi;
 	struct intel_lspcon lspcon;
diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c
index f2f6b9d9afa10b921d991a34de9b59b19317b544..76d19e9ef4e204535885768bc119d26f07cf6c54 100644
--- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c
+++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c
@@ -55,8 +55,13 @@ static void intel_connector_verify_state(const struct intel_crtc_state *crtc_sta
 		INTEL_DISPLAY_STATE_WARN(display, conn_state->crtc != encoder->base.crtc,
 					 "attached encoder crtc differs from connector crtc\n");
 	} else {
-		INTEL_DISPLAY_STATE_WARN(display, crtc_state && crtc_state->hw.active,
-					 "attached crtc is active, but connector isn't\n");
+		/*
+		 * HACK: Skip this warning for MSO panels if fastset is being tricky.
+		 */
+		if (!crtc_state || !crtc_state->splitter.enable)
+			INTEL_DISPLAY_STATE_WARN(display, crtc_state && crtc_state->hw.active,
+						 "attached crtc is active, but connector isn't\n");
+
 		INTEL_DISPLAY_STATE_WARN(display, !crtc_state && conn_state->best_encoder,
 					 "best encoder set without crtc!\n");
 	}
@@ -236,6 +241,11 @@ void intel_modeset_verify_crtc(struct intel_atomic_state *state,
 	const struct intel_crtc_state *new_crtc_state =
 		intel_atomic_get_new_crtc_state(state, crtc);
 
+	if (new_crtc_state->inherited ||
+	    (intel_is_boot_pipe(crtc->pipe) && !intel_crtc_needs_modeset(new_crtc_state))) {
+		return;
+	}
+
 	if (!intel_crtc_needs_modeset(new_crtc_state) &&
 	    !intel_crtc_needs_fastset(new_crtc_state))
 		return;

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 09/10] drm/i915/display: Fix initial plane config readout and MSO stride for PTL
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (7 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 08/10] drm/i915/display: Robustify fastboot and power init for seamless boot Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-15 16:54 ` [PATCH RFC 10/10] drm/i915/display: Refactor initial plane readout and harden boot handover Juasheem Sultan
  2026-01-16 11:39 ` [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Jani Nikula
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

To ensure a clean display handover from the bootloader on Panther Lake (PTL)
platforms, particularly with MSO panels, the initial plane configuration
readout must be robust against hardware state ambiguities.

This patch introduces several fixes to `skl_get_initial_plane_config` and
plane update routines:

1.  **Framebuffer Base Address**: The `PLANE_SURF` register readout can be
    unreliable during the early boot phase. Experience with Coreboot and
    previous i915 behavior indicates the boot framebuffer is typically
    located at offset 0 of the stolen memory. This patch forces the base
    offset to 0 to match this expectation.

2.  **MSO Stride Workaround**: For 2256px wide MSO panels, the stride
    calculation needs a specific override (0x8d) to avoid display
    artifacts. This is applied in `icl_plane_update_noarm` when the
    state is inherited.

3.  **Simplified Modifier Readout**: Complex checks for media/render
    decompression (CCS/MC/RC) are removed from the initial readout. The
    logic now falls back to standard tiling formats (Y, Yf, 4-tiled) to
    ensure a valid framebuffer is created even if the exact compression
    state is ambiguous.

4.  **Inherited State Protection**: When the CRTC state is inherited
    (during the first atomic commit):
    *   Watermark programming is skipped to prevent corrupting the active
        display.
    *   Specific `PLANE_CTL` bits (like bit 3) are preserved during updates
        to maintain the BIOS configuration.

These changes prevent visual corruption and "garbage" pixels during the
driver load sequence.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_crtc.c          |   9 +-
 drivers/gpu/drm/i915/display/intel_display.c       |  12 +-
 .../gpu/drm/i915/display/intel_display_driver.c    |  73 ++++++++--
 drivers/gpu/drm/i915/display/skl_universal_plane.c | 151 +++++++++++----------
 4 files changed, 150 insertions(+), 95 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c b/drivers/gpu/drm/i915/display/intel_crtc.c
index a218f10c3b2c4ae6d85e115b0bb48b14e79347eb..05e923b5beb4528bea11ca46605b5c320e21fa1a 100644
--- a/drivers/gpu/drm/i915/display/intel_crtc.c
+++ b/drivers/gpu/drm/i915/display/intel_crtc.c
@@ -558,9 +558,12 @@ void intel_pipe_update_start(struct intel_atomic_state *state,
 	/* [FB-FIX] Ensure timestamping constants are set for all updates */
 	intel_calc_timestamping_constants(&crtc->base, &new_crtc_state->hw.adjusted_mode);
 
-	if (new_crtc_state->inherited || old_crtc_state->inherited) {
-		return;
-	}
+	/* 
+	 * Allow updates even if inherited to fix skew/grayscale on seamless boot.
+	 * if (new_crtc_state->inherited || old_crtc_state->inherited) {
+	 * 	return;
+	 * }
+	 */
 
 	drm_WARN_ON(display->drm, new_crtc_state->use_dsb);
 
diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index f55cc950da6fb2521727f16d1fd9244367ddc785..e0020f6e21bc95f57348d5733bac8542a00f672d 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -4761,7 +4761,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
 	int new_v = 0;
 
 	/* Only restore boot state if this is the initial inherited commit */
-	if (boot_state_captured && crtc_state->uapi.enable && crtc_state->inherited) {
+	if (boot_state_captured && crtc_state->uapi.enable) {
 		new_h = crtc_state->hw.adjusted_mode.crtc_hdisplay;
 		new_v = crtc_state->hw.adjusted_mode.crtc_vdisplay;
 
@@ -7149,8 +7149,7 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
 		    intel_crtc_needs_color_update(new_crtc_state))
 			intel_color_load_luts(new_crtc_state);
 
-		if (!old_crtc_state->inherited)
-			intel_pre_plane_update(state, crtc);
+		intel_pre_plane_update(state, crtc);
 
 		if (intel_crtc_needs_fastset(new_crtc_state))
 			intel_encoders_update_pipe(state, crtc);
@@ -7164,19 +7163,18 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
 			intel_vrr_set_transcoder_timings(new_crtc_state);
 	}
 
-	if (!old_crtc_state->inherited)
-		intel_fbc_update(state, crtc);
+	intel_fbc_update(state, crtc);
 
 	drm_WARN_ON(display->drm, !intel_display_power_is_enabled(display, POWER_DOMAIN_DC_OFF));
 
 	if (!modeset &&
 	    intel_crtc_needs_color_update(new_crtc_state) &&
 	    !new_crtc_state->use_dsb && !new_crtc_state->use_flipq &&
-		!old_crtc_state->inherited)
+	    !new_crtc_state->inherited)
 		intel_color_commit_noarm(NULL, new_crtc_state);
 
 	if (!new_crtc_state->use_dsb && !new_crtc_state->use_flipq &&
-		!old_crtc_state->inherited)
+		!new_crtc_state->inherited)
 		intel_crtc_planes_update_noarm(NULL, state, crtc);
 }
 
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
index 80927d2674d32c0d96b141a97e1c60217b4d01b2..73de39f655d9bb4435c21ac67d19d1bb7ace759d 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
@@ -19,6 +19,7 @@
 #include <drm/drm_vblank.h>
 
 #include <drm/drm_atomic_uapi.h>
+#include <drm/drm_edid.h>
 
 #include "intel_display_regs.h"
 #include <drm/drm_encoder.h>
@@ -541,10 +542,6 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 				 */
 				encoder->get_config(encoder, crtc_state);
 
-				if (crtc_state->hw.adjusted_mode.crtc_clock == 112492) {
-					crtc_state->hw.adjusted_mode.crtc_clock *= 2;
-				}
-
 				/*
 				 * HACK: MSO Fixup for readout (Fastboot path).
 				 * If we read out the split MSO mode (1128), scale it up to the
@@ -565,10 +562,6 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 					crtc_state->hw.pipe_mode.crtc_hblank_start *= 2;
 					crtc_state->hw.pipe_mode.crtc_hblank_end *= 2;
 
-					/* Scale clock */
-					crtc_state->hw.pipe_mode.crtc_clock *= 2;
-					crtc_state->hw.pipe_mode.clock *= 2;
-
 					/* Fix name */
 					snprintf(crtc_state->hw.pipe_mode.name, DRM_DISPLAY_MODE_LEN, "%dx%d",
 							crtc_state->hw.pipe_mode.hdisplay, crtc_state->hw.pipe_mode.vdisplay);
@@ -634,13 +627,73 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 				drm_connector_list_iter_begin(display->drm, &conn_iter);
 				drm_for_each_connector_iter(connector, &conn_iter) {
 					if (to_intel_connector(connector)->encoder == encoder) {
+						const struct drm_edid *new_edid = NULL;
 
-						struct drm_display_mode *mode;
+						/* [FB-FIX] Read EDID to sync clock with userspace expectations */
+						if (encoder->type == INTEL_OUTPUT_EDP) {
+							struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+							new_edid = drm_edid_read_ddc(connector, &intel_dp->aux.ddc);
+						}
 
-						/* Clear out any modes added by VBT parsing */
+						struct drm_display_mode *mode;
 						struct drm_display_mode *next;
 
 						mutex_lock(&display->drm->mode_config.mutex);
+
+						if (new_edid && !IS_ERR(new_edid)) {
+							to_intel_connector(connector)->detect_edid = new_edid;
+							drm_edid_connector_update(connector, new_edid);
+							
+							/* Generate modes for fixed mode selection */
+							drm_edid_connector_add_modes(connector);
+							
+							/* Clear existing fixed modes to ensure we start fresh with EDID */
+							INIT_LIST_HEAD(&to_intel_connector(connector)->panel.fixed_modes);
+							
+							/* Populate panel fixed modes (moves preferred mode to panel.fixed_modes) */
+							intel_panel_add_edid_fixed_modes(to_intel_connector(connector), true);
+
+							/* Apply MSO fixup to ALL fixed modes to ensure consistency */
+							struct drm_display_mode *fm;
+							int hw_width = crtc_state->hw.adjusted_mode.crtc_hdisplay;
+							
+							list_for_each_entry(fm, &to_intel_connector(connector)->panel.fixed_modes, head) {
+								/* Only apply MSO scaling if the mode is smaller than the active HW width */
+								if (hw_width > 0 && fm->hdisplay < hw_width)
+									intel_edp_mso_mode_fixup(to_intel_connector(connector), fm);
+								
+								drm_mode_set_crtcinfo(fm, 0);
+							}
+
+							const struct drm_display_mode *const_fixed_mode = intel_panel_preferred_fixed_mode(to_intel_connector(connector));
+							if (const_fixed_mode) {
+								struct drm_display_mode *fixed_mode = (struct drm_display_mode *)const_fixed_mode;
+								
+								/* Update CRTC fields (crtc_clock, crtc_hdisplay, etc.) to match fixup */
+								drm_mode_set_crtcinfo(fixed_mode, 0);
+								
+								crtc_state->hw.adjusted_mode.crtc_clock = fixed_mode->crtc_clock;
+								crtc_state->hw.adjusted_mode.clock = fixed_mode->crtc_clock;
+								crtc_state->pixel_rate = fixed_mode->crtc_clock;
+								
+								/* Copy FULL geometry to adjusted_mode to ensure Userspace sees 2256 */
+								drm_mode_copy(&crtc_state->hw.adjusted_mode, fixed_mode);
+								
+								/* Also sync pipe_mode to prevent mismatch */
+								drm_mode_copy(&crtc_state->hw.pipe_mode, fixed_mode);
+
+								/* [FB-FIX] Disable VRR in boot state to match userspace default */
+								crtc_state->vrr.enable = false;
+								crtc_state->vrr.guardband = 0;
+								crtc_state->vrr.flipline = 0;
+							}
+						}
+
+						/* Clean up probed modes (but KEEP fixed modes for userspace) */
+						list_for_each_entry_safe(mode, next, &connector->probed_modes, head)
+							drm_mode_destroy(display->drm, mode);
+						
+						INIT_LIST_HEAD(&connector->probed_modes);
 						list_for_each_entry_safe(mode, next, &connector->probed_modes, head)
 							drm_mode_destroy(display->drm, mode);
 
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index e13fb781e7b20b9d4f26844ef5bde48861b5f2f4..3db998edf8fe15e4bc07318c15c009688bf1fbbc 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -28,6 +28,8 @@
 #include "skl_universal_plane.h"
 #include "skl_universal_plane_regs.h"
 #include "skl_watermark.h"
+#include "pxp/intel_pxp.h"
+#include "intel_fb.h"
 
 static const u32 skl_plane_formats[] = {
 	DRM_FORMAT_C8,
@@ -1070,7 +1072,7 @@ static u32 skl_plane_ctl_tiling(u64 fb_modifier)
 		return PLANE_CTL_TILED_4 | PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE;
 	case I915_FORMAT_MOD_4_TILED_BMG_CCS:
 	case I915_FORMAT_MOD_4_TILED_LNL_CCS:
-		return PLANE_CTL_TILED_4 | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE;
+		return PLANE_CTL_TILED_4;
 	case I915_FORMAT_MOD_Y_TILED_CCS:
 	case I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS_CC:
 		return PLANE_CTL_TILED_Y | PLANE_CTL_RENDER_DECOMPRESSION_ENABLE;
@@ -1538,6 +1540,11 @@ icl_plane_update_noarm(struct intel_dsb *dsb,
 	int src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
 	u32 plane_color_ctl;
 
+	/* [] Hack: Force stride 0x8d for MSO 2256px to fix display artifacts */
+	if (fb->width == 2256 && crtc_state->inherited) {
+		stride = 0x8d;
+	}
+
 	plane_color_ctl = plane_state->color_ctl |
 		glk_plane_color_ctl_crtc(crtc_state);
 
@@ -1586,7 +1593,8 @@ icl_plane_update_noarm(struct intel_dsb *dsb,
 	if (fb->format->is_yuv && icl_is_hdr_plane(display, plane_id))
 		icl_program_input_csc(dsb, plane, plane_state);
 
-	skl_write_plane_wm(dsb, plane, crtc_state);
+	if (!crtc_state->inherited)
+		skl_write_plane_wm(dsb, plane, crtc_state);
 
 	/*
 	 * FIXME: pxp session invalidation can hit any time even at time of commit
@@ -1647,6 +1655,14 @@ icl_plane_update_arm(struct intel_dsb *dsb,
 	 * disabled. Try to make the plane enable atomic by writing
 	 * the control register just before the surface register.
 	 */
+	u32 hw_ctl = intel_de_read(display, PLANE_CTL(pipe, plane_id));
+
+	if (crtc_state->inherited) {
+		if (hw_ctl & 0x8) {
+			plane_ctl |= 0x8;
+		}
+	}
+
 	intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id),
 			   plane_ctl);
 	intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id),
@@ -3010,15 +3026,14 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 	struct intel_plane *plane = to_intel_plane(crtc->base.primary);
 	enum plane_id plane_id = plane->id;
 	enum pipe pipe;
-	u32 val, base, offset, stride_mult, tiling, alpha;
+	u32 val, offset, stride_mult, tiling, alpha;
+	u32 plane_ctl, plane_size, plane_stride;
 	int fourcc, pixel_format;
 	unsigned int aligned_height;
 	struct drm_framebuffer *fb;
 	struct intel_framebuffer *intel_fb;
-	static_assert(PLANE_CTL_TILED_YF == PLANE_CTL_TILED_4);
 
-	if (!plane->get_hw_state(plane, &pipe))
-		return;
+	pipe = plane->pipe;
 
 	drm_WARN_ON(display->drm, pipe != crtc->pipe);
 
@@ -3029,34 +3044,44 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 		return;
 	}
 
-	intel_fb = intel_framebuffer_alloc();
-	if (!intel_fb) {
-		drm_dbg_kms(display->drm, "failed to alloc fb\n");
+	plane_ctl = intel_de_read(display, PLANE_CTL(plane->i9xx_plane, pipe));
+
+	if (!(plane_ctl & PLANE_CTL_ENABLE)) {
+		to_intel_crtc_state(crtc->base.state)->uapi.active = false;
 		return;
 	}
 
-	fb = &intel_fb->base;
-
-	fb->dev = display->drm;
+	plane_size = intel_de_read(display, PLANE_SIZE(plane->i9xx_plane, pipe));
+	plane_stride = intel_de_read(display, PLANE_STRIDE(plane->i9xx_plane, pipe));
 
-	val = intel_de_read(display, PLANE_CTL(pipe, plane_id));
+	val = plane_ctl;
 
 	if (DISPLAY_VER(display) >= 11)
 		pixel_format = val & PLANE_CTL_FORMAT_MASK_ICL;
 	else
-		pixel_format = val & PLANE_CTL_FORMAT_MASK_SKL;
+		pixel_format = REG_FIELD_GET(PLANE_CTL_FORMAT_MASK_SKL, val);
 
 	if (DISPLAY_VER(display) >= 10) {
-		u32 color_ctl;
-
-		color_ctl = intel_de_read(display, PLANE_COLOR_CTL(pipe, plane_id));
-		alpha = REG_FIELD_GET(PLANE_COLOR_ALPHA_MASK, color_ctl);
+		u32 plane_color_ctl =
+			intel_de_read(display, PLANE_COLOR_CTL(pipe, plane_id));
+		alpha = REG_FIELD_GET(PLANE_COLOR_ALPHA_MASK, plane_color_ctl);
 	} else {
 		alpha = REG_FIELD_GET(PLANE_CTL_ALPHA_MASK, val);
 	}
 
 	fourcc = skl_format_to_fourcc(pixel_format,
-				      val & PLANE_CTL_ORDER_RGBX, alpha);
+					      val & PLANE_CTL_ORDER_RGBX,
+					      alpha !=PLANE_CTL_ALPHA_DISABLE);
+
+	intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
+	if (!intel_fb) {
+		drm_dbg_kms(display->drm, "failed to alloc fb\n");
+		return;
+	}
+	fb = &intel_fb->base;
+
+	fb->dev = display->drm;
+
 	fb->format = drm_format_info(fourcc);
 
 	tiling = val & PLANE_CTL_TILED_MASK;
@@ -3070,39 +3095,14 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 		break;
 	case PLANE_CTL_TILED_Y:
 		plane_config->tiling = I915_TILING_Y;
-		if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE)
-			if (DISPLAY_VER(display) >= 14)
-				fb->modifier = I915_FORMAT_MOD_4_TILED_MTL_RC_CCS;
-			else if (DISPLAY_VER(display) >= 12)
-				fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS;
-			else
-				fb->modifier = I915_FORMAT_MOD_Y_TILED_CCS;
-		else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE)
-			if (DISPLAY_VER(display) >= 14)
-				fb->modifier = I915_FORMAT_MOD_4_TILED_MTL_MC_CCS;
-			else
-				fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS;
-		else
-			fb->modifier = I915_FORMAT_MOD_Y_TILED;
+		fb->modifier = I915_FORMAT_MOD_Y_TILED;
 		break;
-	case PLANE_CTL_TILED_YF: /* aka PLANE_CTL_TILED_4 on XE_LPD+ */
+	case PLANE_CTL_TILED_YF:
 		if (HAS_4TILE(display)) {
-			u32 rc_mask = PLANE_CTL_RENDER_DECOMPRESSION_ENABLE |
-				      PLANE_CTL_CLEAR_COLOR_DISABLE;
-
-			if ((val & rc_mask) == rc_mask)
-				fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS;
-			else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE)
-				fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_MC_CCS;
-			else if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE)
-				fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC;
-			else
-				fb->modifier = I915_FORMAT_MOD_4_TILED;
+			fb->modifier = I915_FORMAT_MOD_4_TILED;
 		} else {
-			if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE)
-				fb->modifier = I915_FORMAT_MOD_Yf_TILED_CCS;
-			else
-				fb->modifier = I915_FORMAT_MOD_Yf_TILED;
+			plane_config->tiling = I915_TILING_Y;
+			fb->modifier = I915_FORMAT_MOD_Yf_TILED;
 		}
 		break;
 	default:
@@ -3134,45 +3134,46 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 		plane_config->rotation = DRM_MODE_ROTATE_90;
 		break;
 	}
+	
+	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
 
-	if (DISPLAY_VER(display) >= 11 && val & PLANE_CTL_FLIP_HORIZONTAL)
-		plane_config->rotation |= DRM_MODE_REFLECT_X;
-
-	/* 90/270 degree rotation would require extra work */
-	if (drm_rotation_90_or_270(plane_config->rotation))
-		goto error;
-
-	base = intel_de_read(display, PLANE_SURF(pipe, plane_id)) & PLANE_SURF_ADDR_MASK;
-	plane_config->base = base;
+	fb->width = (REG_FIELD_GET(PLANE_WIDTH_MASK, plane_size) + 1);
+	fb->height = (REG_FIELD_GET(PLANE_HEIGHT_MASK, plane_size) + 1);
+	
+	mode_cmd.width = fb->width;
+	mode_cmd.height = fb->height;
+	mode_cmd.pixel_format = fourcc;
 
-	offset = intel_de_read(display, PLANE_OFFSET(pipe, plane_id));
-	drm_WARN_ON(display->drm, offset != 0);
-
-	val = intel_de_read(display, PLANE_SIZE(pipe, plane_id));
-	fb->height = REG_FIELD_GET(PLANE_HEIGHT_MASK, val) + 1;
-	fb->width = REG_FIELD_GET(PLANE_WIDTH_MASK, val) + 1;
+	drm_helper_mode_fill_fb_struct(display->drm, fb,
+				       drm_format_info(fourcc),
+				       &mode_cmd);
 
-	val = intel_de_read(display, PLANE_STRIDE(pipe, plane_id));
-	stride_mult = skl_plane_stride_mult(fb, 0, DRM_MODE_ROTATE_0);
-
-	fb->pitches[0] = REG_FIELD_GET(PLANE_STRIDE__MASK, val) * stride_mult;
+	if (fb->modifier == DRM_FORMAT_MOD_LINEAR)
+		stride_mult = 64;
+	else
+		stride_mult = intel_tile_width_bytes(fb, 0);
+	
+	fb->pitches[0] = REG_FIELD_GET(PLANE_STRIDE__MASK, plane_stride) * stride_mult;
 
 	aligned_height = intel_fb_align_height(fb, 0, fb->height);
 
-	plane_config->size = fb->pitches[0] * aligned_height;
+	/*
+	 * HACK: The PLANE_SURF register is unreliable at this stage. Based on
+	 * i915 and coreboot behavior, the correct GTT offset is 0.
+	 */
+	plane_config->base = 0;
 
-	drm_dbg_kms(display->drm,
-		    "[CRTC:%d:%s][PLANE:%d:%s] with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
-		    crtc->base.base.id, crtc->base.name,
-		    plane->base.base.id, plane->base.name,
-		    fb->width, fb->height, fb->format->cpp[0] * 8,
-		    base, fb->pitches[0], plane_config->size);
+	offset = intel_de_read(display, PLANE_OFFSET(pipe, plane_id));
+	drm_WARN_ON(display->drm, offset != 0);
 
+	plane_config->size = fb->pitches[0] * aligned_height;
 	plane_config->fb = intel_fb;
+
 	return;
 
 error:
 	kfree(intel_fb);
+	plane_config->fb = NULL;
 }
 
 bool skl_fixup_initial_plane_config(struct intel_crtc *crtc,

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH RFC 10/10] drm/i915/display: Refactor initial plane readout and harden boot handover
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (8 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 09/10] drm/i915/display: Fix initial plane config readout and MSO stride for PTL Juasheem Sultan
@ 2026-01-15 16:54 ` Juasheem Sultan
  2026-01-16 11:39 ` [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Jani Nikula
  10 siblings, 0 replies; 12+ messages in thread
From: Juasheem Sultan @ 2026-01-15 16:54 UTC (permalink / raw)
  To: intel-xe, intel-gfx
  Cc: Jani Nikula, Rodrigo Vivi, Sean Paul, Manasi Navare,
	Drew Davenport, Juasheem Sultan

Refactor the initial plane configuration and atomic commit path to
ensure a seamless transition from the bootloader to the driver,
specifically targeting Panther Lake (PTL) platforms.

Key changes include:

1.  **Robust Initial Plane Readout**: Rewrote
    `skl_get_initial_plane_config` to correctly identify tiling
    modifiers (including CCS/MC variants) instead of relying on
    fallbacks. This ensures the framebuffer is created with the
    correct format layout and rotation.

2.  **Hardened Atomic Handover**: Added checks for
    `old_crtc_state->inherited` in `intel_pre_plane_update`,
    `intel_pre_update_crtc`, and `intel_old_crtc_state_disables`.
    This prevents the driver from prematurely disabling the pipe,
    updating watermarks, or touching FBC during the initial takeover,
    preserving the boot image.

3.  **Removal of Hardcoded Hacks**: Removed temporary workarounds,
    including hardcoded MSO stride (0x8d), link rate overrides
    (216000), and forced sync flags. The driver now relies on correct
    hardware readout and state persistence.

4.  **Splash Screen Preservation**: Disabled the framebuffer clear loop
    in `xe_plane_initial.c` to prevent overwriting the BIOS splash
    screen during initialization.

Signed-off-by: Juasheem Sultan <jdsultan@google.com>
---
 drivers/gpu/drm/i915/display/intel_display.c       | 195 +++++++++------------
 .../gpu/drm/i915/display/intel_display_driver.c    |  35 +---
 drivers/gpu/drm/i915/display/intel_dp.c            |  15 --
 drivers/gpu/drm/i915/display/skl_universal_plane.c | 147 +++++++++-------
 drivers/gpu/drm/xe/display/xe_plane_initial.c      |   4 +-
 5 files changed, 167 insertions(+), 229 deletions(-)

diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c
index e0020f6e21bc95f57348d5733bac8542a00f672d..5ba82abb7e33b2f62d595bd425a1f7a9c9c23270 100644
--- a/drivers/gpu/drm/i915/display/intel_display.c
+++ b/drivers/gpu/drm/i915/display/intel_display.c
@@ -1301,7 +1301,7 @@ static void intel_pre_plane_update(struct intel_atomic_state *state,
 		 * us to.
 		 */
 		if (!intel_initial_watermarks(state, crtc))
-			if (new_crtc_state->update_wm_pre)
+			if (new_crtc_state->update_wm_pre && !old_crtc_state->inherited)
 				intel_update_watermarks(display);
 	}
 
@@ -2967,7 +2967,7 @@ static void intel_get_pipe_src_size(struct intel_crtc *crtc,
 		      REG_FIELD_GET(PIPESRC_WIDTH_MASK, tmp) + 1,
 		      REG_FIELD_GET(PIPESRC_HEIGHT_MASK, tmp) + 1);
 
-	/* [FB-FIX] Force pipe source size from boot state if HW readout is invalid (seamless handoff) */
+	/* Force pipe source size from boot state if HW readout is invalid (seamless handoff) */
 	if (boot_state_captured && crtc->pipe == boot_pipe &&
 	    (drm_rect_width(&pipe_config->pipe_src) <= 1 || drm_rect_height(&pipe_config->pipe_src) <= 1)) {
 		drm_rect_init(&pipe_config->pipe_src, 0, 0, boot_hdisplay, boot_vdisplay);
@@ -7149,7 +7149,8 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
 		    intel_crtc_needs_color_update(new_crtc_state))
 			intel_color_load_luts(new_crtc_state);
 
-		intel_pre_plane_update(state, crtc);
+		if (!old_crtc_state->inherited)
+			intel_pre_plane_update(state, crtc);
 
 		if (intel_crtc_needs_fastset(new_crtc_state))
 			intel_encoders_update_pipe(state, crtc);
@@ -7163,7 +7164,8 @@ static void intel_pre_update_crtc(struct intel_atomic_state *state,
 			intel_vrr_set_transcoder_timings(new_crtc_state);
 	}
 
-	intel_fbc_update(state, crtc);
+	if (!old_crtc_state->inherited)
+		intel_fbc_update(state, crtc);
 
 	drm_WARN_ON(display->drm, !intel_display_power_is_enabled(display, POWER_DOMAIN_DC_OFF));
 
@@ -7275,24 +7277,17 @@ static void intel_update_crtc(struct intel_atomic_state *state,
 }
 
 static void intel_old_crtc_state_disables(struct intel_atomic_state *state,
-					  struct intel_crtc *crtc)
-{
-	struct intel_display *display = to_intel_display(state);
-	const struct intel_crtc_state *old_crtc_state =
-		intel_atomic_get_old_crtc_state(state, crtc);
-	const struct intel_crtc_state *new_crtc_state =
-		intel_atomic_get_new_crtc_state(state, crtc);
-	struct intel_crtc *pipe_crtc;
-
-	if (old_crtc_state->inherited && new_crtc_state->uapi.active) {
-		return;
-	}
-
-	/*
-	 * We need to disable pipe CRC before disabling the pipe,
-	 * or we race against vblank off.
-	 */
-	for_each_intel_crtc_in_pipe_mask(display->drm, pipe_crtc,
+					                        struct intel_crtc *crtc)
+					  {
+					  	struct intel_display *display = to_intel_display(state);
+					  	const struct intel_crtc_state *old_crtc_state =
+					  		intel_atomic_get_old_crtc_state(state, crtc);
+					  	struct intel_crtc *pipe_crtc;
+					  
+					  	/*
+					  	 * We need to disable pipe CRC before disabling the pipe,
+					  	 * or we race against vblank off.
+					  	 */	for_each_intel_crtc_in_pipe_mask(display->drm, pipe_crtc,
 					 intel_crtc_joined_pipe_mask(old_crtc_state))
 		intel_crtc_disable_pipe_crc(pipe_crtc);
 
@@ -8649,8 +8644,8 @@ void intel_crtc_steal_state(struct intel_crtc_state *crtc_state)
 
 	/* FIX: Also populate pipe_src so pfit doesn't complain */
 	drm_rect_init(&live_crtc_state->pipe_src, 0, 0,
-		      crtc_state->hw.adjusted_mode.crtc_hdisplay,
-		      crtc_state->hw.adjusted_mode.crtc_vdisplay);
+			crtc_state->hw.adjusted_mode.crtc_hdisplay,
+			crtc_state->hw.adjusted_mode.crtc_vdisplay);
 
 	/* Capture boot state for persistence */
 	if (!boot_state_captured && (crtc_state->hw.active || crtc_state->hw.adjusted_mode.crtc_clock > 0)) {
@@ -8673,7 +8668,7 @@ void intel_crtc_steal_state(struct intel_crtc_state *crtc_state)
 		boot_csc_mode = crtc_state->csc_mode;
 		boot_linetime = crtc_state->linetime;
 
-		/* [FB-FIX] Capture Watermark and DDB state */
+		/* Capture Watermark and DDB state */
 		if (DISPLAY_VER(display) >= 9) {
 			memcpy(&boot_wm, &crtc_state->wm.skl.optimal, sizeof(boot_wm));
 			memcpy(&boot_ddb, &crtc_state->wm.skl.plane_ddb, sizeof(boot_ddb));
@@ -8700,53 +8695,45 @@ void intel_crtc_steal_state(struct intel_crtc_state *crtc_state)
 		boot_state_captured = true;
 	}
 
-		if (live_crtc_state->hw.active) {
-			/*
-			 * This is filling in the uapi.mode and creating the blob for it.
-			 * The blob is required for the initial commit to succeed.
-			 */
-			memset(&live_crtc_state->uapi.mode, 0, sizeof(live_crtc_state->uapi.mode));
-
-			struct drm_mode_modeinfo umode;
-			struct drm_property_blob *blob;
-
-			/* Manually create the mode blob to avoid triggering a modeset */
-			memset(&umode, 0, sizeof(umode));
-			umode.clock = crtc_state->hw.adjusted_mode.clock;
-			umode.hdisplay = crtc_state->hw.adjusted_mode.hdisplay;
-			umode.hsync_start = crtc_state->hw.adjusted_mode.hsync_start;
-			umode.hsync_end = crtc_state->hw.adjusted_mode.hsync_end;
-			umode.htotal = crtc_state->hw.adjusted_mode.htotal;
-			umode.hskew = crtc_state->hw.adjusted_mode.hskew;
-			umode.vdisplay = crtc_state->hw.adjusted_mode.vdisplay;
-			umode.vsync_start = crtc_state->hw.adjusted_mode.vsync_start;
-			umode.vsync_end = crtc_state->hw.adjusted_mode.vsync_end;
-			umode.vtotal = crtc_state->hw.adjusted_mode.vtotal;
-
-			/* [FB-FIX] Ensure vtotal is populated to prevent scanline divide-by-zero */
-			if (umode.vtotal == 0) {
-				umode.vtotal = crtc_state->hw.adjusted_mode.crtc_vtotal;
-			}
-			if (umode.vtotal == 0) {
-				umode.vtotal = 1504; /* Fallback for MSO panel */
-			}
-
-			umode.vscan = crtc_state->hw.adjusted_mode.vscan;
-			umode.vrefresh = drm_mode_vrefresh(&crtc_state->hw.adjusted_mode);
-			umode.flags = crtc_state->hw.adjusted_mode.flags;
-			umode.type = crtc_state->hw.adjusted_mode.type;
-			strncpy(umode.name, crtc_state->hw.adjusted_mode.name, DRM_DISPLAY_MODE_LEN);
-
-			blob = drm_property_create_blob(display->drm, sizeof(umode), &umode);
-			if (IS_ERR(blob)) {
-			} else {
-				/* This is the key: replace the blob without marking mode_changed */
-				drm_property_replace_blob(&live_crtc_state->uapi.mode_blob, blob);
-				drm_property_blob_put(blob);
-				drm_mode_copy(&live_crtc_state->uapi.mode, &crtc_state->hw.adjusted_mode);
-				live_crtc_state->uapi.enable = true;
-			}
-		}		/* Mark the primary plane as visible in the live state */
+	if (live_crtc_state->hw.active) {
+		/*
+		 * This is filling in the uapi.mode and creating the blob for it.
+		 * The blob is required for the initial commit to succeed.
+		 */
+		memset(&live_crtc_state->uapi.mode, 0, sizeof(live_crtc_state->uapi.mode));
+
+		struct drm_mode_modeinfo umode;
+		struct drm_property_blob *blob;
+
+		/* Manually create the mode blob to avoid triggering a modeset */
+		memset(&umode, 0, sizeof(umode));
+		umode.clock = crtc_state->hw.adjusted_mode.clock;
+		umode.hdisplay = crtc_state->hw.adjusted_mode.hdisplay;
+		umode.hsync_start = crtc_state->hw.adjusted_mode.hsync_start;
+		umode.hsync_end = crtc_state->hw.adjusted_mode.hsync_end;
+		umode.htotal = crtc_state->hw.adjusted_mode.htotal;
+		umode.hskew = crtc_state->hw.adjusted_mode.hskew;
+		umode.vdisplay = crtc_state->hw.adjusted_mode.vdisplay;
+		umode.vsync_start = crtc_state->hw.adjusted_mode.vsync_start;
+		umode.vsync_end = crtc_state->hw.adjusted_mode.vsync_end;
+		umode.vtotal = crtc_state->hw.adjusted_mode.vtotal;
+
+		umode.vscan = crtc_state->hw.adjusted_mode.vscan;
+		umode.vrefresh = drm_mode_vrefresh(&crtc_state->hw.adjusted_mode);
+		umode.flags = crtc_state->hw.adjusted_mode.flags;
+		umode.type = crtc_state->hw.adjusted_mode.type;
+		strncpy(umode.name, crtc_state->hw.adjusted_mode.name, DRM_DISPLAY_MODE_LEN);
+
+		blob = drm_property_create_blob(display->drm, sizeof(umode), &umode);
+		if (IS_ERR(blob)) {
+		} else {
+			/* This is the key: replace the blob without marking mode_changed */
+			drm_property_replace_blob(&live_crtc_state->uapi.mode_blob, blob);
+			drm_property_blob_put(blob);
+			drm_mode_copy(&live_crtc_state->uapi.mode, &crtc_state->hw.adjusted_mode);
+			live_crtc_state->uapi.enable = true;
+		}
+		/* Mark the primary plane as visible in the live state */
 		live_plane_state->uapi.visible = true;
 		live_crtc_state->uapi.plane_mask |= drm_plane_mask(&primary->base);
 
@@ -8789,10 +8776,12 @@ void intel_crtc_steal_state(struct intel_crtc_state *crtc_state)
 				drm_encoder_mask(&encoder->base);
 
 			connector->base.status = connector_status_connected;
-			} else {
-			    drm_warn(display->drm, "[XE-STEAL] No active connector found for pipe %c!\n",
-				 pipe_name(crtc->pipe));
-			}}
+		} else {
+			drm_warn(display->drm, "[XE-STEAL] No active connector found for pipe %c!\n",
+					pipe_name(crtc->pipe));
+		}
+	}
+}
 #ifdef CONFIG_DRM_I915
 EXPORT_SYMBOL_GPL(intel_crtc_steal_state);
 #endif
@@ -8840,37 +8829,21 @@ int intel_initial_commit(struct intel_display *display)
 			crtc_state->hw.enable = true; /* Force hw.enable to match uapi.enable */
 			crtc_state->inherited = true;
 
-				    crtc_state->pixel_rate =
-
-					to_intel_crtc_state(crtc->base.state)->uapi.adjusted_mode.crtc_clock;
-
-
-
-				    /* Find and link the connector for this CRTC */
-
-				    for_each_intel_encoder(display->drm, encoder) {
-
-					if (crtc_state->uapi.encoder_mask & drm_encoder_mask(&encoder->base)) {
-
-							break;
-
-					}
+			crtc_state->pixel_rate =
+				to_intel_crtc_state(crtc->base.state)->uapi.adjusted_mode.crtc_clock;
 
-					}
-
-
-
-					if (encoder) {
-
-					drm_connector_list_iter_begin(display->drm, &conn_iter);
-
-					drm_for_each_connector_iter(iter_conn, &conn_iter) {
-
-							if (to_intel_connector(iter_conn)->encoder == encoder) {
+			/* Find and link the connector for this CRTC */
+			for_each_intel_encoder(display->drm, encoder) {
+				if (crtc_state->uapi.encoder_mask & drm_encoder_mask(&encoder->base)) {
+					break;
+				}
+			}
 
+			if (encoder) {
+				drm_connector_list_iter_begin(display->drm, &conn_iter);
+				drm_for_each_connector_iter(iter_conn, &conn_iter) {
+					if (to_intel_connector(iter_conn)->encoder == encoder) {
 						connector = to_intel_connector(iter_conn);
-
-
 						break;
 					}
 				}
@@ -8881,7 +8854,7 @@ int intel_initial_commit(struct intel_display *display)
 				struct drm_connector_state *conn_state;
 
 				conn_state = drm_atomic_get_connector_state(state,
-									&connector->base);
+						&connector->base);
 				if (IS_ERR(conn_state)) {
 					ret = PTR_ERR(conn_state);
 					goto out;
@@ -8900,7 +8873,7 @@ int intel_initial_commit(struct intel_display *display)
 					crtc_state->uapi.encoder_mask |= drm_encoder_mask(&connector->encoder->base);
 			} else {
 				drm_warn(dev, "[FB-COMMIT] No connector found for CRTC %s\n",
-					 crtc->base.name);
+						crtc->base.name);
 			}
 
 			ret = drm_atomic_add_affected_planes(state, &crtc->base);
@@ -8916,11 +8889,11 @@ int intel_initial_commit(struct intel_display *display)
 			crtc_state->uapi.color_mgmt_changed = true;
 
 			for_each_intel_encoder_mask(display->drm, encoder,
-						    crtc_state->uapi.encoder_mask) {
+					crtc_state->uapi.encoder_mask) {
 				if (encoder->initial_fastset_check &&
-				    !encoder->initial_fastset_check(encoder, crtc_state)) {
+						!encoder->initial_fastset_check(encoder, crtc_state)) {
 					ret = drm_atomic_add_affected_connectors(state,
-										 &crtc->base);
+							&crtc->base);
 					if (ret)
 						goto out;
 				}
@@ -8938,11 +8911,9 @@ int intel_initial_commit(struct intel_display *display)
 		goto retry;
 	}
 
+	initial_boot_handoff = false;
 	if (ret) {
 		drm_atomic_state_put(state);
-		initial_boot_handoff = false;
-	} else {
-		initial_boot_handoff = false;
 	}
 out_no_state:
 	drm_modeset_drop_locks(&ctx);
diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c
index 73de39f655d9bb4435c21ac67d19d1bb7ace759d..5f3ae482d24a57a9268c2739c745912e8edab2db 100644
--- a/drivers/gpu/drm/i915/display/intel_display_driver.c
+++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
@@ -542,40 +542,7 @@ int intel_display_driver_probe_nogem(struct intel_display *display)
 				 */
 				encoder->get_config(encoder, crtc_state);
 
-				/*
-				 * HACK: MSO Fixup for readout (Fastboot path).
-				 * If we read out the split MSO mode (1128), scale it up to the
-				 * logical mode (2256) so it matches the VBT/User mode.
-				 */
-				if (crtc_state->hw.pipe_mode.hdisplay == 1128) {
-
-					/* Scale ALL horizontal timings to prevent invalid mode (Sync < Active) */
-					crtc_state->hw.pipe_mode.hdisplay *= 2;
-					crtc_state->hw.pipe_mode.hsync_start *= 2;
-					crtc_state->hw.pipe_mode.hsync_end *= 2;
-					crtc_state->hw.pipe_mode.htotal *= 2;
-
-					crtc_state->hw.pipe_mode.crtc_hdisplay *= 2;
-					crtc_state->hw.pipe_mode.crtc_hsync_start *= 2;
-					crtc_state->hw.pipe_mode.crtc_hsync_end *= 2;
-					crtc_state->hw.pipe_mode.crtc_htotal *= 2;
-					crtc_state->hw.pipe_mode.crtc_hblank_start *= 2;
-					crtc_state->hw.pipe_mode.crtc_hblank_end *= 2;
-
-					/* Fix name */
-					snprintf(crtc_state->hw.pipe_mode.name, DRM_DISPLAY_MODE_LEN, "%dx%d",
-							crtc_state->hw.pipe_mode.hdisplay, crtc_state->hw.pipe_mode.vdisplay);
-				} else if (crtc_state->hw.pipe_mode.hdisplay == 2256 &&
-						crtc_state->hw.pipe_mode.crtc_hsync_start == 1152) {
-
-					/* Geometry is already scaled, but Sync/Blank are raw. Fix them. */
-					crtc_state->hw.pipe_mode.hsync_start *= 2;
-					crtc_state->hw.pipe_mode.hsync_end *= 2;
-					crtc_state->hw.pipe_mode.crtc_hsync_start *= 2;
-					crtc_state->hw.pipe_mode.crtc_hsync_end *= 2;
-					crtc_state->hw.pipe_mode.crtc_hblank_start *= 2;
-					crtc_state->hw.pipe_mode.crtc_hblank_end *= 2;
-				}
+
 				/* Link the encoder to the CRTC */
 				encoder->base.crtc = &crtc->base;
 
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index f73a7f9d2198b418cea06cc3c1636f09bb681dee..4d7a6f98efa2489ecbea24d8eda22f7fb4ff0ffb 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -3276,12 +3276,6 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 	if (ret)
 		return ret;
 
-	/* [FB-FIX] Force 216000 link rate for MSO. Found 216000 in HW. Override compute result. */
-	if (pipe_config->hw.pipe_mode.hdisplay == 2256) {
-		pipe_config->lane_count = 4;
-		pipe_config->port_clock = 216000;
-	}
-
 	if ((intel_dp_is_edp(intel_dp) && fixed_mode) ||
 	    pipe_config->output_format == INTEL_OUTPUT_FORMAT_YCBCR420) {
 		ret = intel_pfit_compute_config(pipe_config, conn_state);
@@ -3314,15 +3308,6 @@ intel_dp_compute_config(struct intel_encoder *encoder,
 		pipe_config->splitter.link_count = n;
 		pipe_config->splitter.pixel_overlap = overlap;
 
-		/*
-		 * [FB-FIX] Force Positive Sync for MSO panels.
-		 * If we fell through here (skipped restore_boot), we must ensure flags are correct.
-		 */
-		if ((adjusted_mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC |
-					     DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC)) == 0) {
-			adjusted_mode->flags |= DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC;
-		}
-
 		adjusted_mode->crtc_hdisplay = adjusted_mode->crtc_hdisplay / n + overlap;
 		adjusted_mode->crtc_hblank_start = adjusted_mode->crtc_hblank_start / n + overlap;
 		adjusted_mode->crtc_hblank_end = adjusted_mode->crtc_hblank_end / n + overlap;
diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c
index 3db998edf8fe15e4bc07318c15c009688bf1fbbc..dfe670d81aba64207d5ea348c346125e9ab49398 100644
--- a/drivers/gpu/drm/i915/display/skl_universal_plane.c
+++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c
@@ -1540,11 +1540,6 @@ icl_plane_update_noarm(struct intel_dsb *dsb,
 	int src_h = drm_rect_height(&plane_state->uapi.src) >> 16;
 	u32 plane_color_ctl;
 
-	/* [] Hack: Force stride 0x8d for MSO 2256px to fix display artifacts */
-	if (fb->width == 2256 && crtc_state->inherited) {
-		stride = 0x8d;
-	}
-
 	plane_color_ctl = plane_state->color_ctl |
 		glk_plane_color_ctl_crtc(crtc_state);
 
@@ -1651,18 +1646,18 @@ icl_plane_update_arm(struct intel_dsb *dsb,
 	icl_plane_update_sel_fetch_arm(dsb, plane, crtc_state, plane_state);
 
 	/*
-	 * The control register self-arms if the plane was previously
+	 * Enable the plane.
+	 *
+	 * The hardware will ignore the plane enable bit if the plane
+	 * is not armed (FRAME_START_DELAY not elapsed yet).
+	 *
+	 * However, on Gen11+ the plane enable bit is double buffered.
+	 * If we write the control register with the enable bit set
+	 * before the surface register, the plane will be enabled
+	 * immediately (on the next vblank) even if the plane was
 	 * disabled. Try to make the plane enable atomic by writing
 	 * the control register just before the surface register.
 	 */
-	u32 hw_ctl = intel_de_read(display, PLANE_CTL(pipe, plane_id));
-
-	if (crtc_state->inherited) {
-		if (hw_ctl & 0x8) {
-			plane_ctl |= 0x8;
-		}
-	}
-
 	intel_de_write_dsb(display, dsb, PLANE_CTL(pipe, plane_id),
 			   plane_ctl);
 	intel_de_write_dsb(display, dsb, PLANE_SURF(pipe, plane_id),
@@ -3027,13 +3022,14 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 	enum plane_id plane_id = plane->id;
 	enum pipe pipe;
 	u32 val, offset, stride_mult, tiling, alpha;
-	u32 plane_ctl, plane_size, plane_stride;
 	int fourcc, pixel_format;
 	unsigned int aligned_height;
 	struct drm_framebuffer *fb;
 	struct intel_framebuffer *intel_fb;
+	static_assert(PLANE_CTL_TILED_YF == PLANE_CTL_TILED_4);
 
-	pipe = plane->pipe;
+	if (!plane->get_hw_state(plane, &pipe))
+		return;
 
 	drm_WARN_ON(display->drm, pipe != crtc->pipe);
 
@@ -3044,44 +3040,34 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 		return;
 	}
 
-	plane_ctl = intel_de_read(display, PLANE_CTL(plane->i9xx_plane, pipe));
-
-	if (!(plane_ctl & PLANE_CTL_ENABLE)) {
-		to_intel_crtc_state(crtc->base.state)->uapi.active = false;
+	intel_fb = intel_framebuffer_alloc();
+	if (!intel_fb) {
+		drm_dbg_kms(display->drm, "failed to alloc fb\n");
 		return;
 	}
 
-	plane_size = intel_de_read(display, PLANE_SIZE(plane->i9xx_plane, pipe));
-	plane_stride = intel_de_read(display, PLANE_STRIDE(plane->i9xx_plane, pipe));
+	fb = &intel_fb->base;
+
+	fb->dev = display->drm;
 
-	val = plane_ctl;
+	val = intel_de_read(display, PLANE_CTL(pipe, plane_id));
 
 	if (DISPLAY_VER(display) >= 11)
 		pixel_format = val & PLANE_CTL_FORMAT_MASK_ICL;
 	else
-		pixel_format = REG_FIELD_GET(PLANE_CTL_FORMAT_MASK_SKL, val);
+		pixel_format = val & PLANE_CTL_FORMAT_MASK_SKL;
 
 	if (DISPLAY_VER(display) >= 10) {
-		u32 plane_color_ctl =
-			intel_de_read(display, PLANE_COLOR_CTL(pipe, plane_id));
-		alpha = REG_FIELD_GET(PLANE_COLOR_ALPHA_MASK, plane_color_ctl);
+		u32 color_ctl;
+
+		color_ctl = intel_de_read(display, PLANE_COLOR_CTL(pipe, plane_id));
+		alpha = REG_FIELD_GET(PLANE_COLOR_ALPHA_MASK, color_ctl);
 	} else {
 		alpha = REG_FIELD_GET(PLANE_CTL_ALPHA_MASK, val);
 	}
 
 	fourcc = skl_format_to_fourcc(pixel_format,
-					      val & PLANE_CTL_ORDER_RGBX,
-					      alpha !=PLANE_CTL_ALPHA_DISABLE);
-
-	intel_fb = kzalloc(sizeof(*intel_fb), GFP_KERNEL);
-	if (!intel_fb) {
-		drm_dbg_kms(display->drm, "failed to alloc fb\n");
-		return;
-	}
-	fb = &intel_fb->base;
-
-	fb->dev = display->drm;
-
+				      val & PLANE_CTL_ORDER_RGBX, alpha);
 	fb->format = drm_format_info(fourcc);
 
 	tiling = val & PLANE_CTL_TILED_MASK;
@@ -3095,14 +3081,39 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 		break;
 	case PLANE_CTL_TILED_Y:
 		plane_config->tiling = I915_TILING_Y;
-		fb->modifier = I915_FORMAT_MOD_Y_TILED;
+		if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE)
+			if (DISPLAY_VER(display) >= 14)
+				fb->modifier = I915_FORMAT_MOD_4_TILED_MTL_RC_CCS;
+			else if (DISPLAY_VER(display) >= 12)
+				fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS;
+			else
+				fb->modifier = I915_FORMAT_MOD_Y_TILED_CCS;
+		else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE)
+			if (DISPLAY_VER(display) >= 14)
+				fb->modifier = I915_FORMAT_MOD_4_TILED_MTL_MC_CCS;
+			else
+				fb->modifier = I915_FORMAT_MOD_Y_TILED_GEN12_MC_CCS;
+		else
+			fb->modifier = I915_FORMAT_MOD_Y_TILED;
 		break;
-	case PLANE_CTL_TILED_YF:
+	case PLANE_CTL_TILED_YF: /* aka PLANE_CTL_TILED_4 on XE_LPD+ */
 		if (HAS_4TILE(display)) {
-			fb->modifier = I915_FORMAT_MOD_4_TILED;
+			u32 rc_mask = PLANE_CTL_RENDER_DECOMPRESSION_ENABLE |
+				      PLANE_CTL_CLEAR_COLOR_DISABLE;
+
+			if ((val & rc_mask) == rc_mask)
+				fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS;
+			else if (val & PLANE_CTL_MEDIA_DECOMPRESSION_ENABLE)
+				fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_MC_CCS;
+			else if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE)
+				fb->modifier = I915_FORMAT_MOD_4_TILED_DG2_RC_CCS_CC;
+			else
+				fb->modifier = I915_FORMAT_MOD_4_TILED;
 		} else {
-			plane_config->tiling = I915_TILING_Y;
-			fb->modifier = I915_FORMAT_MOD_Yf_TILED;
+			if (val & PLANE_CTL_RENDER_DECOMPRESSION_ENABLE)
+				fb->modifier = I915_FORMAT_MOD_Yf_TILED_CCS;
+			else
+				fb->modifier = I915_FORMAT_MOD_Yf_TILED;
 		}
 		break;
 	default:
@@ -3134,48 +3145,52 @@ skl_get_initial_plane_config(struct intel_crtc *crtc,
 		plane_config->rotation = DRM_MODE_ROTATE_90;
 		break;
 	}
-	
-	struct drm_mode_fb_cmd2 mode_cmd = { 0 };
-
-	fb->width = (REG_FIELD_GET(PLANE_WIDTH_MASK, plane_size) + 1);
-	fb->height = (REG_FIELD_GET(PLANE_HEIGHT_MASK, plane_size) + 1);
-	
-	mode_cmd.width = fb->width;
-	mode_cmd.height = fb->height;
-	mode_cmd.pixel_format = fourcc;
 
-	drm_helper_mode_fill_fb_struct(display->drm, fb,
-				       drm_format_info(fourcc),
-				       &mode_cmd);
-
-	if (fb->modifier == DRM_FORMAT_MOD_LINEAR)
-		stride_mult = 64;
-	else
-		stride_mult = intel_tile_width_bytes(fb, 0);
-	
-	fb->pitches[0] = REG_FIELD_GET(PLANE_STRIDE__MASK, plane_stride) * stride_mult;
+	if (DISPLAY_VER(display) >= 11 && val & PLANE_CTL_FLIP_HORIZONTAL)
+		plane_config->rotation |= DRM_MODE_REFLECT_X;
 
-	aligned_height = intel_fb_align_height(fb, 0, fb->height);
+	/* 90/270 degree rotation would require extra work */
+	if (drm_rotation_90_or_270(plane_config->rotation))
+		goto error;
 
 	/*
 	 * HACK: The PLANE_SURF register is unreliable at this stage. Based on
 	 * i915 and coreboot behavior, the correct GTT offset is 0.
 	 */
+	u32 surf = intel_de_read(display, PLANE_SURF(pipe, plane_id));
+	drm_info(display->drm, "[FB-FIX] PLANE_SURF read: 0x%08x, Forcing base to 0\n", surf);
 	plane_config->base = 0;
 
 	offset = intel_de_read(display, PLANE_OFFSET(pipe, plane_id));
 	drm_WARN_ON(display->drm, offset != 0);
 
+	val = intel_de_read(display, PLANE_SIZE(pipe, plane_id));
+	fb->height = REG_FIELD_GET(PLANE_HEIGHT_MASK, val) + 1;
+	fb->width = REG_FIELD_GET(PLANE_WIDTH_MASK, val) + 1;
+
+	val = intel_de_read(display, PLANE_STRIDE(pipe, plane_id));
+	stride_mult = skl_plane_stride_mult(fb, 0, DRM_MODE_ROTATE_0);
+
+	fb->pitches[0] = REG_FIELD_GET(PLANE_STRIDE__MASK, val) * stride_mult;
+
+	aligned_height = intel_fb_align_height(fb, 0, fb->height);
+
 	plane_config->size = fb->pitches[0] * aligned_height;
-	plane_config->fb = intel_fb;
 
+	drm_dbg_kms(display->drm,
+		    "[CRTC:%d:%s][PLANE:%d:%s] with fb: size=%dx%d@%d, offset=%x, pitch %d, size 0x%x\n",
+		    crtc->base.base.id, crtc->base.name,
+		    plane->base.base.id, plane->base.name,
+		    fb->width, fb->height, fb->format->cpp[0] * 8,
+		    plane_config->base, fb->pitches[0], plane_config->size);
+
+	plane_config->fb = intel_fb;
 	return;
 
 error:
 	kfree(intel_fb);
 	plane_config->fb = NULL;
 }
-
 bool skl_fixup_initial_plane_config(struct intel_crtc *crtc,
 				    const struct intel_initial_plane_config *plane_config)
 {
diff --git a/drivers/gpu/drm/xe/display/xe_plane_initial.c b/drivers/gpu/drm/xe/display/xe_plane_initial.c
index 6a987224e90dc5620194a277ec5f52820da5efce..c7b1371d823bbfa71785df61e7aa601bf76d725e 100644
--- a/drivers/gpu/drm/xe/display/xe_plane_initial.c
+++ b/drivers/gpu/drm/xe/display/xe_plane_initial.c
@@ -192,7 +192,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
 	}
 	/* Reference handed over to fb */
 	xe_bo_put(bo);
-	if (bo->vmap.vaddr && plane_config->fb) {
+	/*if (bo->vmap.vaddr && plane_config->fb) {
 		int x, y;
 		void *vaddr = bo->vmap.vaddr;
 		u32 height = plane_config->fb->base.height;
@@ -209,7 +209,7 @@ intel_alloc_initial_plane_obj(struct intel_crtc *crtc,
 				row[x] = color;
 			}
 		}
-	}
+	}*/
 
 	return true;
 

-- 
2.52.0.457.g6b5491de43-goog


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL
  2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
                   ` (9 preceding siblings ...)
  2026-01-15 16:54 ` [PATCH RFC 10/10] drm/i915/display: Refactor initial plane readout and harden boot handover Juasheem Sultan
@ 2026-01-16 11:39 ` Jani Nikula
  10 siblings, 0 replies; 12+ messages in thread
From: Jani Nikula @ 2026-01-16 11:39 UTC (permalink / raw)
  To: Juasheem Sultan, intel-xe, intel-gfx
  Cc: Rodrigo Vivi, Sean Paul, Manasi Navare, Drew Davenport,
	Juasheem Sultan

On Thu, 15 Jan 2026, Juasheem Sultan <jdsultan@google.com> wrote:
> This RFC patch series proposes an implementation of seamless boot (fastboot)
> support for Panther Lake (PTL) platforms within the Xe driver, mirroring
> functionality already present in i915. The primary focus is on devices using
> MSO (Multi-Segment Operation) panels and EDP panels.
>
> The goal of this series is to achieve a flicker-free transition from the
> bootloader (BIOS/UEFI) to the kernel driver by strictly adhering to the
> hardware state established by the firmware.

The goal is something we obviously share.

> Key areas addressed in this revision:
> 1.  **Boot State Preservation**: Where necessary, make amendments to initial
> 	hardware readouts and ensure that the framebuffer state as established
> 	by firmware is correctly passed into the driver as a memory object.
> 2.  **Atomic State Handoff**: Adjusting the atomic commit path to prevent
>     unnecessary pipe disables or power well toggles when inheriting a valid
>     boot state.
> 3.  **Passive Probing**: Introducing a "passive" initialization path that
>     builds the DRM software state without triggering hardware resets.
>
> This is an initial implementation subject to discussion. 

- There's just way, *way* too much code duplication. I can't even make
  myself read through the entire thing because it's all familiar
  existing stuff, just subtly different. I can't stress this
  enough. There's so much duplication that I won't take on telling you
  what needs to be done. It's on you to figure out how to deduplicate.

- There's way too much magic values and hacking for a specific
  configuration.

- There's way too much code being added in xe_display.c when the goal is
  to unify and deduplicate everything under i915/display. All of this
  needs to happen in the i915/display directory, shared between i915 and
  xe drivers.

- There's way too many random unrelated unexplained things going on
  here. Things like unused struct members being added, commented out
  code being added, stuff being first added and then removed later in
  the series.

- There are way too many things being done in individual patches. The
  "key changes include" lists in commit messages are a pretty big
  clue. Each patch should do one thing, and one thing only. At times it
  feels like the changes in single patches aren't even related to each
  other.

All in all, I get the distinct feeling a lot of this is LLM generated,
which is something you'd do well to disclose if it's indeed the
case. Please see [1] for current discussion on the matter.

Again, the goal is something we share, but I honestly fear taking this
series as the starting point for that will be counter-productive.

I would love to see small, focused patch series tackling existing issues
one by one, instead of a giant hack attempting to fix everything at
once.


BR,
Jani.


[1] https://lore.kernel.org/r/20260113000612.1133427-1-dave.hansen@linux.intel.com


>
> Change-Id: I5cd3bdd4f6f687f007e91f6d0afbfeecfc06762b
> Signed-off-by: Juasheem Sultan <jdsultan@google.com>
> ---
> Juasheem Sultan (10):
>       drm/i915/display: Implement passive initialization for splash screen preservation
>       drm/xe/display: Implement seamless boot state reconstruction for PTL
>       drm/i915/display: Implement aggressive boot state preservation for PTL
>       drm/xe/display: Fix initial plane reconstruction and stolen memory handling
>       drm/i915/display: Enable early PLL readout and robustify modeset setup
>       drm/i915/display: Fix vblank timestamps and update logic for fastboot
>       drm/i915/display: Implement seamless fastboot for MSO panels on PTL
>       drm/i915/display: Robustify fastboot and power init for seamless boot
>       drm/i915/display: Fix initial plane config readout and MSO stride for PTL
>       drm/i915/display: Refactor initial plane readout and harden boot handover
>
>  drivers/gpu/drm/i915/display/intel_bios.c          |  40 +-
>  drivers/gpu/drm/i915/display/intel_cdclk.c         |  36 -
>  drivers/gpu/drm/i915/display/intel_cdclk.h         |  41 +-
>  drivers/gpu/drm/i915/display/intel_connector.c     |   9 +
>  drivers/gpu/drm/i915/display/intel_connector.h     |   1 +
>  drivers/gpu/drm/i915/display/intel_crtc.c          |  62 +-
>  drivers/gpu/drm/i915/display/intel_ddi.c           | 205 +++++-
>  drivers/gpu/drm/i915/display/intel_ddi.h           |   2 +
>  drivers/gpu/drm/i915/display/intel_display.c       | 814 +++++++++++++++++++--
>  drivers/gpu/drm/i915/display/intel_display.h       |   6 +
>  drivers/gpu/drm/i915/display/intel_display_core.h  |   2 +
>  .../gpu/drm/i915/display/intel_display_driver.c    | 329 ++++++++-
>  drivers/gpu/drm/i915/display/intel_display_power.c |  16 +-
>  drivers/gpu/drm/i915/display/intel_display_types.h |   8 +
>  drivers/gpu/drm/i915/display/intel_dp.c            | 372 ++++++++--
>  drivers/gpu/drm/i915/display/intel_dp.h            |   5 +
>  drivers/gpu/drm/i915/display/intel_dpll_mgr.c      |  16 +-
>  drivers/gpu/drm/i915/display/intel_dpll_mgr.h      |   1 +
>  drivers/gpu/drm/i915/display/intel_hdmi.c          |  39 +
>  drivers/gpu/drm/i915/display/intel_hdmi.h          |   2 +
>  drivers/gpu/drm/i915/display/intel_modeset_setup.c |   7 +
>  .../gpu/drm/i915/display/intel_modeset_verify.c    |  14 +-
>  drivers/gpu/drm/i915/display/intel_panel.c         |  41 +-
>  drivers/gpu/drm/i915/display/intel_panel.h         |   3 +
>  drivers/gpu/drm/i915/display/intel_plane_initial.c |  53 ++
>  drivers/gpu/drm/i915/display/skl_universal_plane.c |  32 +-
>  drivers/gpu/drm/xe/display/xe_display.c            | 309 +++++++-
>  drivers/gpu/drm/xe/display/xe_display.h            |   2 +
>  drivers/gpu/drm/xe/display/xe_plane_initial.c      |  28 +-
>  drivers/gpu/drm/xe/xe_device.c                     |   2 +-
>  drivers/gpu/drm/xe/xe_ggtt.h                       |   3 +
>  drivers/gpu/drm/xe/xe_ttm_stolen_mgr.c             |   2 +
>  32 files changed, 2302 insertions(+), 200 deletions(-)
> ---
> base-commit: 812e4b8966d421afd4df8f794bf15f1a1a3ec7b6
> change-id: 20260115-upstream-prep-b29156970a75
>
> Best regards,

-- 
Jani Nikula, Intel

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-01-16 14:03 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-01-15 16:54 [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 01/10] drm/i915/display: Implement passive initialization for splash screen preservation Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 02/10] drm/xe/display: Implement seamless boot state reconstruction for PTL Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 03/10] drm/i915/display: Implement aggressive boot state preservation " Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 04/10] drm/xe/display: Fix initial plane reconstruction and stolen memory handling Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 05/10] drm/i915/display: Enable early PLL readout and robustify modeset setup Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 06/10] drm/i915/display: Fix vblank timestamps and update logic for fastboot Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 07/10] drm/i915/display: Implement seamless fastboot for MSO panels on PTL Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 08/10] drm/i915/display: Robustify fastboot and power init for seamless boot Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 09/10] drm/i915/display: Fix initial plane config readout and MSO stride for PTL Juasheem Sultan
2026-01-15 16:54 ` [PATCH RFC 10/10] drm/i915/display: Refactor initial plane readout and harden boot handover Juasheem Sultan
2026-01-16 11:39 ` [RFC PATCH 00/10] Enable seamless boot (fastboot) for PTL Jani Nikula

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox