summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/i915/display/intel_dp.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/i915/display/intel_dp.c')
-rw-r--r--drivers/gpu/drm/i915/display/intel_dp.c263
1 files changed, 237 insertions, 26 deletions
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
index 2eab591a8ef5..0ec82fcbcf48 100644
--- a/drivers/gpu/drm/i915/display/intel_dp.c
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
@@ -51,7 +51,6 @@
#include <drm/drm_probe_helper.h>
#include "g4x_dp.h"
-#include "i915_utils.h"
#include "intel_alpm.h"
#include "intel_atomic.h"
#include "intel_audio.h"
@@ -64,6 +63,8 @@
#include "intel_ddi.h"
#include "intel_de.h"
#include "intel_display_driver.h"
+#include "intel_display_jiffies.h"
+#include "intel_display_utils.h"
#include "intel_display_regs.h"
#include "intel_display_rpm.h"
#include "intel_display_types.h"
@@ -93,14 +94,10 @@
#include "intel_psr.h"
#include "intel_quirks.h"
#include "intel_tc.h"
+#include "intel_vblank.h"
#include "intel_vdsc.h"
#include "intel_vrr.h"
-/* DP DSC throughput values used for slice count calculations KPixels/s */
-#define DP_DSC_PEAK_PIXEL_RATE 2720000
-#define DP_DSC_MAX_ENC_THROUGHPUT_0 340000
-#define DP_DSC_MAX_ENC_THROUGHPUT_1 400000
-
/* Max DSC line buffer depth supported by HW. */
#define INTEL_DP_DSC_MAX_LINE_BUF_DEPTH 13
@@ -1018,13 +1015,43 @@ u8 intel_dp_dsc_get_slice_count(const struct intel_connector *connector,
struct intel_display *display = to_intel_display(connector);
u8 min_slice_count, i;
int max_slice_width;
+ int tp_rgb_yuv444;
+ int tp_yuv422_420;
- if (mode_clock <= DP_DSC_PEAK_PIXEL_RATE)
- min_slice_count = DIV_ROUND_UP(mode_clock,
- DP_DSC_MAX_ENC_THROUGHPUT_0);
- else
- min_slice_count = DIV_ROUND_UP(mode_clock,
- DP_DSC_MAX_ENC_THROUGHPUT_1);
+ /*
+ * TODO: Use the throughput value specific to the actual RGB/YUV
+ * format of the output.
+ * The RGB/YUV444 throughput value should be always either equal
+ * or smaller than the YUV422/420 value, but let's not depend on
+ * this assumption.
+ */
+ if (mode_clock > max(connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444,
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420))
+ return 0;
+
+ if (mode_hdisplay > connector->dp.dsc_branch_caps.max_line_width)
+ return 0;
+
+ /*
+ * TODO: Pass the total pixel rate of all the streams transferred to
+ * an MST tiled display, calculate the total slice count for all tiles
+ * from this and the per-tile slice count from the total slice count.
+ */
+ tp_rgb_yuv444 = drm_dp_dsc_sink_max_slice_throughput(connector->dp.dsc_dpcd,
+ mode_clock, true);
+ tp_yuv422_420 = drm_dp_dsc_sink_max_slice_throughput(connector->dp.dsc_dpcd,
+ mode_clock, false);
+
+ /*
+ * TODO: Use the throughput value specific to the actual RGB/YUV
+ * format of the output.
+ * For now use the smaller of these, which is ok, potentially
+ * resulting in a higher than required minimum slice count.
+ * The RGB/YUV444 throughput value should be always either equal
+ * or smaller than the YUV422/420 value, but let's not depend on
+ * this assumption.
+ */
+ min_slice_count = DIV_ROUND_UP(mode_clock, min(tp_rgb_yuv444, tp_yuv422_420));
/*
* Due to some DSC engine BW limitations, we need to enable second
@@ -2340,24 +2367,26 @@ static int intel_edp_dsc_compute_pipe_bpp(struct intel_dp *intel_dp,
return 0;
}
-static void intel_dp_fec_compute_config(struct intel_dp *intel_dp,
- struct intel_crtc_state *crtc_state)
+/*
+ * Return whether FEC must be enabled for 8b10b SST or MST links. On 128b132b
+ * links FEC is always enabled implicitly by the HW, so this function returns
+ * false for that case.
+ */
+bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state,
+ bool dsc_enabled_on_crtc)
{
- if (crtc_state->fec_enable)
- return;
+ if (intel_dp_is_uhbr(crtc_state))
+ return false;
/*
* Though eDP v1.5 supports FEC with DSC, unlike DP, it is optional.
* Since, FEC is a bandwidth overhead, continue to not enable it for
* eDP. Until, there is a good reason to do so.
*/
- if (intel_dp_is_edp(intel_dp))
- return;
-
- if (intel_dp_is_uhbr(crtc_state))
- return;
+ if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP))
+ return false;
- crtc_state->fec_enable = true;
+ return dsc_enabled_on_crtc || intel_dsc_enabled_on_link(crtc_state);
}
int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
@@ -2375,7 +2404,11 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
bool is_mst = intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST);
int ret;
- intel_dp_fec_compute_config(intel_dp, pipe_config);
+ /*
+ * FIXME: set the FEC enabled state once pipe_config->port_clock is
+ * already known, so the UHBR/non-UHBR mode can be determined.
+ */
+ pipe_config->fec_enable = intel_dp_needs_8b10b_fec(pipe_config, true);
if (!intel_dp_dsc_supports_format(connector, pipe_config->output_format))
return -EINVAL;
@@ -2450,7 +2483,8 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
return ret;
}
- pipe_config->dsc.compression_enable = true;
+ intel_dsc_enable_on_crtc(pipe_config);
+
drm_dbg_kms(display->drm, "DP DSC computed with Input Bpp = %d "
"Compressed Bpp = " FXP_Q4_FMT " Slice Count = %d\n",
pipe_config->pipe_bpp,
@@ -2460,6 +2494,40 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp,
return 0;
}
+static int
+dsc_throughput_quirk_max_bpp_x16(const struct intel_connector *connector,
+ const struct intel_crtc_state *crtc_state)
+{
+ const struct drm_display_mode *adjusted_mode =
+ &crtc_state->hw.adjusted_mode;
+
+ if (!connector->dp.dsc_throughput_quirk)
+ return INT_MAX;
+
+ /*
+ * Synaptics Panamera branch devices have a problem decompressing a
+ * stream with a compressed link-bpp higher than 12, if the pixel
+ * clock is higher than ~50 % of the maximum overall throughput
+ * reported by the branch device. Work around this by limiting the
+ * maximum link bpp for such pixel clocks.
+ *
+ * TODO: Use the throughput value specific to the actual RGB/YUV
+ * format of the output, after determining the pixel clock limit for
+ * YUV modes. For now use the smaller of the throughput values, which
+ * may result in limiting the link-bpp value already at a lower than
+ * required mode clock in case of native YUV422/420 output formats.
+ * The RGB/YUV444 throughput value should be always either equal or
+ * smaller than the YUV422/420 value, but let's not depend on this
+ * assumption.
+ */
+ if (adjusted_mode->crtc_clock <
+ min(connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444,
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420) / 2)
+ return INT_MAX;
+
+ return fxp_q4_from_int(12);
+}
+
/*
* Calculate the output link min, max bpp values in limits based on the pipe bpp
* range, crtc_state and dsc mode. Return true on success.
@@ -2491,6 +2559,7 @@ intel_dp_compute_config_link_bpp_limits(struct intel_dp *intel_dp,
} else {
int dsc_src_min_bpp, dsc_sink_min_bpp, dsc_min_bpp;
int dsc_src_max_bpp, dsc_sink_max_bpp, dsc_max_bpp;
+ int throughput_max_bpp_x16;
dsc_src_min_bpp = intel_dp_dsc_min_src_compressed_bpp();
dsc_sink_min_bpp = intel_dp_dsc_sink_min_compressed_bpp(crtc_state);
@@ -2505,6 +2574,19 @@ intel_dp_compute_config_link_bpp_limits(struct intel_dp *intel_dp,
min(dsc_sink_max_bpp, dsc_src_max_bpp) : dsc_src_max_bpp;
max_link_bpp_x16 = min(max_link_bpp_x16, fxp_q4_from_int(dsc_max_bpp));
+
+ throughput_max_bpp_x16 = dsc_throughput_quirk_max_bpp_x16(connector, crtc_state);
+ throughput_max_bpp_x16 = clamp(throughput_max_bpp_x16,
+ limits->link.min_bpp_x16, max_link_bpp_x16);
+ if (throughput_max_bpp_x16 < max_link_bpp_x16) {
+ max_link_bpp_x16 = throughput_max_bpp_x16;
+
+ drm_dbg_kms(display->drm,
+ "[CRTC:%d:%s][CONNECTOR:%d:%s] Decreasing link max bpp to " FXP_Q4_FMT " due to DSC throughput quirk\n",
+ crtc->base.base.id, crtc->base.name,
+ connector->base.base.id, connector->base.name,
+ FXP_Q4_ARGS(max_link_bpp_x16));
+ }
}
limits->link.max_bpp_x16 = max_link_bpp_x16;
@@ -4169,7 +4251,36 @@ static void intel_dp_read_dsc_dpcd(struct drm_dp_aux *aux,
dsc_dpcd);
}
-void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
+static void init_dsc_overall_throughput_limits(struct intel_connector *connector, bool is_branch)
+{
+ u8 branch_caps[DP_DSC_BRANCH_CAP_SIZE];
+ int line_width;
+
+ connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444 = INT_MAX;
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420 = INT_MAX;
+ connector->dp.dsc_branch_caps.max_line_width = INT_MAX;
+
+ if (!is_branch)
+ return;
+
+ if (drm_dp_dpcd_read_data(connector->dp.dsc_decompression_aux,
+ DP_DSC_BRANCH_OVERALL_THROUGHPUT_0, branch_caps,
+ sizeof(branch_caps)) != 0)
+ return;
+
+ connector->dp.dsc_branch_caps.overall_throughput.rgb_yuv444 =
+ drm_dp_dsc_branch_max_overall_throughput(branch_caps, true) ? : INT_MAX;
+
+ connector->dp.dsc_branch_caps.overall_throughput.yuv422_420 =
+ drm_dp_dsc_branch_max_overall_throughput(branch_caps, false) ? : INT_MAX;
+
+ line_width = drm_dp_dsc_branch_max_line_width(branch_caps);
+ connector->dp.dsc_branch_caps.max_line_width = line_width > 0 ? line_width : INT_MAX;
+}
+
+void intel_dp_get_dsc_sink_cap(u8 dpcd_rev,
+ const struct drm_dp_desc *desc, bool is_branch,
+ struct intel_connector *connector)
{
struct intel_display *display = to_intel_display(connector);
@@ -4182,6 +4293,9 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
/* Clear fec_capable to avoid using stale values */
connector->dp.fec_capability = 0;
+ memset(&connector->dp.dsc_branch_caps, 0, sizeof(connector->dp.dsc_branch_caps));
+ connector->dp.dsc_throughput_quirk = false;
+
if (dpcd_rev < DP_DPCD_REV_14)
return;
@@ -4196,6 +4310,19 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector)
drm_dbg_kms(display->drm, "FEC CAPABILITY: %x\n",
connector->dp.fec_capability);
+
+ if (!(connector->dp.dsc_dpcd[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED))
+ return;
+
+ init_dsc_overall_throughput_limits(connector, is_branch);
+
+ /*
+ * TODO: Move the HW rev check as well to the DRM core quirk table if
+ * that's required after clarifying the list of affected devices.
+ */
+ if (drm_dp_has_quirk(desc, DP_DPCD_QUIRK_DSC_THROUGHPUT_BPP_LIMIT) &&
+ desc->ident.hw_rev == 0x10)
+ connector->dp.dsc_throughput_quirk = true;
}
static void intel_edp_get_dsc_sink_cap(u8 edp_dpcd_rev, struct intel_connector *connector)
@@ -4204,6 +4331,9 @@ static void intel_edp_get_dsc_sink_cap(u8 edp_dpcd_rev, struct intel_connector *
return;
intel_dp_read_dsc_dpcd(connector->dp.dsc_decompression_aux, connector->dp.dsc_dpcd);
+
+ if (connector->dp.dsc_dpcd[0] & DP_DSC_DECOMPRESSION_IS_SUPPORTED)
+ init_dsc_overall_throughput_limits(connector, false);
}
static void
@@ -4220,6 +4350,7 @@ intel_dp_detect_dsc_caps(struct intel_dp *intel_dp, struct intel_connector *conn
connector);
else
intel_dp_get_dsc_sink_cap(intel_dp->dpcd[DP_DPCD_REV],
+ &intel_dp->desc, drm_dp_is_branch(intel_dp->dpcd),
connector);
}
@@ -5553,7 +5684,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp)
if (intel_alpm_get_error(intel_dp)) {
intel_alpm_disable(intel_dp);
- intel_dp->alpm_parameters.sink_alpm_error = true;
+ intel_dp->alpm.sink_alpm_error = true;
}
if (intel_dp_test_short_pulse(intel_dp))
@@ -5921,6 +6052,8 @@ intel_dp_detect(struct drm_connector *_connector,
memset(connector->dp.dsc_dpcd, 0, sizeof(connector->dp.dsc_dpcd));
intel_dp->psr.sink_panel_replay_support = false;
intel_dp->psr.sink_panel_replay_su_support = false;
+ intel_dp->psr.sink_panel_replay_dsc_support =
+ INTEL_DP_PANEL_REPLAY_DSC_NOT_SUPPORTED;
intel_dp_mst_disconnect(intel_dp);
@@ -6857,3 +6990,81 @@ void intel_dp_mst_resume(struct intel_display *display)
}
}
}
+
+static
+int intel_dp_sdp_compute_config_late(struct intel_crtc_state *crtc_state)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int guardband = intel_crtc_vblank_length(crtc_state);
+ int min_sdp_guardband = intel_dp_sdp_min_guardband(crtc_state, false);
+
+ if (guardband < min_sdp_guardband) {
+ drm_dbg_kms(display->drm, "guardband %d < min sdp guardband %d\n",
+ guardband, min_sdp_guardband);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+int intel_dp_compute_config_late(struct intel_encoder *encoder,
+ struct intel_crtc_state *crtc_state,
+ struct drm_connector_state *conn_state)
+{
+ struct intel_dp *intel_dp = enc_to_intel_dp(encoder);
+ int ret;
+
+ intel_psr_compute_config_late(intel_dp, crtc_state);
+
+ ret = intel_dp_sdp_compute_config_late(crtc_state);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static
+int intel_dp_get_lines_for_sdp(const struct intel_crtc_state *crtc_state, u32 type)
+{
+ switch (type) {
+ case DP_SDP_VSC_EXT_VESA:
+ case DP_SDP_VSC_EXT_CEA:
+ return 10;
+ case HDMI_PACKET_TYPE_GAMUT_METADATA:
+ return 8;
+ case DP_SDP_PPS:
+ return 7;
+ case DP_SDP_ADAPTIVE_SYNC:
+ return crtc_state->vrr.vsync_start + 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+int intel_dp_sdp_min_guardband(const struct intel_crtc_state *crtc_state,
+ bool assume_all_enabled)
+{
+ struct intel_display *display = to_intel_display(crtc_state);
+ int sdp_guardband = 0;
+
+ if (assume_all_enabled ||
+ crtc_state->infoframes.enable &
+ intel_hdmi_infoframe_enable(HDMI_PACKET_TYPE_GAMUT_METADATA))
+ sdp_guardband = max(sdp_guardband,
+ intel_dp_get_lines_for_sdp(crtc_state,
+ HDMI_PACKET_TYPE_GAMUT_METADATA));
+
+ if (assume_all_enabled ||
+ crtc_state->dsc.compression_enable)
+ sdp_guardband = max(sdp_guardband,
+ intel_dp_get_lines_for_sdp(crtc_state, DP_SDP_PPS));
+
+ if ((assume_all_enabled && HAS_AS_SDP(display)) ||
+ crtc_state->infoframes.enable & intel_hdmi_infoframe_enable(DP_SDP_ADAPTIVE_SYNC))
+ sdp_guardband = max(sdp_guardband,
+ intel_dp_get_lines_for_sdp(crtc_state, DP_SDP_ADAPTIVE_SYNC));
+
+ return sdp_guardband;
+}