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