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 <[email protected]>
---
 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

Reply via email to