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