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