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