File Index
Files and directories
Sign in
FILE

intel_link_bw.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/i915/display/intel_link_bw.c C
  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
 25static 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 */
 53void 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 */
100static 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->base,
119							 crtc);
120		if (IS_ERR(crtc_state))
121			return PTR_ERR(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(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
153int 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, false);
162	if (ret == -ENOSPC)
163		ret = __intel_link_bw_reduce_bpp(state, limits, pipe_mask, reason, 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 */
181bool 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 */
214bool
215intel_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
240static 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
261static bool
262assert_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 */
313int 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, new_limits);
321	if (ret != -EAGAIN)
322		return ret;
323
324	if (!assert_link_limit_change_valid(display, &old_limits, new_limits))
325		return -EINVAL;
326
327	return -EAGAIN;
328}
329
330static 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
339static 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(str, 10, &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);
352
353	return 0;
354}
355
356/* modifies str */
357static 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(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(*frac_str))
380		return -EINVAL;
381
382	err = str_to_fxp_q4_nonneg_int(frac_str, &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(10, 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
401static 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(kbuf))
408		return PTR_ERR(kbuf);
409
410	err = str_to_fxp_q4_nonneg(kbuf, val_x16);
411
412	kfree(kbuf);
413
414	return err;
415}
416
417static 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
434static ssize_t
435force_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, &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(min_bpp) ||
456	     bpp_x16 > fxp_q4_from_int(intel_display_max_pipe_bpp(display))))
457		return -EINVAL;
458
459	err = drm_modeset_lock_single_interruptible(&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(&display->drm->mode_config.connection_mutex);
466
467	*offp += len;
468
469	return len;
470}
471DEFINE_SHOW_STORE_ATTRIBUTE(force_link_bpp);
472
473void 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}
~/ Read-only file preview.