| 1 | // SPDX-License-Identifier: MIT |
| 2 | /* |
| 3 | * Copyright © 2023 Intel Corporation |
| 4 | */ |
| 5 | |
| 6 | #include <linux/ctype.h> |
| 7 | #include <linux/debugfs.h> |
| 8 | #include <linux/int_log.h> |
| 9 | #include <linux/math.h> |
| 10 | |
| 11 | #include <drm/drm_fixed.h> |
| 12 | #include <drm/drm_print.h> |
| 13 | |
| 14 | #include "intel_atomic.h" |
| 15 | #include "intel_crtc.h" |
| 16 | #include "intel_display_core.h" |
| 17 | #include "intel_display_types.h" |
| 18 | #include "intel_dp.h" |
| 19 | #include "intel_dp_mst.h" |
| 20 | #include "intel_dp_tunnel.h" |
| 21 | #include "intel_fdi.h" |
| 22 | #include "intel_link_bw.h" |
| 23 | #include "intel_vdsc.h" |
| 24 | |
| 25 | static int get_forced_link_bpp_x16(struct intel_atomic_state *state, |
| 26 | const struct intel_crtc *crtc) |
| 27 | { |
| 28 | struct intel_digital_connector_state *conn_state; |
| 29 | struct intel_connector *connector; |
| 30 | int force_bpp_x16 = INT_MAX; |
| 31 | int i; |
| 32 | |
| 33 | for_each_new_intel_connector_in_state(state, connector, conn_state, i) { |
| 34 | if (conn_state->base.crtc != &crtc->base) |
| 35 | continue; |
| 36 | |
| 37 | if (!connector->link.force_bpp_x16) |
| 38 | continue; |
| 39 | |
| 40 | force_bpp_x16 = min(force_bpp_x16, connector->link.force_bpp_x16); |
| 41 | } |
| 42 | |
| 43 | return force_bpp_x16 < INT_MAX ? force_bpp_x16 : 0; |
| 44 | } |
| 45 | |
| 46 | /** |
| 47 | * intel_link_bw_init_limits - initialize BW limits |
| 48 | * @state: Atomic state |
| 49 | * @limits: link BW limits |
| 50 | * |
| 51 | * Initialize @limits. |
| 52 | */ |
| 53 | void intel_link_bw_init_limits(struct intel_atomic_state *state, |
| 54 | struct intel_link_bw_limits *limits) |
| 55 | { |
| 56 | struct intel_display *display = to_intel_display(state); |
| 57 | enum pipe pipe; |
| 58 | |
| 59 | limits->link_dsc_pipes = 0; |
| 60 | limits->bpp_limit_reached_pipes = 0; |
| 61 | for_each_pipe(display, pipe) { |
| 62 | struct intel_crtc *crtc = intel_crtc_for_pipe(display, pipe); |
| 63 | const struct intel_crtc_state *crtc_state = |
| 64 | intel_atomic_get_new_crtc_state(state, crtc); |
| 65 | int forced_bpp_x16 = get_forced_link_bpp_x16(state, crtc); |
| 66 | |
| 67 | if (state->base.duplicated && crtc_state) { |
| 68 | limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16; |
| 69 | if (intel_dsc_enabled_on_link(crtc_state)) |
| 70 | limits->link_dsc_pipes |= BIT(pipe); |
| 71 | } else { |
| 72 | limits->max_bpp_x16[pipe] = INT_MAX; |
| 73 | } |
| 74 | |
| 75 | if (forced_bpp_x16) |
| 76 | limits->max_bpp_x16[pipe] = min(limits->max_bpp_x16[pipe], forced_bpp_x16); |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | /** |
| 81 | * __intel_link_bw_reduce_bpp - reduce maximum link bpp for a selected pipe |
| 82 | * @state: atomic state |
| 83 | * @limits: link BW limits |
| 84 | * @pipe_mask: mask of pipes to select from |
| 85 | * @reason: explanation of why bpp reduction is needed |
| 86 | * @reduce_forced_bpp: allow reducing bpps below their forced link bpp |
| 87 | * |
| 88 | * Select the pipe from @pipe_mask with the biggest link bpp value and set the |
| 89 | * maximum of link bpp in @limits below this value. Modeset the selected pipe, |
| 90 | * so that its state will get recomputed. |
| 91 | * |
| 92 | * This function can be called to resolve a link's BW overallocation by reducing |
| 93 | * the link bpp of one pipe on the link and hence reducing the total link BW. |
| 94 | * |
| 95 | * Returns |
| 96 | * - 0 in case of success |
| 97 | * - %-ENOSPC if no pipe can further reduce its link bpp |
| 98 | * - Other negative error, if modesetting the selected pipe failed |
| 99 | */ |
| 100 | static int __intel_link_bw_reduce_bpp(struct intel_atomic_state *state, |
| 101 | struct intel_link_bw_limits *limits, |
| 102 | u8 pipe_mask, |
| 103 | const char *reason, |
| 104 | bool reduce_forced_bpp) |
| 105 | { |
| 106 | struct intel_display *display = to_intel_display(state); |
| 107 | enum pipe max_bpp_pipe = INVALID_PIPE; |
| 108 | struct intel_crtc *crtc; |
| 109 | int max_bpp_x16 = 0; |
| 110 | |
| 111 | for_each_intel_crtc_in_pipe_mask(display->drm, crtc, pipe_mask) { |
| 112 | struct intel_crtc_state *crtc_state; |
| 113 | int link_bpp_x16; |
| 114 | |
| 115 | if (limits->bpp_limit_reached_pipes & BIT(crtc->pipe)) |
| 116 | continue; |
| 117 | |
| 118 | crtc_state = intel_atomic_get_crtc_state(state: &state->base, |
| 119 | crtc); |
| 120 | if (IS_ERR(ptr: crtc_state)) |
| 121 | return PTR_ERR(ptr: crtc_state); |
| 122 | |
| 123 | if (crtc_state->dsc.compression_enable) |
| 124 | link_bpp_x16 = crtc_state->dsc.compressed_bpp_x16; |
| 125 | else |
| 126 | /* |
| 127 | * TODO: for YUV420 the actual link bpp is only half |
| 128 | * of the pipe bpp value. The MST encoder's BW allocation |
| 129 | * is based on the pipe bpp value, set the actual link bpp |
| 130 | * limit here once the MST BW allocation is fixed. |
| 131 | */ |
| 132 | link_bpp_x16 = fxp_q4_from_int(val_int: crtc_state->pipe_bpp); |
| 133 | |
| 134 | if (!reduce_forced_bpp && |
| 135 | link_bpp_x16 <= get_forced_link_bpp_x16(state, crtc)) |
| 136 | continue; |
| 137 | |
| 138 | if (link_bpp_x16 > max_bpp_x16) { |
| 139 | max_bpp_x16 = link_bpp_x16; |
| 140 | max_bpp_pipe = crtc->pipe; |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | if (max_bpp_pipe == INVALID_PIPE) |
| 145 | return -ENOSPC; |
| 146 | |
| 147 | limits->max_bpp_x16[max_bpp_pipe] = max_bpp_x16 - 1; |
| 148 | |
| 149 | return intel_modeset_pipes_in_mask_early(state, reason, |
| 150 | BIT(max_bpp_pipe)); |
| 151 | } |
| 152 | |
| 153 | int intel_link_bw_reduce_bpp(struct intel_atomic_state *state, |
| 154 | struct intel_link_bw_limits *limits, |
| 155 | u8 pipe_mask, |
| 156 | const char *reason) |
| 157 | { |
| 158 | int ret; |
| 159 | |
| 160 | /* Try to keep any forced link BPP. */ |
| 161 | ret = __intel_link_bw_reduce_bpp(state, limits, pipe_mask, reason, reduce_forced_bpp: false); |
| 162 | if (ret == -ENOSPC) |
| 163 | ret = __intel_link_bw_reduce_bpp(state, limits, pipe_mask, reason, reduce_forced_bpp: true); |
| 164 | |
| 165 | return ret; |
| 166 | } |
| 167 | |
| 168 | /** |
| 169 | * intel_link_bw_compute_pipe_bpp - compute pipe bpp limited by max link bpp |
| 170 | * @crtc_state: the crtc state |
| 171 | * |
| 172 | * Compute the pipe bpp limited by the CRTC's maximum link bpp. Encoders can |
| 173 | * call this function during state computation in the simple case where the |
| 174 | * link bpp will always match the pipe bpp. This is the case for all non-DP |
| 175 | * encoders, while DP encoders will use a link bpp lower than pipe bpp in case |
| 176 | * of DSC compression. |
| 177 | * |
| 178 | * Returns %true in case of success, %false if pipe bpp would need to be |
| 179 | * reduced below its valid range. |
| 180 | */ |
| 181 | bool intel_link_bw_compute_pipe_bpp(struct intel_crtc_state *crtc_state) |
| 182 | { |
| 183 | int pipe_bpp = min(crtc_state->pipe_bpp, |
| 184 | fxp_q4_to_int(crtc_state->max_link_bpp_x16)); |
| 185 | |
| 186 | pipe_bpp = rounddown(pipe_bpp, 2 * 3); |
| 187 | |
| 188 | if (pipe_bpp < 6 * 3) |
| 189 | return false; |
| 190 | |
| 191 | crtc_state->pipe_bpp = pipe_bpp; |
| 192 | |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | /** |
| 197 | * intel_link_bw_set_bpp_limit_for_pipe - set link bpp limit for a pipe to its minimum |
| 198 | * @state: atomic state |
| 199 | * @old_limits: link BW limits |
| 200 | * @new_limits: link BW limits |
| 201 | * @pipe: pipe |
| 202 | * |
| 203 | * Set the link bpp limit for @pipe in @new_limits to its value in |
| 204 | * @old_limits and mark this limit as the minimum. This function must be |
| 205 | * called after a pipe's compute config function failed, @old_limits |
| 206 | * containing the bpp limit with which compute config previously passed. |
| 207 | * |
| 208 | * The function will fail if setting a minimum is not possible, either |
| 209 | * because the old and new limits match (and so would lead to a pipe compute |
| 210 | * config failure) or the limit is already at the minimum. |
| 211 | * |
| 212 | * Returns %true in case of success. |
| 213 | */ |
| 214 | bool |
| 215 | intel_link_bw_set_bpp_limit_for_pipe(struct intel_atomic_state *state, |
| 216 | const struct intel_link_bw_limits *old_limits, |
| 217 | struct intel_link_bw_limits *new_limits, |
| 218 | enum pipe pipe) |
| 219 | { |
| 220 | struct intel_display *display = to_intel_display(state); |
| 221 | |
| 222 | if (pipe == INVALID_PIPE) |
| 223 | return false; |
| 224 | |
| 225 | if (new_limits->max_bpp_x16[pipe] == |
| 226 | old_limits->max_bpp_x16[pipe]) |
| 227 | return false; |
| 228 | |
| 229 | if (drm_WARN_ON(display->drm, |
| 230 | new_limits->bpp_limit_reached_pipes & BIT(pipe))) |
| 231 | return false; |
| 232 | |
| 233 | new_limits->max_bpp_x16[pipe] = |
| 234 | old_limits->max_bpp_x16[pipe]; |
| 235 | new_limits->bpp_limit_reached_pipes |= BIT(pipe); |
| 236 | |
| 237 | return true; |
| 238 | } |
| 239 | |
| 240 | static int check_all_link_config(struct intel_atomic_state *state, |
| 241 | struct intel_link_bw_limits *limits) |
| 242 | { |
| 243 | /* TODO: Check additional shared display link configurations like MST */ |
| 244 | int ret; |
| 245 | |
| 246 | ret = intel_dp_mst_atomic_check_link(state, limits); |
| 247 | if (ret) |
| 248 | return ret; |
| 249 | |
| 250 | ret = intel_dp_tunnel_atomic_check_link(state, limits); |
| 251 | if (ret) |
| 252 | return ret; |
| 253 | |
| 254 | ret = intel_fdi_atomic_check_link(state, limits); |
| 255 | if (ret) |
| 256 | return ret; |
| 257 | |
| 258 | return 0; |
| 259 | } |
| 260 | |
| 261 | static bool |
| 262 | assert_link_limit_change_valid(struct intel_display *display, |
| 263 | const struct intel_link_bw_limits *old_limits, |
| 264 | const struct intel_link_bw_limits *new_limits) |
| 265 | { |
| 266 | bool bpps_changed = false; |
| 267 | enum pipe pipe; |
| 268 | |
| 269 | /* DSC can't be disabled after it was enabled. */ |
| 270 | if (drm_WARN_ON(display->drm, |
| 271 | (old_limits->link_dsc_pipes & new_limits->link_dsc_pipes) != |
| 272 | old_limits->link_dsc_pipes)) |
| 273 | return false; |
| 274 | |
| 275 | for_each_pipe(display, pipe) { |
| 276 | /* The bpp limit can only decrease. */ |
| 277 | if (drm_WARN_ON(display->drm, |
| 278 | new_limits->max_bpp_x16[pipe] > |
| 279 | old_limits->max_bpp_x16[pipe])) |
| 280 | return false; |
| 281 | |
| 282 | if (new_limits->max_bpp_x16[pipe] < |
| 283 | old_limits->max_bpp_x16[pipe]) |
| 284 | bpps_changed = true; |
| 285 | } |
| 286 | |
| 287 | /* At least one limit must change. */ |
| 288 | if (drm_WARN_ON(display->drm, |
| 289 | !bpps_changed && |
| 290 | new_limits->link_dsc_pipes == |
| 291 | old_limits->link_dsc_pipes)) |
| 292 | return false; |
| 293 | |
| 294 | return true; |
| 295 | } |
| 296 | |
| 297 | /** |
| 298 | * intel_link_bw_atomic_check - check display link states and set a fallback config if needed |
| 299 | * @state: atomic state |
| 300 | * @new_limits: link BW limits |
| 301 | * |
| 302 | * Check the configuration of all shared display links in @state and set new BW |
| 303 | * limits in @new_limits if there is a BW limitation. |
| 304 | * |
| 305 | * Returns: |
| 306 | * - 0 if the configuration is valid |
| 307 | * - %-EAGAIN, if the configuration is invalid and @new_limits got updated |
| 308 | * with fallback values with which the configuration of all CRTCs |
| 309 | * in @state must be recomputed |
| 310 | * - Other negative error, if the configuration is invalid without a |
| 311 | * fallback possibility, or the check failed for another reason |
| 312 | */ |
| 313 | int intel_link_bw_atomic_check(struct intel_atomic_state *state, |
| 314 | struct intel_link_bw_limits *new_limits) |
| 315 | { |
| 316 | struct intel_display *display = to_intel_display(state); |
| 317 | struct intel_link_bw_limits old_limits = *new_limits; |
| 318 | int ret; |
| 319 | |
| 320 | ret = check_all_link_config(state, limits: new_limits); |
| 321 | if (ret != -EAGAIN) |
| 322 | return ret; |
| 323 | |
| 324 | if (!assert_link_limit_change_valid(display, old_limits: &old_limits, new_limits)) |
| 325 | return -EINVAL; |
| 326 | |
| 327 | return -EAGAIN; |
| 328 | } |
| 329 | |
| 330 | static int force_link_bpp_show(struct seq_file *m, void *data) |
| 331 | { |
| 332 | struct intel_connector *connector = m->private; |
| 333 | |
| 334 | seq_printf(m, FXP_Q4_FMT "\n" , FXP_Q4_ARGS(connector->link.force_bpp_x16)); |
| 335 | |
| 336 | return 0; |
| 337 | } |
| 338 | |
| 339 | static int str_to_fxp_q4_nonneg_int(const char *str, int *val_x16) |
| 340 | { |
| 341 | unsigned int val; |
| 342 | int err; |
| 343 | |
| 344 | err = kstrtouint(s: str, base: 10, res: &val); |
| 345 | if (err) |
| 346 | return err; |
| 347 | |
| 348 | if (val > INT_MAX >> 4) |
| 349 | return -ERANGE; |
| 350 | |
| 351 | *val_x16 = fxp_q4_from_int(val_int: val); |
| 352 | |
| 353 | return 0; |
| 354 | } |
| 355 | |
| 356 | /* modifies str */ |
| 357 | static int str_to_fxp_q4_nonneg(char *str, int *val_x16) |
| 358 | { |
| 359 | const char *int_str; |
| 360 | char *frac_str; |
| 361 | int frac_digits; |
| 362 | int frac_val; |
| 363 | int err; |
| 364 | |
| 365 | int_str = strim(str); |
| 366 | frac_str = strchr(int_str, '.'); |
| 367 | |
| 368 | if (frac_str) |
| 369 | *frac_str++ = '\0'; |
| 370 | |
| 371 | err = str_to_fxp_q4_nonneg_int(str: int_str, val_x16); |
| 372 | if (err) |
| 373 | return err; |
| 374 | |
| 375 | if (!frac_str) |
| 376 | return 0; |
| 377 | |
| 378 | /* prevent negative number/leading +- sign mark */ |
| 379 | if (!isdigit(c: *frac_str)) |
| 380 | return -EINVAL; |
| 381 | |
| 382 | err = str_to_fxp_q4_nonneg_int(str: frac_str, val_x16: &frac_val); |
| 383 | if (err) |
| 384 | return err; |
| 385 | |
| 386 | frac_digits = strlen(frac_str); |
| 387 | if (frac_digits > intlog10(INT_MAX) >> 24 || |
| 388 | frac_val > INT_MAX - int_pow(base: 10, exp: frac_digits) / 2) |
| 389 | return -ERANGE; |
| 390 | |
| 391 | frac_val = DIV_ROUND_CLOSEST(frac_val, (int)int_pow(10, frac_digits)); |
| 392 | |
| 393 | if (*val_x16 > INT_MAX - frac_val) |
| 394 | return -ERANGE; |
| 395 | |
| 396 | *val_x16 += frac_val; |
| 397 | |
| 398 | return 0; |
| 399 | } |
| 400 | |
| 401 | static int user_str_to_fxp_q4_nonneg(const char __user *ubuf, size_t len, int *val_x16) |
| 402 | { |
| 403 | char *kbuf; |
| 404 | int err; |
| 405 | |
| 406 | kbuf = memdup_user_nul(ubuf, len); |
| 407 | if (IS_ERR(ptr: kbuf)) |
| 408 | return PTR_ERR(ptr: kbuf); |
| 409 | |
| 410 | err = str_to_fxp_q4_nonneg(str: kbuf, val_x16); |
| 411 | |
| 412 | kfree(objp: kbuf); |
| 413 | |
| 414 | return err; |
| 415 | } |
| 416 | |
| 417 | static bool connector_supports_dsc(struct intel_connector *connector) |
| 418 | { |
| 419 | struct intel_display *display = to_intel_display(connector); |
| 420 | |
| 421 | switch (connector->base.connector_type) { |
| 422 | case DRM_MODE_CONNECTOR_eDP: |
| 423 | return intel_dp_has_dsc(connector); |
| 424 | case DRM_MODE_CONNECTOR_DisplayPort: |
| 425 | if (connector->mst.dp) |
| 426 | return HAS_DSC_MST(display); |
| 427 | |
| 428 | return HAS_DSC(display); |
| 429 | default: |
| 430 | return false; |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | static ssize_t |
| 435 | force_link_bpp_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) |
| 436 | { |
| 437 | struct seq_file *m = file->private_data; |
| 438 | struct intel_connector *connector = m->private; |
| 439 | struct intel_display *display = to_intel_display(connector); |
| 440 | int min_bpp; |
| 441 | int bpp_x16; |
| 442 | int err; |
| 443 | |
| 444 | err = user_str_to_fxp_q4_nonneg(ubuf, len, val_x16: &bpp_x16); |
| 445 | if (err) |
| 446 | return err; |
| 447 | |
| 448 | /* TODO: Make the non-DSC min_bpp value connector specific. */ |
| 449 | if (connector_supports_dsc(connector)) |
| 450 | min_bpp = intel_dp_dsc_min_src_compressed_bpp(); |
| 451 | else |
| 452 | min_bpp = intel_display_min_pipe_bpp(); |
| 453 | |
| 454 | if (bpp_x16 && |
| 455 | (bpp_x16 < fxp_q4_from_int(val_int: min_bpp) || |
| 456 | bpp_x16 > fxp_q4_from_int(val_int: intel_display_max_pipe_bpp(display)))) |
| 457 | return -EINVAL; |
| 458 | |
| 459 | err = drm_modeset_lock_single_interruptible(lock: &display->drm->mode_config.connection_mutex); |
| 460 | if (err) |
| 461 | return err; |
| 462 | |
| 463 | connector->link.force_bpp_x16 = bpp_x16; |
| 464 | |
| 465 | drm_modeset_unlock(lock: &display->drm->mode_config.connection_mutex); |
| 466 | |
| 467 | *offp += len; |
| 468 | |
| 469 | return len; |
| 470 | } |
| 471 | DEFINE_SHOW_STORE_ATTRIBUTE(force_link_bpp); |
| 472 | |
| 473 | void intel_link_bw_connector_debugfs_add(struct intel_connector *connector) |
| 474 | { |
| 475 | struct intel_display *display = to_intel_display(connector); |
| 476 | struct dentry *root = connector->base.debugfs_entry; |
| 477 | |
| 478 | switch (connector->base.connector_type) { |
| 479 | case DRM_MODE_CONNECTOR_DisplayPort: |
| 480 | case DRM_MODE_CONNECTOR_eDP: |
| 481 | case DRM_MODE_CONNECTOR_HDMIA: |
| 482 | break; |
| 483 | case DRM_MODE_CONNECTOR_VGA: |
| 484 | case DRM_MODE_CONNECTOR_SVIDEO: |
| 485 | case DRM_MODE_CONNECTOR_LVDS: |
| 486 | case DRM_MODE_CONNECTOR_DVID: |
| 487 | if (HAS_FDI(display)) |
| 488 | break; |
| 489 | |
| 490 | return; |
| 491 | default: |
| 492 | return; |
| 493 | } |
| 494 | |
| 495 | debugfs_create_file("intel_force_link_bpp" , 0644, root, |
| 496 | connector, &force_link_bpp_fops); |
| 497 | } |
| 498 | |