xref: /linux/drivers/gpu/drm/i915/display/intel_link_bw.c (revision 2c1ed907520c50326b8f604907a8478b27881a2e)
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