On Wed, Nov 19, 2025 at 05:33:21PM +0200, Jani Nikula wrote:
> When big joiner is enabled, it reserves the adjacent pipe as the
> secondary pipe. This happens without the user space knowing, and
> subsequent attempts at using the CRTC with that pipe will fail. If the
> user space does not have a coping mechanism, i.e. trying another CRTC,
> this leads to a black screen.
> 
> Try to reduce the impact of the problem on discrete platforms by mapping
> the CRTCs to pipes in order A, C, B, and D. If the user space reserves
> CRTCs in order, this should trick it to using pipes that are more likely
> to be available for and after joining.
> 
> Limit this to discrete platforms, which have four pipes, and no eDP, a
> combination that should benefit the most with least drawbacks.
> 
> Although there are currently no platforms with more than four pipes, add
> a fallback for initializing the rest of the pipes to not miss them.
> 
> Signed-off-by: Jani Nikula <[email protected]>
> 
> ---
> 
> v2: Also remove WARN_ON()
> 
> v3: Limit to discrete
> 
> There are a number of issues in IGT with assuming CRTC index == pipe, at
> least with CRC and vblank waits. With them being used a lot in tests, we
> won't get enough test coverage until they're fixed.
> ---
>  drivers/gpu/drm/i915/display/intel_crtc.c     |  2 --
>  .../drm/i915/display/intel_display_driver.c   | 26 ++++++++++++++++++-
>  2 files changed, 25 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/gpu/drm/i915/display/intel_crtc.c 
> b/drivers/gpu/drm/i915/display/intel_crtc.c
> index 9d2a23c96c61..11e58d07ddef 100644
> --- a/drivers/gpu/drm/i915/display/intel_crtc.c
> +++ b/drivers/gpu/drm/i915/display/intel_crtc.c
> @@ -394,8 +394,6 @@ int intel_crtc_init(struct intel_display *display, enum 
> pipe pipe)
>  
>       cpu_latency_qos_add_request(&crtc->vblank_pm_qos, PM_QOS_DEFAULT_VALUE);
>  
> -     drm_WARN_ON(display->drm, drm_crtc_index(&crtc->base) != crtc->pipe);
> -
>       if (HAS_CASF(display))
>               drm_crtc_create_sharpness_strength_property(&crtc->base);
>  
> diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c 
> b/drivers/gpu/drm/i915/display/intel_display_driver.c
> index 7e000ba3e08b..b5c9cdf14a43 100644
> --- a/drivers/gpu/drm/i915/display/intel_display_driver.c
> +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c
> @@ -452,6 +452,7 @@ 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)
>  {
> +     u8 pipe_mask = U8_MAX;
>       enum pipe pipe;
>       int ret;
>  
> @@ -470,7 +471,30 @@ int intel_display_driver_probe_nogem(struct 
> intel_display *display)
>                   INTEL_NUM_PIPES(display),
>                   INTEL_NUM_PIPES(display) > 1 ? "s" : "");
>  
> -     for_each_pipe(display, pipe) {
> +     /*
> +      * Expose the pipes in order A, C, B, D on discrete platforms to trick
> +      * user space into using pipes that are more likely to be available for
> +      * both a) user space if pipe B has been reserved for the joiner, and b)
> +      * the joiner if pipe A doesn't need the joiner.
> +      *
> +      * Fall back to normal initialization for the remaining pipes, if any.
> +      */
> +     if (HAS_BIGJOINER(display) && display->platform.dgfx) {

uncompressed joiner is also a thing.

> +             enum pipe pipe_order[] = { PIPE_A, PIPE_C, PIPE_B, PIPE_D };
> +             int i;
> +
> +             for (i = 0; i < ARRAY_SIZE(pipe_order); i++) {
> +                     pipe = pipe_order[i];
> +
> +                     ret = intel_crtc_init(display, pipe);
> +                     if (ret)
> +                             goto err_mode_config;
> +
> +                     pipe_mask &= ~BIT(pipe);
> +             }
> +     }

I was thinking it might be easier to just do the B<->C swap inside
intel_crtc_init(). Kinda similar how we we already do the plane
A<->B swap in intel_primary_plane_create(). But I guess the
loop here would become a bit more confusing since it would have
to iterate all possible pipes and not just the ones present in
the runtime info pipe_mask.

> +
> +     for_each_pipe_masked(display, pipe, pipe_mask) {
>               ret = intel_crtc_init(display, pipe);
>               if (ret)
>                       goto err_mode_config;
> -- 
> 2.47.3

-- 
Ville Syrjälä
Intel

Reply via email to