xref: /linux/drivers/gpu/drm/i915/display/intel_link_bw.c (revision 7255fcc80d4b525cc10cfaaf7f485830d4ed2000)
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  */
23 void 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(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 							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  */
65 int 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(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->base,
83 							 crtc);
84 		if (IS_ERR(crtc_state))
85 			return PTR_ERR(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(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  */
131 bool
132 intel_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(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 
157 static 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 
178 static bool
179 assert_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  */
230 int 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(state->base.dev);
234 	struct intel_link_bw_limits old_limits = *new_limits;
235 	int ret;
236 
237 	ret = check_all_link_config(state, new_limits);
238 	if (ret != -EAGAIN)
239 		return ret;
240 
241 	if (!assert_link_limit_change_valid(i915, &old_limits, new_limits))
242 		return -EINVAL;
243 
244 	return -EAGAIN;
245 }
246