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