18ca0b875SImre Deak // SPDX-License-Identifier: MIT
28ca0b875SImre Deak /*
38ca0b875SImre Deak  * Copyright © 2023 Intel Corporation
48ca0b875SImre Deak  */
58ca0b875SImre Deak 
631967638SImre Deak #include <drm/drm_fixed.h>
731967638SImre Deak 
88ca0b875SImre Deak #include "i915_drv.h"
98ca0b875SImre Deak 
108ca0b875SImre Deak #include "intel_atomic.h"
111dd9d86aSImre Deak #include "intel_crtc.h"
128ca0b875SImre Deak #include "intel_display_types.h"
1336f579ffSImre Deak #include "intel_dp_mst.h"
1439818c06SImre Deak #include "intel_dp_tunnel.h"
15998d2cd3SImre Deak #include "intel_fdi.h"
168ca0b875SImre Deak #include "intel_link_bw.h"
178ca0b875SImre Deak 
188ca0b875SImre Deak /**
198ca0b875SImre Deak  * intel_link_bw_init_limits - initialize BW limits
201dd9d86aSImre Deak  * @state: Atomic state
218ca0b875SImre Deak  * @limits: link BW limits
228ca0b875SImre Deak  *
238ca0b875SImre Deak  * Initialize @limits.
248ca0b875SImre Deak  */
intel_link_bw_init_limits(struct intel_atomic_state * state,struct intel_link_bw_limits * limits)251dd9d86aSImre Deak void intel_link_bw_init_limits(struct intel_atomic_state *state,
261dd9d86aSImre Deak 			       struct intel_link_bw_limits *limits)
278ca0b875SImre Deak {
28*9aec90f9SJani Nikula 	struct intel_display *display = to_intel_display(state);
291dd9d86aSImre Deak 	struct drm_i915_private *i915 = to_i915(state->base.dev);
308ca0b875SImre Deak 	enum pipe pipe;
318ca0b875SImre Deak 
3236f579ffSImre Deak 	limits->force_fec_pipes = 0;
338ca0b875SImre Deak 	limits->bpp_limit_reached_pipes = 0;
34*9aec90f9SJani Nikula 	for_each_pipe(display, pipe) {
351dd9d86aSImre Deak 		const struct intel_crtc_state *crtc_state =
361dd9d86aSImre Deak 			intel_atomic_get_new_crtc_state(state,
371dd9d86aSImre Deak 							intel_crtc_for_pipe(i915, pipe));
381dd9d86aSImre Deak 
391dd9d86aSImre Deak 		if (state->base.duplicated && crtc_state) {
401dd9d86aSImre Deak 			limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16;
411dd9d86aSImre Deak 			if (crtc_state->fec_enable)
421dd9d86aSImre Deak 				limits->force_fec_pipes |= BIT(pipe);
431dd9d86aSImre Deak 		} else {
448ca0b875SImre Deak 			limits->max_bpp_x16[pipe] = INT_MAX;
458ca0b875SImre Deak 		}
461dd9d86aSImre Deak 	}
471dd9d86aSImre Deak }
488ca0b875SImre Deak 
498ca0b875SImre Deak /**
508ca0b875SImre Deak  * intel_link_bw_reduce_bpp - reduce maximum link bpp for a selected pipe
518ca0b875SImre Deak  * @state: atomic state
528ca0b875SImre Deak  * @limits: link BW limits
538ca0b875SImre Deak  * @pipe_mask: mask of pipes to select from
548ca0b875SImre Deak  * @reason: explanation of why bpp reduction is needed
558ca0b875SImre Deak  *
568ca0b875SImre Deak  * Select the pipe from @pipe_mask with the biggest link bpp value and set the
578ca0b875SImre Deak  * maximum of link bpp in @limits below this value. Modeset the selected pipe,
588ca0b875SImre Deak  * so that its state will get recomputed.
598ca0b875SImre Deak  *
608ca0b875SImre Deak  * This function can be called to resolve a link's BW overallocation by reducing
618ca0b875SImre Deak  * the link bpp of one pipe on the link and hence reducing the total link BW.
628ca0b875SImre Deak  *
638ca0b875SImre Deak  * Returns
648ca0b875SImre Deak  *   - 0 in case of success
658ca0b875SImre Deak  *   - %-ENOSPC if no pipe can further reduce its link bpp
668ca0b875SImre Deak  *   - Other negative error, if modesetting the selected pipe failed
678ca0b875SImre Deak  */
intel_link_bw_reduce_bpp(struct intel_atomic_state * state,struct intel_link_bw_limits * limits,u8 pipe_mask,const char * reason)688ca0b875SImre Deak int intel_link_bw_reduce_bpp(struct intel_atomic_state *state,
698ca0b875SImre Deak 			     struct intel_link_bw_limits *limits,
708ca0b875SImre Deak 			     u8 pipe_mask,
718ca0b875SImre Deak 			     const char *reason)
728ca0b875SImre Deak {
73*9aec90f9SJani Nikula 	struct intel_display *display = to_intel_display(state);
748ca0b875SImre Deak 	enum pipe max_bpp_pipe = INVALID_PIPE;
758ca0b875SImre Deak 	struct intel_crtc *crtc;
767c8601aeSImre Deak 	int max_bpp_x16 = 0;
778ca0b875SImre Deak 
78*9aec90f9SJani Nikula 	for_each_intel_crtc_in_pipe_mask(display->drm, crtc, pipe_mask) {
798ca0b875SImre Deak 		struct intel_crtc_state *crtc_state;
807c8601aeSImre Deak 		int link_bpp_x16;
818ca0b875SImre Deak 
828ca0b875SImre Deak 		if (limits->bpp_limit_reached_pipes & BIT(crtc->pipe))
838ca0b875SImre Deak 			continue;
848ca0b875SImre Deak 
858ca0b875SImre Deak 		crtc_state = intel_atomic_get_crtc_state(&state->base,
868ca0b875SImre Deak 							 crtc);
878ca0b875SImre Deak 		if (IS_ERR(crtc_state))
888ca0b875SImre Deak 			return PTR_ERR(crtc_state);
898ca0b875SImre Deak 
908ca0b875SImre Deak 		if (crtc_state->dsc.compression_enable)
917c8601aeSImre Deak 			link_bpp_x16 = crtc_state->dsc.compressed_bpp_x16;
928ca0b875SImre Deak 		else
938ca0b875SImre Deak 			/*
948ca0b875SImre Deak 			 * TODO: for YUV420 the actual link bpp is only half
958ca0b875SImre Deak 			 * of the pipe bpp value. The MST encoder's BW allocation
968ca0b875SImre Deak 			 * is based on the pipe bpp value, set the actual link bpp
978ca0b875SImre Deak 			 * limit here once the MST BW allocation is fixed.
988ca0b875SImre Deak 			 */
9931967638SImre Deak 			link_bpp_x16 = fxp_q4_from_int(crtc_state->pipe_bpp);
1008ca0b875SImre Deak 
1017c8601aeSImre Deak 		if (link_bpp_x16 > max_bpp_x16) {
1027c8601aeSImre Deak 			max_bpp_x16 = link_bpp_x16;
1038ca0b875SImre Deak 			max_bpp_pipe = crtc->pipe;
1048ca0b875SImre Deak 		}
1058ca0b875SImre Deak 	}
1068ca0b875SImre Deak 
1078ca0b875SImre Deak 	if (max_bpp_pipe == INVALID_PIPE)
1088ca0b875SImre Deak 		return -ENOSPC;
1098ca0b875SImre Deak 
1107c8601aeSImre Deak 	limits->max_bpp_x16[max_bpp_pipe] = max_bpp_x16 - 1;
1118ca0b875SImre Deak 
1128ca0b875SImre Deak 	return intel_modeset_pipes_in_mask_early(state, reason,
1138ca0b875SImre Deak 						 BIT(max_bpp_pipe));
1148ca0b875SImre Deak }
1158ca0b875SImre Deak 
1168ca0b875SImre Deak /**
1178ca0b875SImre Deak  * intel_link_bw_set_bpp_limit_for_pipe - set link bpp limit for a pipe to its minimum
1188ca0b875SImre Deak  * @state: atomic state
1198ca0b875SImre Deak  * @old_limits: link BW limits
1208ca0b875SImre Deak  * @new_limits: link BW limits
1218ca0b875SImre Deak  * @pipe: pipe
1228ca0b875SImre Deak  *
1238ca0b875SImre Deak  * Set the link bpp limit for @pipe in @new_limits to its value in
1248ca0b875SImre Deak  * @old_limits and mark this limit as the minimum. This function must be
1258ca0b875SImre Deak  * called after a pipe's compute config function failed, @old_limits
1268ca0b875SImre Deak  * containing the bpp limit with which compute config previously passed.
1278ca0b875SImre Deak  *
1288ca0b875SImre Deak  * The function will fail if setting a minimum is not possible, either
1298ca0b875SImre Deak  * because the old and new limits match (and so would lead to a pipe compute
1308ca0b875SImre Deak  * config failure) or the limit is already at the minimum.
1318ca0b875SImre Deak  *
1328ca0b875SImre Deak  * Returns %true in case of success.
1338ca0b875SImre Deak  */
1348ca0b875SImre Deak bool
intel_link_bw_set_bpp_limit_for_pipe(struct intel_atomic_state * state,const struct intel_link_bw_limits * old_limits,struct intel_link_bw_limits * new_limits,enum pipe pipe)1358ca0b875SImre Deak intel_link_bw_set_bpp_limit_for_pipe(struct intel_atomic_state *state,
1368ca0b875SImre Deak 				     const struct intel_link_bw_limits *old_limits,
1378ca0b875SImre Deak 				     struct intel_link_bw_limits *new_limits,
1388ca0b875SImre Deak 				     enum pipe pipe)
1398ca0b875SImre Deak {
140*9aec90f9SJani Nikula 	struct intel_display *display = to_intel_display(state);
1418ca0b875SImre Deak 
1428ca0b875SImre Deak 	if (pipe == INVALID_PIPE)
1438ca0b875SImre Deak 		return false;
1448ca0b875SImre Deak 
1458ca0b875SImre Deak 	if (new_limits->max_bpp_x16[pipe] ==
1468ca0b875SImre Deak 	    old_limits->max_bpp_x16[pipe])
1478ca0b875SImre Deak 		return false;
1488ca0b875SImre Deak 
149*9aec90f9SJani Nikula 	if (drm_WARN_ON(display->drm,
1508ca0b875SImre Deak 			new_limits->bpp_limit_reached_pipes & BIT(pipe)))
1518ca0b875SImre Deak 		return false;
1528ca0b875SImre Deak 
1538ca0b875SImre Deak 	new_limits->max_bpp_x16[pipe] =
1548ca0b875SImre Deak 		old_limits->max_bpp_x16[pipe];
1558ca0b875SImre Deak 	new_limits->bpp_limit_reached_pipes |= BIT(pipe);
1568ca0b875SImre Deak 
1578ca0b875SImre Deak 	return true;
1588ca0b875SImre Deak }
1598ca0b875SImre Deak 
check_all_link_config(struct intel_atomic_state * state,struct intel_link_bw_limits * limits)1608ca0b875SImre Deak static int check_all_link_config(struct intel_atomic_state *state,
1618ca0b875SImre Deak 				 struct intel_link_bw_limits *limits)
1628ca0b875SImre Deak {
163998d2cd3SImre Deak 	/* TODO: Check additional shared display link configurations like MST */
164998d2cd3SImre Deak 	int ret;
165998d2cd3SImre Deak 
16636f579ffSImre Deak 	ret = intel_dp_mst_atomic_check_link(state, limits);
16736f579ffSImre Deak 	if (ret)
16836f579ffSImre Deak 		return ret;
16936f579ffSImre Deak 
17039818c06SImre Deak 	ret = intel_dp_tunnel_atomic_check_link(state, limits);
17139818c06SImre Deak 	if (ret)
17239818c06SImre Deak 		return ret;
17339818c06SImre Deak 
174998d2cd3SImre Deak 	ret = intel_fdi_atomic_check_link(state, limits);
175998d2cd3SImre Deak 	if (ret)
176998d2cd3SImre Deak 		return ret;
177998d2cd3SImre Deak 
1788ca0b875SImre Deak 	return 0;
1798ca0b875SImre Deak }
1808ca0b875SImre Deak 
1818ca0b875SImre Deak static bool
assert_link_limit_change_valid(struct intel_display * display,const struct intel_link_bw_limits * old_limits,const struct intel_link_bw_limits * new_limits)182*9aec90f9SJani Nikula assert_link_limit_change_valid(struct intel_display *display,
1838ca0b875SImre Deak 			       const struct intel_link_bw_limits *old_limits,
1848ca0b875SImre Deak 			       const struct intel_link_bw_limits *new_limits)
1858ca0b875SImre Deak {
1868ca0b875SImre Deak 	bool bpps_changed = false;
1878ca0b875SImre Deak 	enum pipe pipe;
1888ca0b875SImre Deak 
18936f579ffSImre Deak 	/* FEC can't be forced off after it was forced on. */
190*9aec90f9SJani Nikula 	if (drm_WARN_ON(display->drm,
19136f579ffSImre Deak 			(old_limits->force_fec_pipes & new_limits->force_fec_pipes) !=
19236f579ffSImre Deak 			old_limits->force_fec_pipes))
19336f579ffSImre Deak 		return false;
19436f579ffSImre Deak 
195*9aec90f9SJani Nikula 	for_each_pipe(display, pipe) {
1968ca0b875SImre Deak 		/* The bpp limit can only decrease. */
197*9aec90f9SJani Nikula 		if (drm_WARN_ON(display->drm,
1988ca0b875SImre Deak 				new_limits->max_bpp_x16[pipe] >
1998ca0b875SImre Deak 				old_limits->max_bpp_x16[pipe]))
2008ca0b875SImre Deak 			return false;
2018ca0b875SImre Deak 
2028ca0b875SImre Deak 		if (new_limits->max_bpp_x16[pipe] <
2038ca0b875SImre Deak 		    old_limits->max_bpp_x16[pipe])
2048ca0b875SImre Deak 			bpps_changed = true;
2058ca0b875SImre Deak 	}
2068ca0b875SImre Deak 
2078ca0b875SImre Deak 	/* At least one limit must change. */
208*9aec90f9SJani Nikula 	if (drm_WARN_ON(display->drm,
20936f579ffSImre Deak 			!bpps_changed &&
21036f579ffSImre Deak 			new_limits->force_fec_pipes ==
21136f579ffSImre Deak 			old_limits->force_fec_pipes))
2128ca0b875SImre Deak 		return false;
2138ca0b875SImre Deak 
2148ca0b875SImre Deak 	return true;
2158ca0b875SImre Deak }
2168ca0b875SImre Deak 
2178ca0b875SImre Deak /**
2188ca0b875SImre Deak  * intel_link_bw_atomic_check - check display link states and set a fallback config if needed
2198ca0b875SImre Deak  * @state: atomic state
2208ca0b875SImre Deak  * @new_limits: link BW limits
2218ca0b875SImre Deak  *
2228ca0b875SImre Deak  * Check the configuration of all shared display links in @state and set new BW
2238ca0b875SImre Deak  * limits in @new_limits if there is a BW limitation.
2248ca0b875SImre Deak  *
2258ca0b875SImre Deak  * Returns:
2268ca0b875SImre Deak  *   - 0 if the confugration is valid
2278ca0b875SImre Deak  *   - %-EAGAIN, if the configuration is invalid and @new_limits got updated
2288ca0b875SImre Deak  *     with fallback values with which the configuration of all CRTCs
2298ca0b875SImre Deak  *     in @state must be recomputed
2308ca0b875SImre Deak  *   - Other negative error, if the configuration is invalid without a
2318ca0b875SImre Deak  *     fallback possibility, or the check failed for another reason
2328ca0b875SImre Deak  */
intel_link_bw_atomic_check(struct intel_atomic_state * state,struct intel_link_bw_limits * new_limits)2338ca0b875SImre Deak int intel_link_bw_atomic_check(struct intel_atomic_state *state,
2348ca0b875SImre Deak 			       struct intel_link_bw_limits *new_limits)
2358ca0b875SImre Deak {
236*9aec90f9SJani Nikula 	struct intel_display *display = to_intel_display(state);
2378ca0b875SImre Deak 	struct intel_link_bw_limits old_limits = *new_limits;
2388ca0b875SImre Deak 	int ret;
2398ca0b875SImre Deak 
2408ca0b875SImre Deak 	ret = check_all_link_config(state, new_limits);
2418ca0b875SImre Deak 	if (ret != -EAGAIN)
2428ca0b875SImre Deak 		return ret;
2438ca0b875SImre Deak 
244*9aec90f9SJani Nikula 	if (!assert_link_limit_change_valid(display, &old_limits, new_limits))
2458ca0b875SImre Deak 		return -EINVAL;
2468ca0b875SImre Deak 
2478ca0b875SImre Deak 	return -EAGAIN;
2488ca0b875SImre Deak }
249