mirror of
https://github.com/torvalds/linux.git
synced 2026-05-24 07:03:03 +02:00
drm/i915: Add support for forcing the link bpp on a connector
Add support for forcing the link bpp on a connector via a connector debugfs entry. During reducing link bpps due to a link BW limit, keep bpps close to their forced value. Add the debugfs entry to all relevant connectors: all DP connectors and on an FDI link CRT/SDVO/LVDS/HDMI connectors. v2: - Move adding the debugfs entries to this patch. - Rename i915_force_link_bpp to intel_force_link_bpp. (Jani) - Select the relevant connectors via platform checks. (Jani) - Use for_each_new_intel_connector_in_state(). (Jani) - Fix 64 bit division vs. 32 bit build when converting str to q4. (lkp) - Avoid division and addition overflow when converting str to q4. v3: - Add TODO: to make the non-DSC min bpp value connector specific. (Ankit) Cc: Jani Nikula <jani.nikula@intel.com> Reviewed-by: Ankit Nautiyal <ankit.k.nautiyal@intel.com> Signed-off-by: Imre Deak <imre.deak@intel.com> Link: https://lore.kernel.org/r/20250509180340.554867-12-imre.deak@intel.com
This commit is contained in:
parent
67e12c64b4
commit
f7f46a80fa
|
|
@ -39,6 +39,7 @@
|
|||
#include "intel_hdcp.h"
|
||||
#include "intel_hdmi.h"
|
||||
#include "intel_hotplug.h"
|
||||
#include "intel_link_bw.h"
|
||||
#include "intel_panel.h"
|
||||
#include "intel_pps.h"
|
||||
#include "intel_psr.h"
|
||||
|
|
@ -1325,6 +1326,7 @@ void intel_connector_debugfs_add(struct intel_connector *connector)
|
|||
intel_psr_connector_debugfs_add(connector);
|
||||
intel_alpm_lobf_debugfs_add(connector);
|
||||
intel_dp_link_training_debugfs_add(connector);
|
||||
intel_link_bw_connector_debugfs_add(connector);
|
||||
|
||||
if (DISPLAY_VER(display) >= 11 &&
|
||||
((connector_type == DRM_MODE_CONNECTOR_DisplayPort && !connector->mst.dp) ||
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ struct intel_display_platforms {
|
|||
#define HAS_GMBUS_BURST_READ(__display) (DISPLAY_VER(__display) >= 10 || (__display)->platform.kabylake)
|
||||
#define HAS_GMBUS_IRQ(__display) (DISPLAY_VER(__display) >= 4)
|
||||
#define HAS_GMCH(__display) (DISPLAY_INFO(__display)->has_gmch)
|
||||
#define HAS_FDI(__display) (IS_DISPLAY_VER((__display), 5, 8) && !HAS_GMCH(__display))
|
||||
#define HAS_HOTPLUG(__display) (DISPLAY_INFO(__display)->has_hotplug)
|
||||
#define HAS_HW_SAGV_WM(__display) (DISPLAY_VER(__display) >= 13 && !(__display)->platform.dgfx)
|
||||
#define HAS_IPC(__display) (DISPLAY_INFO(__display)->has_ipc)
|
||||
|
|
|
|||
|
|
@ -550,6 +550,10 @@ struct intel_connector {
|
|||
struct intel_dp *dp;
|
||||
} mst;
|
||||
|
||||
struct {
|
||||
int force_bpp_x16;
|
||||
} link;
|
||||
|
||||
/* Work struct to schedule a uevent on link train failure */
|
||||
struct work_struct modeset_retry_work;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,11 @@
|
|||
* Copyright © 2023 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/int_log.h>
|
||||
#include <linux/math.h>
|
||||
|
||||
#include <drm/drm_fixed.h>
|
||||
#include <drm/drm_print.h>
|
||||
|
||||
|
|
@ -10,11 +15,33 @@
|
|||
#include "intel_crtc.h"
|
||||
#include "intel_display_core.h"
|
||||
#include "intel_display_types.h"
|
||||
#include "intel_dp.h"
|
||||
#include "intel_dp_mst.h"
|
||||
#include "intel_dp_tunnel.h"
|
||||
#include "intel_fdi.h"
|
||||
#include "intel_link_bw.h"
|
||||
|
||||
static int get_forced_link_bpp_x16(struct intel_atomic_state *state,
|
||||
const struct intel_crtc *crtc)
|
||||
{
|
||||
struct intel_digital_connector_state *conn_state;
|
||||
struct intel_connector *connector;
|
||||
int force_bpp_x16 = INT_MAX;
|
||||
int i;
|
||||
|
||||
for_each_new_intel_connector_in_state(state, connector, conn_state, i) {
|
||||
if (conn_state->base.crtc != &crtc->base)
|
||||
continue;
|
||||
|
||||
if (!connector->link.force_bpp_x16)
|
||||
continue;
|
||||
|
||||
force_bpp_x16 = min(force_bpp_x16, connector->link.force_bpp_x16);
|
||||
}
|
||||
|
||||
return force_bpp_x16 < INT_MAX ? force_bpp_x16 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_link_bw_init_limits - initialize BW limits
|
||||
* @state: Atomic state
|
||||
|
|
@ -31,9 +58,10 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
|
|||
limits->force_fec_pipes = 0;
|
||||
limits->bpp_limit_reached_pipes = 0;
|
||||
for_each_pipe(display, pipe) {
|
||||
struct intel_crtc *crtc = intel_crtc_for_pipe(display, pipe);
|
||||
const struct intel_crtc_state *crtc_state =
|
||||
intel_atomic_get_new_crtc_state(state,
|
||||
intel_crtc_for_pipe(display, pipe));
|
||||
intel_atomic_get_new_crtc_state(state, crtc);
|
||||
int forced_bpp_x16 = get_forced_link_bpp_x16(state, crtc);
|
||||
|
||||
if (state->base.duplicated && crtc_state) {
|
||||
limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16;
|
||||
|
|
@ -42,15 +70,19 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
|
|||
} else {
|
||||
limits->max_bpp_x16[pipe] = INT_MAX;
|
||||
}
|
||||
|
||||
if (forced_bpp_x16)
|
||||
limits->max_bpp_x16[pipe] = min(limits->max_bpp_x16[pipe], forced_bpp_x16);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_link_bw_reduce_bpp - reduce maximum link bpp for a selected pipe
|
||||
* __intel_link_bw_reduce_bpp - reduce maximum link bpp for a selected pipe
|
||||
* @state: atomic state
|
||||
* @limits: link BW limits
|
||||
* @pipe_mask: mask of pipes to select from
|
||||
* @reason: explanation of why bpp reduction is needed
|
||||
* @reduce_forced_bpp: allow reducing bpps below their forced link bpp
|
||||
*
|
||||
* Select the pipe from @pipe_mask with the biggest link bpp value and set the
|
||||
* maximum of link bpp in @limits below this value. Modeset the selected pipe,
|
||||
|
|
@ -64,10 +96,11 @@ void intel_link_bw_init_limits(struct intel_atomic_state *state,
|
|||
* - %-ENOSPC if no pipe can further reduce its link bpp
|
||||
* - Other negative error, if modesetting the selected pipe failed
|
||||
*/
|
||||
int intel_link_bw_reduce_bpp(struct intel_atomic_state *state,
|
||||
struct intel_link_bw_limits *limits,
|
||||
u8 pipe_mask,
|
||||
const char *reason)
|
||||
static int __intel_link_bw_reduce_bpp(struct intel_atomic_state *state,
|
||||
struct intel_link_bw_limits *limits,
|
||||
u8 pipe_mask,
|
||||
const char *reason,
|
||||
bool reduce_forced_bpp)
|
||||
{
|
||||
struct intel_display *display = to_intel_display(state);
|
||||
enum pipe max_bpp_pipe = INVALID_PIPE;
|
||||
|
|
@ -97,6 +130,10 @@ int intel_link_bw_reduce_bpp(struct intel_atomic_state *state,
|
|||
*/
|
||||
link_bpp_x16 = fxp_q4_from_int(crtc_state->pipe_bpp);
|
||||
|
||||
if (!reduce_forced_bpp &&
|
||||
link_bpp_x16 <= get_forced_link_bpp_x16(state, crtc))
|
||||
continue;
|
||||
|
||||
if (link_bpp_x16 > max_bpp_x16) {
|
||||
max_bpp_x16 = link_bpp_x16;
|
||||
max_bpp_pipe = crtc->pipe;
|
||||
|
|
@ -112,6 +149,21 @@ int intel_link_bw_reduce_bpp(struct intel_atomic_state *state,
|
|||
BIT(max_bpp_pipe));
|
||||
}
|
||||
|
||||
int intel_link_bw_reduce_bpp(struct intel_atomic_state *state,
|
||||
struct intel_link_bw_limits *limits,
|
||||
u8 pipe_mask,
|
||||
const char *reason)
|
||||
{
|
||||
int ret;
|
||||
|
||||
/* Try to keep any forced link BPP. */
|
||||
ret = __intel_link_bw_reduce_bpp(state, limits, pipe_mask, reason, false);
|
||||
if (ret == -ENOSPC)
|
||||
ret = __intel_link_bw_reduce_bpp(state, limits, pipe_mask, reason, true);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* intel_link_bw_set_bpp_limit_for_pipe - set link bpp limit for a pipe to its minimum
|
||||
* @state: atomic state
|
||||
|
|
@ -245,3 +297,176 @@ int intel_link_bw_atomic_check(struct intel_atomic_state *state,
|
|||
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int force_link_bpp_show(struct seq_file *m, void *data)
|
||||
{
|
||||
struct intel_connector *connector = m->private;
|
||||
|
||||
seq_printf(m, FXP_Q4_FMT "\n", FXP_Q4_ARGS(connector->link.force_bpp_x16));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int str_to_fxp_q4_nonneg_int(const char *str, int *val_x16)
|
||||
{
|
||||
unsigned int val;
|
||||
int err;
|
||||
|
||||
err = kstrtouint(str, 10, &val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (val > INT_MAX >> 4)
|
||||
return -ERANGE;
|
||||
|
||||
*val_x16 = fxp_q4_from_int(val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* modifies str */
|
||||
static int str_to_fxp_q4_nonneg(char *str, int *val_x16)
|
||||
{
|
||||
const char *int_str;
|
||||
char *frac_str;
|
||||
int frac_digits;
|
||||
int frac_val;
|
||||
int err;
|
||||
|
||||
int_str = strim(str);
|
||||
frac_str = strchr(int_str, '.');
|
||||
|
||||
if (frac_str)
|
||||
*frac_str++ = '\0';
|
||||
|
||||
err = str_to_fxp_q4_nonneg_int(int_str, val_x16);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (!frac_str)
|
||||
return 0;
|
||||
|
||||
/* prevent negative number/leading +- sign mark */
|
||||
if (!isdigit(*frac_str))
|
||||
return -EINVAL;
|
||||
|
||||
err = str_to_fxp_q4_nonneg_int(frac_str, &frac_val);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
frac_digits = strlen(frac_str);
|
||||
if (frac_digits > intlog10(INT_MAX) >> 24 ||
|
||||
frac_val > INT_MAX - int_pow(10, frac_digits) / 2)
|
||||
return -ERANGE;
|
||||
|
||||
frac_val = DIV_ROUND_CLOSEST(frac_val, (int)int_pow(10, frac_digits));
|
||||
|
||||
if (*val_x16 > INT_MAX - frac_val)
|
||||
return -ERANGE;
|
||||
|
||||
*val_x16 += frac_val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int user_str_to_fxp_q4_nonneg(const char __user *ubuf, size_t len, int *val_x16)
|
||||
{
|
||||
char *kbuf;
|
||||
int err;
|
||||
|
||||
kbuf = memdup_user_nul(ubuf, len);
|
||||
if (IS_ERR(kbuf))
|
||||
return PTR_ERR(kbuf);
|
||||
|
||||
err = str_to_fxp_q4_nonneg(kbuf, val_x16);
|
||||
|
||||
kfree(kbuf);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool connector_supports_dsc(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_display *display = to_intel_display(connector);
|
||||
|
||||
switch (connector->base.connector_type) {
|
||||
case DRM_MODE_CONNECTOR_eDP:
|
||||
return intel_dp_has_dsc(connector);
|
||||
case DRM_MODE_CONNECTOR_DisplayPort:
|
||||
if (connector->mst.dp)
|
||||
return HAS_DSC_MST(display);
|
||||
|
||||
return HAS_DSC(display);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
force_link_bpp_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp)
|
||||
{
|
||||
struct seq_file *m = file->private_data;
|
||||
struct intel_connector *connector = m->private;
|
||||
struct intel_display *display = to_intel_display(connector);
|
||||
int min_bpp;
|
||||
int bpp_x16;
|
||||
int err;
|
||||
|
||||
err = user_str_to_fxp_q4_nonneg(ubuf, len, &bpp_x16);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* TODO: Make the non-DSC min_bpp value connector specific. */
|
||||
if (connector_supports_dsc(connector))
|
||||
min_bpp = intel_dp_dsc_min_src_compressed_bpp();
|
||||
else
|
||||
min_bpp = intel_display_min_pipe_bpp();
|
||||
|
||||
if (bpp_x16 &&
|
||||
(bpp_x16 < fxp_q4_from_int(min_bpp) ||
|
||||
bpp_x16 > fxp_q4_from_int(intel_display_max_pipe_bpp(display))))
|
||||
return -EINVAL;
|
||||
|
||||
err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
connector->link.force_bpp_x16 = bpp_x16;
|
||||
|
||||
drm_modeset_unlock(&display->drm->mode_config.connection_mutex);
|
||||
|
||||
*offp += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
DEFINE_SHOW_STORE_ATTRIBUTE(force_link_bpp);
|
||||
|
||||
void intel_link_bw_connector_debugfs_add(struct intel_connector *connector)
|
||||
{
|
||||
struct intel_display *display = to_intel_display(connector);
|
||||
struct dentry *root = connector->base.debugfs_entry;
|
||||
|
||||
switch (connector->base.connector_type) {
|
||||
case DRM_MODE_CONNECTOR_DisplayPort:
|
||||
case DRM_MODE_CONNECTOR_eDP:
|
||||
break;
|
||||
case DRM_MODE_CONNECTOR_VGA:
|
||||
case DRM_MODE_CONNECTOR_SVIDEO:
|
||||
case DRM_MODE_CONNECTOR_LVDS:
|
||||
case DRM_MODE_CONNECTOR_DVID:
|
||||
if (HAS_FDI(display))
|
||||
break;
|
||||
|
||||
return;
|
||||
case DRM_MODE_CONNECTOR_HDMIA:
|
||||
if (HAS_FDI(display) && !HAS_DDI(display))
|
||||
break;
|
||||
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
debugfs_create_file("intel_force_link_bpp", 0644, root,
|
||||
connector, &force_link_bpp_fops);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include "intel_display_limits.h"
|
||||
|
||||
struct intel_atomic_state;
|
||||
struct intel_connector;
|
||||
struct intel_crtc_state;
|
||||
|
||||
struct intel_link_bw_limits {
|
||||
|
|
@ -32,5 +33,6 @@ bool intel_link_bw_set_bpp_limit_for_pipe(struct intel_atomic_state *state,
|
|||
enum pipe pipe);
|
||||
int intel_link_bw_atomic_check(struct intel_atomic_state *state,
|
||||
struct intel_link_bw_limits *new_limits);
|
||||
void intel_link_bw_connector_debugfs_add(struct intel_connector *connector);
|
||||
|
||||
#endif
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user