Merge tag 'topic/pipe-reorder-2026-04-15' of https://gitlab.freedesktop.org/drm/i915/kernel into drm-next

drm/i915/display: change pipe allocation order for discrete platforms

This is a topic pull request for changing the pipe allocation order for
discrete platforms from the usual A,B,C,D to A,C,B,D. The goal is to
help pipe joiner configurations that reserve the adjacent pipe as the
secondary pipe without the user space knowing. More details in the
relevant commit message. The CRTC iteration is also changed to remain in
pipe order.

Signed-off-by: Dave Airlie <airlied@redhat.com>
From: Jani Nikula <jani.nikula@intel.com>
Link: https://patch.msgid.link/d69501d53c233386d70ed10290af24aafebf434f@intel.com
This commit is contained in:
Dave Airlie 2026-04-17 07:32:30 +10:00
commit 993eb19140
7 changed files with 95 additions and 70 deletions

View File

@ -209,6 +209,8 @@ static struct intel_crtc *intel_crtc_alloc(void)
crtc->base.state = &crtc_state->uapi;
crtc->config = crtc_state;
INIT_LIST_HEAD(&crtc->pipe_head);
return crtc;
}
@ -222,6 +224,8 @@ static void intel_crtc_destroy(struct drm_crtc *_crtc)
{
struct intel_crtc *crtc = to_intel_crtc(_crtc);
list_del(&crtc->pipe_head);
cpu_latency_qos_remove_request(&crtc->vblank_pm_qos);
drm_crtc_cleanup(&crtc->base);
@ -308,6 +312,20 @@ static const struct drm_crtc_funcs i8xx_crtc_funcs = {
.get_vblank_timestamp = intel_crtc_get_vblank_timestamp,
};
static void add_crtc_to_pipe_list(struct intel_display *display, struct intel_crtc *crtc)
{
struct intel_crtc *iter;
list_for_each_entry(iter, &display->pipe_list, pipe_head) {
if (crtc->pipe < iter->pipe) {
list_add_tail(&crtc->pipe_head, &iter->pipe_head);
return;
}
}
list_add_tail(&crtc->pipe_head, &display->pipe_list);
}
static int __intel_crtc_init(struct intel_display *display, enum pipe pipe)
{
struct intel_plane *primary, *cursor;
@ -393,11 +411,11 @@ static 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) && crtc->num_scalers >= 2)
drm_crtc_create_sharpness_strength_property(&crtc->base);
add_crtc_to_pipe_list(display, crtc);
return 0;
fail:
@ -406,6 +424,31 @@ static int __intel_crtc_init(struct intel_display *display, enum pipe pipe)
return ret;
}
#define HAS_PIPE(display, pipe) (DISPLAY_RUNTIME_INFO(display)->pipe_mask & BIT(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.
*
* Swap pipes B and C only if both are available i.e. not fused off.
*/
static enum pipe reorder_pipe(struct intel_display *display, enum pipe pipe)
{
if (!display->platform.dgfx || !HAS_PIPE(display, PIPE_B) || !HAS_PIPE(display, PIPE_C))
return pipe;
switch (pipe) {
case PIPE_B:
return PIPE_C;
case PIPE_C:
return PIPE_B;
default:
return pipe;
}
}
int intel_crtc_init(struct intel_display *display)
{
enum pipe pipe;
@ -415,7 +458,7 @@ int intel_crtc_init(struct intel_display *display)
INTEL_NUM_PIPES(display), str_plural(INTEL_NUM_PIPES(display)));
for_each_pipe(display, pipe) {
ret = __intel_crtc_init(display, pipe);
ret = __intel_crtc_init(display, reorder_pipe(display, pipe));
if (ret)
return ret;
}

View File

@ -5939,17 +5939,6 @@ static int intel_atomic_check_joiner(struct intel_atomic_state *state,
return -EINVAL;
}
/*
* The state copy logic assumes the primary crtc gets processed
* before the secondary crtc during the main compute_config loop.
* This works because the crtcs are created in pipe order,
* and the hardware requires primary pipe < secondary pipe as well.
* Should that change we need to rethink the logic.
*/
if (WARN_ON(drm_crtc_index(&primary_crtc->base) >
drm_crtc_index(&secondary_crtc->base)))
return -EINVAL;
drm_dbg_kms(display->drm,
"[CRTC:%d:%s] Used as secondary for joiner primary [CRTC:%d:%s]\n",
secondary_crtc->base.base.id, secondary_crtc->base.name,
@ -6327,9 +6316,7 @@ static int intel_atomic_check_config(struct intel_atomic_state *state,
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) {
if (!intel_crtc_needs_modeset(new_crtc_state)) {
if (intel_crtc_is_joiner_secondary(new_crtc_state))
copy_joiner_crtc_state_nomodeset(state, crtc);
else
if (!intel_crtc_is_joiner_secondary(new_crtc_state))
intel_crtc_copy_uapi_to_hw_state_nomodeset(state, crtc);
continue;
}
@ -6460,8 +6447,11 @@ int intel_atomic_check(struct drm_device *dev,
goto fail;
for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) {
if (!intel_crtc_needs_modeset(new_crtc_state))
if (!intel_crtc_needs_modeset(new_crtc_state)) {
if (intel_crtc_is_joiner_secondary(new_crtc_state))
copy_joiner_crtc_state_nomodeset(state, crtc);
continue;
}
if (intel_crtc_is_joiner_secondary(new_crtc_state)) {
drm_WARN_ON(display->drm, new_crtc_state->uapi.enable);

View File

@ -212,22 +212,23 @@ enum phy_fia {
base.head) \
for_each_if((intel_plane)->pipe == (intel_crtc)->pipe)
#define for_each_intel_crtc(dev, intel_crtc) \
list_for_each_entry(intel_crtc, \
&(dev)->mode_config.crtc_list, \
base.head)
#define for_each_intel_crtc(dev, crtc) \
list_for_each_entry((crtc), \
&to_intel_display(dev)->pipe_list, \
pipe_head)
#define for_each_intel_crtc_in_pipe_mask(dev, intel_crtc, pipe_mask) \
list_for_each_entry(intel_crtc, \
&(dev)->mode_config.crtc_list, \
base.head) \
for_each_if((pipe_mask) & BIT(intel_crtc->pipe))
#define for_each_intel_crtc_reverse(dev, crtc) \
list_for_each_entry_reverse((crtc), \
&to_intel_display(dev)->pipe_list, \
pipe_head)
#define for_each_intel_crtc_in_pipe_mask_reverse(dev, intel_crtc, pipe_mask) \
list_for_each_entry_reverse((intel_crtc), \
&(dev)->mode_config.crtc_list, \
base.head) \
for_each_if((pipe_mask) & BIT((intel_crtc)->pipe))
#define for_each_intel_crtc_in_pipe_mask(dev, crtc, pipe_mask) \
for_each_intel_crtc((dev), (crtc)) \
for_each_if((pipe_mask) & BIT((crtc)->pipe))
#define for_each_intel_crtc_in_pipe_mask_reverse(dev, crtc, pipe_mask) \
for_each_intel_crtc_reverse((dev), (crtc)) \
for_each_if((pipe_mask) & BIT((crtc)->pipe))
#define for_each_intel_encoder(dev, intel_encoder) \
list_for_each_entry(intel_encoder, \
@ -269,14 +270,6 @@ enum phy_fia {
(__i)++) \
for_each_if(plane)
#define for_each_old_intel_crtc_in_state(__state, crtc, old_crtc_state, __i) \
for ((__i) = 0; \
(__i) < (__state)->base.dev->mode_config.num_crtc && \
((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \
(old_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].old_state), 1); \
(__i)++) \
for_each_if(crtc)
#define for_each_new_intel_plane_in_state(__state, plane, new_plane_state, __i) \
for ((__i) = 0; \
(__i) < (__state)->base.dev->mode_config.num_total_plane && \
@ -285,22 +278,6 @@ enum phy_fia {
(__i)++) \
for_each_if(plane)
#define for_each_new_intel_crtc_in_state(__state, crtc, new_crtc_state, __i) \
for ((__i) = 0; \
(__i) < (__state)->base.dev->mode_config.num_crtc && \
((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \
(new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \
(__i)++) \
for_each_if(crtc)
#define for_each_new_intel_crtc_in_state_reverse(__state, crtc, new_crtc_state, __i) \
for ((__i) = (__state)->base.dev->mode_config.num_crtc - 1; \
(__i) >= 0 && \
((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \
(new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \
(__i)--) \
for_each_if(crtc)
#define for_each_oldnew_intel_plane_in_state(__state, plane, old_plane_state, new_plane_state, __i) \
for ((__i) = 0; \
(__i) < (__state)->base.dev->mode_config.num_total_plane && \
@ -310,23 +287,32 @@ enum phy_fia {
(__i)++) \
for_each_if(plane)
#define for_each_old_intel_crtc_in_state(__state, crtc, old_crtc_state, __i) \
for_each_intel_crtc((__state)->base.dev, (crtc)) \
for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \
(old_crtc_state) = intel_atomic_get_old_crtc_state((__state), (crtc))))
#define for_each_new_intel_crtc_in_state(__state, crtc, new_crtc_state, __i) \
for_each_intel_crtc((__state)->base.dev, (crtc)) \
for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \
(new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc))))
#define for_each_new_intel_crtc_in_state_reverse(__state, crtc, new_crtc_state, __i) \
for_each_intel_crtc_reverse((__state)->base.dev, (crtc)) \
for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \
(new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc))))
#define for_each_oldnew_intel_crtc_in_state(__state, crtc, old_crtc_state, new_crtc_state, __i) \
for ((__i) = 0; \
(__i) < (__state)->base.dev->mode_config.num_crtc && \
((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \
(old_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].old_state), \
(new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \
(__i)++) \
for_each_if(crtc)
for_each_intel_crtc((__state)->base.dev, (crtc)) \
for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \
(old_crtc_state) = intel_atomic_get_old_crtc_state((__state), (crtc)), \
(new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc))))
#define for_each_oldnew_intel_crtc_in_state_reverse(__state, crtc, old_crtc_state, new_crtc_state, __i) \
for ((__i) = (__state)->base.dev->mode_config.num_crtc - 1; \
(__i) >= 0 && \
((crtc) = to_intel_crtc((__state)->base.crtcs[__i].ptr), \
(old_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].old_state), \
(new_crtc_state) = to_intel_crtc_state((__state)->base.crtcs[__i].new_state), 1); \
(__i)--) \
for_each_if(crtc)
for_each_intel_crtc_reverse((__state)->base.dev, (crtc)) \
for_each_if(((__i) = drm_crtc_index(&(crtc)->base), (void)(__i), \
(old_crtc_state) = intel_atomic_get_old_crtc_state((__state), (crtc)), \
(new_crtc_state) = intel_atomic_get_new_crtc_state((__state), (crtc))))
#define intel_atomic_crtc_state_for_each_plane_state( \
plane, plane_state, \

View File

@ -294,6 +294,9 @@ struct intel_display {
/* Parent, or core, driver functions exposed to display */
const struct intel_display_parent_interface *parent;
/* list of all intel_crtcs sorted by pipe */
struct list_head pipe_list;
/* Display functions */
struct {
/* Top level crtc-ish functions */

View File

@ -117,6 +117,7 @@ static void intel_mode_config_init(struct intel_display *display)
drm_mode_config_init(display->drm);
INIT_LIST_HEAD(&display->global.obj_list);
INIT_LIST_HEAD(&display->pipe_list);
mode_config->min_width = 0;
mode_config->min_height = 0;

View File

@ -1484,6 +1484,7 @@ struct intel_flipq {
struct intel_crtc {
struct drm_crtc base;
struct list_head pipe_head;
enum pipe pipe;
/*
* Whether the crtc and the connected output pipeline is active. Implies

View File

@ -21,6 +21,7 @@
#include "intel_audio.h"
#include "intel_bw.h"
#include "intel_display.h"
#include "intel_display_core.h"
#include "intel_display_device.h"
#include "intel_display_driver.h"
#include "intel_display_irq.h"