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

source code of linux/drivers/gpu/drm/i915/display/intel_link_bw.c