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 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 */ 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 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 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 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 */ 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