summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display/intel_dp_mst.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_dp_mst.c')
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp_mst.c246
1 files changed, 169 insertions, 77 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c
index b651c990af85..c772ba19c547 100644
--- a/drivers/gpu/drm/i915/display/intel_dp_mst.c
+++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c
@@ -51,25 +51,39 @@
#include "intel_vdsc.h"
#include "skl_scaler.h"
-static int intel_dp_mst_check_constraints(struct drm_i915_private *i915, int bpp,
- const struct drm_display_mode *adjusted_mode,
- struct intel_crtc_state *crtc_state,
- bool dsc)
+static int intel_dp_mst_max_dpt_bpp(const struct intel_crtc_state *crtc_state,
+ bool dsc)
{
- if (intel_dp_is_uhbr(crtc_state) && DISPLAY_VER(i915) < 14 && dsc) {
- int output_bpp = bpp;
- /* DisplayPort 2 128b/132b, bits per lane is always 32 */
- int symbol_clock = crtc_state->port_clock / 32;
-
- if (output_bpp * adjusted_mode->crtc_clock >=
- symbol_clock * 72) {
- drm_dbg_kms(&i915->drm, "UHBR check failed(required bw %d available %d)\n",
- output_bpp * adjusted_mode->crtc_clock, symbol_clock * 72);
- return -EINVAL;
- }
- }
+ struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+ const struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
- return 0;
+ if (!intel_dp_is_uhbr(crtc_state) || DISPLAY_VER(i915) >= 20 || !dsc)
+ return INT_MAX;
+
+ /*
+ * DSC->DPT interface width:
+ * ICL-MTL: 72 bits (each branch has 72 bits, only left branch is used)
+ * LNL+: 144 bits (not a bottleneck in any config)
+ *
+ * Bspec/49259 suggests that the FEC overhead needs to be
+ * applied here, though HW people claim that neither this FEC
+ * or any other overhead is applicable here (that is the actual
+ * available_bw is just symbol_clock * 72). However based on
+ * testing on MTL-P the
+ * - DELL U3224KBA display
+ * - Unigraf UCD-500 CTS test sink
+ * devices the
+ * - 5120x2880/995.59Mhz
+ * - 6016x3384/1357.23Mhz
+ * - 6144x3456/1413.39Mhz
+ * modes (all the ones having a DPT limit on the above devices),
+ * both the channel coding efficiency and an additional 3%
+ * overhead needs to be accounted for.
+ */
+ return div64_u64(mul_u32_u32(intel_dp_link_symbol_clock(crtc_state->port_clock) * 72,
+ drm_dp_bw_channel_coding_efficiency(true)),
+ mul_u32_u32(adjusted_mode->crtc_clock, 1030000));
}
static int intel_dp_mst_bw_overhead(const struct intel_crtc_state *crtc_state,
@@ -88,11 +102,10 @@ static int intel_dp_mst_bw_overhead(const struct intel_crtc_state *crtc_state,
if (dsc) {
flags |= DRM_DP_BW_OVERHEAD_DSC;
- /* TODO: add support for bigjoiner */
dsc_slice_count = intel_dp_dsc_get_slice_count(connector,
adjusted_mode->clock,
adjusted_mode->hdisplay,
- false);
+ crtc_state->bigjoiner_pipes);
}
overhead = drm_dp_bw_overhead(crtc_state->lane_count,
@@ -158,6 +171,7 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
const struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode;
int bpp, slots = -EINVAL;
+ int max_dpt_bpp;
int ret = 0;
mst_state = drm_atomic_get_mst_topology_state(state, &intel_dp->mst_mgr);
@@ -178,6 +192,13 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
crtc_state->port_clock,
crtc_state->lane_count);
+ max_dpt_bpp = intel_dp_mst_max_dpt_bpp(crtc_state, dsc);
+ if (max_bpp > max_dpt_bpp) {
+ drm_dbg_kms(&i915->drm, "Limiting bpp to max DPT bpp (%d -> %d)\n",
+ max_bpp, max_dpt_bpp);
+ max_bpp = max_dpt_bpp;
+ }
+
drm_dbg_kms(&i915->drm, "Looking for slots in range min bpp %d max bpp %d\n",
min_bpp, max_bpp);
@@ -189,10 +210,6 @@ static int intel_dp_mst_find_vcpi_slots_for_bpp(struct intel_encoder *encoder,
drm_dbg_kms(&i915->drm, "Trying bpp %d\n", bpp);
- ret = intel_dp_mst_check_constraints(i915, bpp, adjusted_mode, crtc_state, dsc);
- if (ret)
- continue;
-
link_bpp_x16 = to_bpp_x16(dsc ? bpp :
intel_dp_output_bpp(crtc_state->output_format, bpp));
@@ -404,15 +421,22 @@ static int mode_hblank_period_ns(const struct drm_display_mode *mode)
static bool
hblank_expansion_quirk_needs_dsc(const struct intel_connector *connector,
- const struct intel_crtc_state *crtc_state)
+ const struct intel_crtc_state *crtc_state,
+ const struct link_config_limits *limits)
{
const struct drm_display_mode *adjusted_mode =
&crtc_state->hw.adjusted_mode;
+ bool is_uhbr_sink = connector->mst_port &&
+ drm_dp_128b132b_supported(connector->mst_port->dpcd);
+ int hblank_limit = is_uhbr_sink ? 500 : 300;
if (!connector->dp.dsc_hblank_expansion_quirk)
return false;
- if (mode_hblank_period_ns(adjusted_mode) > 300)
+ if (is_uhbr_sink && !drm_dp_is_uhbr_rate(limits->max_rate))
+ return false;
+
+ if (mode_hblank_period_ns(adjusted_mode) > hblank_limit)
return false;
return true;
@@ -428,7 +452,7 @@ adjust_limits_for_dsc_hblank_expansion_quirk(const struct intel_connector *conne
const struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc);
int min_bpp_x16 = limits->link.min_bpp_x16;
- if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state))
+ if (!hblank_expansion_quirk_needs_dsc(connector, crtc_state, limits))
return true;
if (!dsc) {
@@ -525,14 +549,15 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
{
struct drm_i915_private *dev_priv = to_i915(encoder->base.dev);
struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state);
+ struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc);
struct intel_dp_mst_encoder *intel_mst = enc_to_mst(encoder);
struct intel_dp *intel_dp = &intel_mst->primary->dp;
- const struct intel_connector *connector =
+ struct intel_connector *connector =
to_intel_connector(conn_state->connector);
const struct drm_display_mode *adjusted_mode =
&pipe_config->hw.adjusted_mode;
struct link_config_limits limits;
- bool dsc_needed;
+ bool dsc_needed, joiner_needs_dsc;
int ret = 0;
if (pipe_config->fec_enable &&
@@ -542,11 +567,18 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN)
return -EINVAL;
+ if (intel_dp_need_bigjoiner(intel_dp, connector,
+ adjusted_mode->crtc_hdisplay,
+ adjusted_mode->crtc_clock))
+ pipe_config->bigjoiner_pipes = GENMASK(crtc->pipe + 1, crtc->pipe);
+
pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB;
pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB;
pipe_config->has_pch_encoder = false;
- dsc_needed = intel_dp->force_dsc_en ||
+ joiner_needs_dsc = intel_dp_joiner_needs_dsc(dev_priv, pipe_config->bigjoiner_pipes);
+
+ dsc_needed = joiner_needs_dsc || intel_dp->force_dsc_en ||
!intel_dp_mst_compute_config_limits(intel_dp,
connector,
pipe_config,
@@ -566,8 +598,8 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
/* enable compression if the mode doesn't fit available BW */
if (dsc_needed) {
- drm_dbg_kms(&dev_priv->drm, "Try DSC (fallback=%s, force=%s)\n",
- str_yes_no(ret),
+ drm_dbg_kms(&dev_priv->drm, "Try DSC (fallback=%s, joiner=%s, force=%s)\n",
+ str_yes_no(ret), str_yes_no(joiner_needs_dsc),
str_yes_no(intel_dp->force_dsc_en));
if (!intel_dp_mst_dsc_source_support(pipe_config))
@@ -613,7 +645,7 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder,
if (IS_GEMINILAKE(dev_priv) || IS_BROXTON(dev_priv))
pipe_config->lane_lat_optim_mask =
- bxt_ddi_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
+ bxt_dpio_phy_calc_lane_lat_optim_mask(pipe_config->lane_count);
intel_dp_audio_compute_config(encoder, pipe_config, conn_state);
@@ -954,6 +986,7 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
struct drm_dp_mst_atomic_payload *new_payload =
drm_atomic_get_mst_payload_state(new_mst_state, connector->port);
struct drm_i915_private *dev_priv = to_i915(connector->base.dev);
+ struct intel_crtc *pipe_crtc;
bool last_mst_stream;
intel_dp->active_mst_links--;
@@ -962,7 +995,13 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
DISPLAY_VER(dev_priv) >= 12 && last_mst_stream &&
!intel_dp_mst_is_master_trans(old_crtc_state));
- intel_crtc_vblank_off(old_crtc_state);
+ for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
+ intel_crtc_joined_pipe_mask(old_crtc_state)) {
+ const struct intel_crtc_state *old_pipe_crtc_state =
+ intel_atomic_get_old_crtc_state(state, pipe_crtc);
+
+ intel_crtc_vblank_off(old_pipe_crtc_state);
+ }
intel_disable_transcoder(old_crtc_state);
@@ -980,12 +1019,18 @@ static void intel_mst_post_disable_dp(struct intel_atomic_state *state,
intel_ddi_disable_transcoder_func(old_crtc_state);
- intel_dsc_disable(old_crtc_state);
+ for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, pipe_crtc,
+ intel_crtc_joined_pipe_mask(old_crtc_state)) {
+ const struct intel_crtc_state *old_pipe_crtc_state =
+ intel_atomic_get_old_crtc_state(state, pipe_crtc);
- if (DISPLAY_VER(dev_priv) >= 9)
- skl_scaler_disable(old_crtc_state);
- else
- ilk_pfit_disable(old_crtc_state);
+ intel_dsc_disable(old_pipe_crtc_state);
+
+ if (DISPLAY_VER(dev_priv) >= 9)
+ skl_scaler_disable(old_pipe_crtc_state);
+ else
+ ilk_pfit_disable(old_pipe_crtc_state);
+ }
/*
* Power down mst path before disabling the port, otherwise we end
@@ -1117,6 +1162,39 @@ static void intel_mst_pre_enable_dp(struct intel_atomic_state *state,
intel_ddi_set_dp_msa(pipe_config, conn_state);
}
+static void enable_bs_jitter_was(const struct intel_crtc_state *crtc_state)
+{
+ struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev);
+ u32 clear = 0;
+ u32 set = 0;
+
+ if (!IS_ALDERLAKE_P(i915))
+ return;
+
+ if (!IS_DISPLAY_STEP(i915, STEP_D0, STEP_FOREVER))
+ return;
+
+ /* Wa_14013163432:adlp */
+ if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
+ set |= DP_MST_FEC_BS_JITTER_WA(crtc_state->cpu_transcoder);
+
+ /* Wa_14014143976:adlp */
+ if (IS_DISPLAY_STEP(i915, STEP_E0, STEP_FOREVER)) {
+ if (intel_dp_is_uhbr(crtc_state))
+ set |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
+ else if (crtc_state->fec_enable)
+ clear |= DP_MST_SHORT_HBLANK_WA(crtc_state->cpu_transcoder);
+
+ if (crtc_state->fec_enable || intel_dp_is_uhbr(crtc_state))
+ set |= DP_MST_DPT_DPTP_ALIGN_WA(crtc_state->cpu_transcoder);
+ }
+
+ if (!clear && !set)
+ return;
+
+ intel_de_rmw(i915, CHICKEN_MISC_3, clear, set);
+}
+
static void intel_mst_enable_dp(struct intel_atomic_state *state,
struct intel_encoder *encoder,
const struct intel_crtc_state *pipe_config,
@@ -1131,6 +1209,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
drm_atomic_get_new_mst_topology_state(&state->base, &intel_dp->mst_mgr);
enum transcoder trans = pipe_config->cpu_transcoder;
bool first_mst_stream = intel_dp->active_mst_links == 1;
+ struct intel_crtc *pipe_crtc;
drm_WARN_ON(&dev_priv->drm, pipe_config->has_pch_encoder);
@@ -1145,6 +1224,8 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
TRANS_DP2_VFREQ_PIXEL_CLOCK(crtc_clock_hz & 0xffffff));
}
+ enable_bs_jitter_was(pipe_config);
+
intel_ddi_enable_transcoder_func(encoder, pipe_config);
clear_act_sent(encoder, pipe_config);
@@ -1172,7 +1253,13 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state,
intel_enable_transcoder(pipe_config);
- intel_crtc_vblank_on(pipe_config);
+ for_each_intel_crtc_in_pipe_mask_reverse(&dev_priv->drm, pipe_crtc,
+ intel_crtc_joined_pipe_mask(pipe_config)) {
+ const struct intel_crtc_state *pipe_crtc_state =
+ intel_atomic_get_new_crtc_state(state, pipe_crtc);
+
+ intel_crtc_vblank_on(pipe_crtc_state);
+ }
intel_hdcp_enable(state, encoder, pipe_config, conn_state);
}
@@ -1285,7 +1372,7 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
struct drm_dp_mst_topology_mgr *mgr = &intel_dp->mst_mgr;
struct drm_dp_mst_port *port = intel_connector->port;
const int min_bpp = 18;
- int max_dotclk = to_i915(connector->dev)->max_dotclk_freq;
+ int max_dotclk = to_i915(connector->dev)->display.cdclk.max_dotclk_freq;
int max_rate, mode_rate, max_lanes, max_link_clock;
int ret;
bool dsc = false, bigjoiner = false;
@@ -1302,8 +1389,13 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
if (*status != MODE_OK)
return 0;
- if (mode->flags & DRM_MODE_FLAG_DBLSCAN) {
- *status = MODE_NO_DBLESCAN;
+ if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
+ *status = MODE_H_ILLEGAL;
+ return 0;
+ }
+
+ if (mode->clock < 10000) {
+ *status = MODE_CLOCK_LOW;
return 0;
}
@@ -1314,10 +1406,6 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
max_link_clock, max_lanes);
mode_rate = intel_dp_link_required(mode->clock, min_bpp);
- ret = drm_modeset_lock(&mgr->base.lock, ctx);
- if (ret)
- return ret;
-
/*
* TODO:
* - Also check if compression would allow for the mode
@@ -1330,27 +1418,18 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
* corresponding link capabilities of the sink) in case the
* stream is uncompressed for it by the last branch device.
*/
- if (mode_rate > max_rate || mode->clock > max_dotclk ||
- drm_dp_calc_pbn_mode(mode->clock, min_bpp << 4) > port->full_pbn) {
- *status = MODE_CLOCK_HIGH;
- return 0;
- }
-
- if (mode->clock < 10000) {
- *status = MODE_CLOCK_LOW;
- return 0;
- }
-
- if (mode->flags & DRM_MODE_FLAG_DBLCLK) {
- *status = MODE_H_ILLEGAL;
- return 0;
- }
-
- if (intel_dp_need_bigjoiner(intel_dp, mode->hdisplay, target_clock)) {
+ if (intel_dp_need_bigjoiner(intel_dp, intel_connector,
+ mode->hdisplay, target_clock)) {
bigjoiner = true;
max_dotclk *= 2;
+ }
+
+ ret = drm_modeset_lock(&mgr->base.lock, ctx);
+ if (ret)
+ return ret;
- /* TODO: add support for bigjoiner */
+ if (mode_rate > max_rate || mode->clock > max_dotclk ||
+ drm_dp_calc_pbn_mode(mode->clock, min_bpp << 4) > port->full_pbn) {
*status = MODE_CLOCK_HIGH;
return 0;
}
@@ -1383,11 +1462,7 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
dsc = dsc_max_compressed_bpp && dsc_slice_count;
}
- /*
- * Big joiner configuration needs DSC for TGL which is not true for
- * XE_LPD where uncompressed joiner is supported.
- */
- if (DISPLAY_VER(dev_priv) < 13 && bigjoiner && !dsc) {
+ if (intel_dp_joiner_needs_dsc(dev_priv, bigjoiner) && !dsc) {
*status = MODE_CLOCK_HIGH;
return 0;
}
@@ -1397,7 +1472,7 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector,
return 0;
}
- *status = intel_mode_valid_max_plane_size(dev_priv, mode, false);
+ *status = intel_mode_valid_max_plane_size(dev_priv, mode, bigjoiner);
return 0;
}
@@ -1509,24 +1584,41 @@ intel_dp_mst_read_decompression_port_dsc_caps(struct intel_dp *intel_dp,
static bool detect_dsc_hblank_expansion_quirk(const struct intel_connector *connector)
{
struct drm_i915_private *i915 = to_i915(connector->base.dev);
+ struct drm_dp_aux *aux = connector->dp.dsc_decompression_aux;
struct drm_dp_desc desc;
u8 dpcd[DP_RECEIVER_CAP_SIZE];
- if (!connector->dp.dsc_decompression_aux)
+ if (!aux)
return false;
- if (drm_dp_read_desc(connector->dp.dsc_decompression_aux,
- &desc, true) < 0)
+ /*
+ * A logical port's OUI (at least for affected sinks) is all 0, so
+ * instead of that the parent port's OUI is used for identification.
+ */
+ if (drm_dp_mst_port_is_logical(connector->port)) {
+ aux = drm_dp_mst_aux_for_parent(connector->port);
+ if (!aux)
+ aux = &connector->mst_port->aux;
+ }
+
+ if (drm_dp_read_dpcd_caps(aux, dpcd) < 0)
return false;
- if (!drm_dp_has_quirk(&desc,
- DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC))
+ if (drm_dp_read_desc(aux, &desc, drm_dp_is_branch(dpcd)) < 0)
return false;
- if (drm_dp_read_dpcd_caps(connector->dp.dsc_decompression_aux, dpcd) < 0)
+ if (!drm_dp_has_quirk(&desc,
+ DP_DPCD_QUIRK_HBLANK_EXPANSION_REQUIRES_DSC))
return false;
- if (!(dpcd[DP_RECEIVE_PORT_0_CAP_0] & DP_HBLANK_EXPANSION_CAPABLE))
+ /*
+ * UHBR (MST sink) devices requiring this quirk don't advertise the
+ * HBLANK expansion support. Presuming that they perform HBLANK
+ * expansion internally, or are affected by this issue on modes with a
+ * short HBLANK for other reasons.
+ */
+ if (!drm_dp_128b132b_supported(dpcd) &&
+ !(dpcd[DP_RECEIVE_PORT_0_CAP_0] & DP_HBLANK_EXPANSION_CAPABLE))
return false;
drm_dbg_kms(&i915->drm,