Skip to content

Commit 8ca0b87

Browse files
committed
drm/i915: Add helpers for BW management on shared display links
At the moment a modeset fails if the config computation of a pipe can't fit its required BW to the available link BW even though the limitation may be resolved by reducing the BW requirement of other pipes. To improve the above this patch adds helper functions checking the overall BW limits after all CRTC states have been computed. If the check fails the maximum link bpp for a selected pipe will be reduced and all the CRTC states will be recomputed until either the overall BW limit check passes, or further bpp reduction is not possible (because all pipes/encoders sharing the link BW reached their minimum link bpp). Atm, the MST encoder allocates twice the required BW for YUV420 format streams. A follow-up patchset will fix that, add a code comment about this. This change prepares for upcoming patches enabling the above BW management on FDI and MST links. v2: - Rename intel_crtc_state::max_link_bpp to max_link_bpp_x16 and intel_link_bw_limits::max_bpp to max_bpp_x16. (Jani) v3: - Add the helper functions in a separate patch. (Ville) - Add the functions to intel_link_bw.c instead of intel_atomic.c (Ville) - Return -ENOSPC instead of -EINVAL to userspace in case of a link BW limit failure. v4: - Make intel_atomic_check_config() static. v5: (Ville) - Rename intel_link_bw_limits::min_bpp_pipes to min_bpp_reached_pipes and intel_link_bw_reset_pipe_limit_to_min() to intel_link_bw_set_min_bpp_for_pipe(). - Rename pipe_bpp to link_bpp in intel_link_bw_reduce_bpp(). - Add FIXME: comment about MST encoder's YUV420 BW allocation and tracking the link bpp limit accordingly. v6: - Move intel_link_bw_compute_pipe_bpp() to intel_fdi.c (Ville) - WARN_ON(BIT(pipe) & min_bpp_reached_pipes) in intel_link_bw_set_bpp_limit_for_pipe(). (Ville) - Rename intel_link_bw_set_min_bpp_for_pipe() to intel_link_bw_set_bpp_limit_for_pipe() and intel_link_bw_limits::min_bpp_reached_pipes to bpp_limit_reached_pipes. (Ville) - Remove unused header includes. Cc: Jani Nikula <[email protected]> Cc: Ville Syrjälä <[email protected]> Reviewed-by: Ville Syrjälä <[email protected]> Signed-off-by: Imre Deak <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/[email protected]
1 parent 1050e4c commit 8ca0b87

File tree

7 files changed

+311
-5
lines changed

7 files changed

+311
-5
lines changed

drivers/gpu/drm/i915/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,7 @@ i915-y += \
268268
display/intel_hotplug.o \
269269
display/intel_hotplug_irq.o \
270270
display/intel_hti.o \
271+
display/intel_link_bw.o \
271272
display/intel_load_detect.o \
272273
display/intel_lpe_audio.o \
273274
display/intel_modeset_lock.o \

drivers/gpu/drm/i915/display/intel_crtc.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ void intel_crtc_state_reset(struct intel_crtc_state *crtc_state,
176176
crtc_state->hsw_workaround_pipe = INVALID_PIPE;
177177
crtc_state->scaler_state.scaler_id = -1;
178178
crtc_state->mst_master_transcoder = INVALID_TRANSCODER;
179+
crtc_state->max_link_bpp_x16 = INT_MAX;
179180
}
180181

181182
static struct intel_crtc *intel_crtc_alloc(void)

drivers/gpu/drm/i915/display/intel_display.c

Lines changed: 61 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
#include "intel_frontbuffer.h"
8989
#include "intel_hdmi.h"
9090
#include "intel_hotplug.h"
91+
#include "intel_link_bw.h"
9192
#include "intel_lvds.h"
9293
#include "intel_lvds_regs.h"
9394
#include "intel_modeset_setup.h"
@@ -4644,7 +4645,8 @@ intel_crtc_prepare_cleared_state(struct intel_atomic_state *state,
46444645

46454646
static int
46464647
intel_modeset_pipe_config(struct intel_atomic_state *state,
4647-
struct intel_crtc *crtc)
4648+
struct intel_crtc *crtc,
4649+
const struct intel_link_bw_limits *limits)
46484650
{
46494651
struct drm_i915_private *i915 = to_i915(crtc->base.dev);
46504652
struct intel_crtc_state *crtc_state =
@@ -4676,6 +4678,15 @@ intel_modeset_pipe_config(struct intel_atomic_state *state,
46764678
if (ret)
46774679
return ret;
46784680

4681+
crtc_state->max_link_bpp_x16 = limits->max_bpp_x16[crtc->pipe];
4682+
4683+
if (crtc_state->pipe_bpp > to_bpp_int(crtc_state->max_link_bpp_x16)) {
4684+
drm_dbg_kms(&i915->drm,
4685+
"[CRTC:%d:%s] Link bpp limited to " BPP_X16_FMT "\n",
4686+
crtc->base.base.id, crtc->base.name,
4687+
BPP_X16_ARGS(crtc_state->max_link_bpp_x16));
4688+
}
4689+
46794690
base_bpp = crtc_state->pipe_bpp;
46804691

46814692
/*
@@ -6283,14 +6294,18 @@ static int intel_bigjoiner_add_affected_crtcs(struct intel_atomic_state *state)
62836294
return 0;
62846295
}
62856296

6286-
static int intel_atomic_check_config(struct intel_atomic_state *state)
6297+
static int intel_atomic_check_config(struct intel_atomic_state *state,
6298+
struct intel_link_bw_limits *limits,
6299+
enum pipe *failed_pipe)
62876300
{
62886301
struct drm_i915_private *i915 = to_i915(state->base.dev);
62896302
struct intel_crtc_state *new_crtc_state;
62906303
struct intel_crtc *crtc;
62916304
int ret;
62926305
int i;
62936306

6307+
*failed_pipe = INVALID_PIPE;
6308+
62946309
ret = intel_bigjoiner_add_affected_crtcs(state);
62956310
if (ret)
62966311
return ret;
@@ -6316,7 +6331,7 @@ static int intel_atomic_check_config(struct intel_atomic_state *state)
63166331
if (!new_crtc_state->hw.enable)
63176332
continue;
63186333

6319-
ret = intel_modeset_pipe_config(state, crtc);
6334+
ret = intel_modeset_pipe_config(state, crtc, limits);
63206335
if (ret)
63216336
break;
63226337

@@ -6325,9 +6340,51 @@ static int intel_atomic_check_config(struct intel_atomic_state *state)
63256340
break;
63266341
}
63276342

6343+
if (ret)
6344+
*failed_pipe = crtc->pipe;
6345+
63286346
return ret;
63296347
}
63306348

6349+
static int intel_atomic_check_config_and_link(struct intel_atomic_state *state)
6350+
{
6351+
struct drm_i915_private *i915 = to_i915(state->base.dev);
6352+
struct intel_link_bw_limits new_limits;
6353+
struct intel_link_bw_limits old_limits;
6354+
int ret;
6355+
6356+
intel_link_bw_init_limits(i915, &new_limits);
6357+
old_limits = new_limits;
6358+
6359+
while (true) {
6360+
enum pipe failed_pipe;
6361+
6362+
ret = intel_atomic_check_config(state, &new_limits,
6363+
&failed_pipe);
6364+
if (ret) {
6365+
/*
6366+
* The bpp limit for a pipe is below the minimum it supports, set the
6367+
* limit to the minimum and recalculate the config.
6368+
*/
6369+
if (ret == -EINVAL &&
6370+
intel_link_bw_set_bpp_limit_for_pipe(state,
6371+
&old_limits,
6372+
&new_limits,
6373+
failed_pipe))
6374+
continue;
6375+
6376+
break;
6377+
}
6378+
6379+
old_limits = new_limits;
6380+
6381+
ret = intel_link_bw_atomic_check(state, &new_limits);
6382+
if (ret != -EAGAIN)
6383+
break;
6384+
}
6385+
6386+
return ret;
6387+
}
63316388
/**
63326389
* intel_atomic_check - validate state object
63336390
* @dev: drm device
@@ -6372,7 +6429,7 @@ int intel_atomic_check(struct drm_device *dev,
63726429
return ret;
63736430
}
63746431

6375-
ret = intel_atomic_check_config(state);
6432+
ret = intel_atomic_check_config_and_link(state);
63766433
if (ret)
63776434
goto fail;
63786435

drivers/gpu/drm/i915/display/intel_display_types.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1191,7 +1191,8 @@ struct intel_crtc_state {
11911191
u32 ctrl, div;
11921192
} dsi_pll;
11931193

1194-
int pipe_bpp;
1194+
int max_link_bpp_x16; /* in 1/16 bpp units */
1195+
int pipe_bpp; /* in 1 bpp units */
11951196
struct intel_link_m_n dp_m_n;
11961197

11971198
/* m2_n2 for eDP downclock */

drivers/gpu/drm/i915/display/intel_dp_mst.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ static int intel_dp_mst_compute_link_config(struct intel_encoder *encoder,
157157
int slots = -EINVAL;
158158
int link_bpp;
159159

160+
/*
161+
* FIXME: allocate the BW according to link_bpp, which in the case of
162+
* YUV420 is only half of the pipe bpp value.
163+
*/
160164
slots = intel_dp_mst_find_vcpi_slots_for_bpp(encoder, crtc_state,
161165
to_bpp_int(limits->link.max_bpp_x16),
162166
to_bpp_int(limits->link.min_bpp_x16),
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
// SPDX-License-Identifier: MIT
2+
/*
3+
* Copyright © 2023 Intel Corporation
4+
*/
5+
6+
#include "i915_drv.h"
7+
8+
#include "intel_atomic.h"
9+
#include "intel_display_types.h"
10+
#include "intel_link_bw.h"
11+
12+
/**
13+
* intel_link_bw_init_limits - initialize BW limits
14+
* @i915: device instance
15+
* @limits: link BW limits
16+
*
17+
* Initialize @limits.
18+
*/
19+
void intel_link_bw_init_limits(struct drm_i915_private *i915, struct intel_link_bw_limits *limits)
20+
{
21+
enum pipe pipe;
22+
23+
limits->bpp_limit_reached_pipes = 0;
24+
for_each_pipe(i915, pipe)
25+
limits->max_bpp_x16[pipe] = INT_MAX;
26+
}
27+
28+
/**
29+
* intel_link_bw_reduce_bpp - reduce maximum link bpp for a selected pipe
30+
* @state: atomic state
31+
* @limits: link BW limits
32+
* @pipe_mask: mask of pipes to select from
33+
* @reason: explanation of why bpp reduction is needed
34+
*
35+
* Select the pipe from @pipe_mask with the biggest link bpp value and set the
36+
* maximum of link bpp in @limits below this value. Modeset the selected pipe,
37+
* so that its state will get recomputed.
38+
*
39+
* This function can be called to resolve a link's BW overallocation by reducing
40+
* the link bpp of one pipe on the link and hence reducing the total link BW.
41+
*
42+
* Returns
43+
* - 0 in case of success
44+
* - %-ENOSPC if no pipe can further reduce its link bpp
45+
* - Other negative error, if modesetting the selected pipe failed
46+
*/
47+
int intel_link_bw_reduce_bpp(struct intel_atomic_state *state,
48+
struct intel_link_bw_limits *limits,
49+
u8 pipe_mask,
50+
const char *reason)
51+
{
52+
struct drm_i915_private *i915 = to_i915(state->base.dev);
53+
enum pipe max_bpp_pipe = INVALID_PIPE;
54+
struct intel_crtc *crtc;
55+
int max_bpp = 0;
56+
57+
for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, pipe_mask) {
58+
struct intel_crtc_state *crtc_state;
59+
int link_bpp;
60+
61+
if (limits->bpp_limit_reached_pipes & BIT(crtc->pipe))
62+
continue;
63+
64+
crtc_state = intel_atomic_get_crtc_state(&state->base,
65+
crtc);
66+
if (IS_ERR(crtc_state))
67+
return PTR_ERR(crtc_state);
68+
69+
if (crtc_state->dsc.compression_enable)
70+
link_bpp = crtc_state->dsc.compressed_bpp;
71+
else
72+
/*
73+
* TODO: for YUV420 the actual link bpp is only half
74+
* of the pipe bpp value. The MST encoder's BW allocation
75+
* is based on the pipe bpp value, set the actual link bpp
76+
* limit here once the MST BW allocation is fixed.
77+
*/
78+
link_bpp = crtc_state->pipe_bpp;
79+
80+
if (link_bpp > max_bpp) {
81+
max_bpp = link_bpp;
82+
max_bpp_pipe = crtc->pipe;
83+
}
84+
}
85+
86+
if (max_bpp_pipe == INVALID_PIPE)
87+
return -ENOSPC;
88+
89+
limits->max_bpp_x16[max_bpp_pipe] = to_bpp_x16(max_bpp) - 1;
90+
91+
return intel_modeset_pipes_in_mask_early(state, reason,
92+
BIT(max_bpp_pipe));
93+
}
94+
95+
/**
96+
* intel_link_bw_set_bpp_limit_for_pipe - set link bpp limit for a pipe to its minimum
97+
* @state: atomic state
98+
* @old_limits: link BW limits
99+
* @new_limits: link BW limits
100+
* @pipe: pipe
101+
*
102+
* Set the link bpp limit for @pipe in @new_limits to its value in
103+
* @old_limits and mark this limit as the minimum. This function must be
104+
* called after a pipe's compute config function failed, @old_limits
105+
* containing the bpp limit with which compute config previously passed.
106+
*
107+
* The function will fail if setting a minimum is not possible, either
108+
* because the old and new limits match (and so would lead to a pipe compute
109+
* config failure) or the limit is already at the minimum.
110+
*
111+
* Returns %true in case of success.
112+
*/
113+
bool
114+
intel_link_bw_set_bpp_limit_for_pipe(struct intel_atomic_state *state,
115+
const struct intel_link_bw_limits *old_limits,
116+
struct intel_link_bw_limits *new_limits,
117+
enum pipe pipe)
118+
{
119+
struct drm_i915_private *i915 = to_i915(state->base.dev);
120+
121+
if (pipe == INVALID_PIPE)
122+
return false;
123+
124+
if (new_limits->max_bpp_x16[pipe] ==
125+
old_limits->max_bpp_x16[pipe])
126+
return false;
127+
128+
if (drm_WARN_ON(&i915->drm,
129+
new_limits->bpp_limit_reached_pipes & BIT(pipe)))
130+
return false;
131+
132+
new_limits->max_bpp_x16[pipe] =
133+
old_limits->max_bpp_x16[pipe];
134+
new_limits->bpp_limit_reached_pipes |= BIT(pipe);
135+
136+
return true;
137+
}
138+
139+
static int check_all_link_config(struct intel_atomic_state *state,
140+
struct intel_link_bw_limits *limits)
141+
{
142+
/* TODO: Check all shared display link configurations like FDI */
143+
return 0;
144+
}
145+
146+
static bool
147+
assert_link_limit_change_valid(struct drm_i915_private *i915,
148+
const struct intel_link_bw_limits *old_limits,
149+
const struct intel_link_bw_limits *new_limits)
150+
{
151+
bool bpps_changed = false;
152+
enum pipe pipe;
153+
154+
for_each_pipe(i915, pipe) {
155+
/* The bpp limit can only decrease. */
156+
if (drm_WARN_ON(&i915->drm,
157+
new_limits->max_bpp_x16[pipe] >
158+
old_limits->max_bpp_x16[pipe]))
159+
return false;
160+
161+
if (new_limits->max_bpp_x16[pipe] <
162+
old_limits->max_bpp_x16[pipe])
163+
bpps_changed = true;
164+
}
165+
166+
/* At least one limit must change. */
167+
if (drm_WARN_ON(&i915->drm,
168+
!bpps_changed))
169+
return false;
170+
171+
return true;
172+
}
173+
174+
/**
175+
* intel_link_bw_atomic_check - check display link states and set a fallback config if needed
176+
* @state: atomic state
177+
* @new_limits: link BW limits
178+
*
179+
* Check the configuration of all shared display links in @state and set new BW
180+
* limits in @new_limits if there is a BW limitation.
181+
*
182+
* Returns:
183+
* - 0 if the confugration is valid
184+
* - %-EAGAIN, if the configuration is invalid and @new_limits got updated
185+
* with fallback values with which the configuration of all CRTCs
186+
* in @state must be recomputed
187+
* - Other negative error, if the configuration is invalid without a
188+
* fallback possibility, or the check failed for another reason
189+
*/
190+
int intel_link_bw_atomic_check(struct intel_atomic_state *state,
191+
struct intel_link_bw_limits *new_limits)
192+
{
193+
struct drm_i915_private *i915 = to_i915(state->base.dev);
194+
struct intel_link_bw_limits old_limits = *new_limits;
195+
int ret;
196+
197+
ret = check_all_link_config(state, new_limits);
198+
if (ret != -EAGAIN)
199+
return ret;
200+
201+
if (!assert_link_limit_change_valid(i915, &old_limits, new_limits))
202+
return -EINVAL;
203+
204+
return -EAGAIN;
205+
}

0 commit comments

Comments
 (0)