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