From e5dbaa9f43ea6da88d6cf1dd020308fc448c82ea Mon Sep 17 00:00:00 2001 From: Juha-Pekka Heikkila Date: Fri, 2 Feb 2024 17:06:02 +0200 Subject: [PATCH 01/90] drm/i915/display: On Xe2 always enable decompression with tile4 With Xe2 always treat tile4 as if it was using flat ccs. Signed-off-by: Juha-Pekka Heikkila Reviewed-by: Mika Kahola Signed-off-by: Mika Kahola Link: https://patchwork.freedesktop.org/patch/msgid/20240202150602.430036-2-juhapekka.heikkila@gmail.com --- drivers/gpu/drm/i915/display/skl_universal_plane.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/gpu/drm/i915/display/skl_universal_plane.c b/drivers/gpu/drm/i915/display/skl_universal_plane.c index 392d93e97bf8..e941e2e4fd14 100644 --- a/drivers/gpu/drm/i915/display/skl_universal_plane.c +++ b/drivers/gpu/drm/i915/display/skl_universal_plane.c @@ -948,6 +948,11 @@ static u32 skl_plane_ctl(const struct intel_crtc_state *crtc_state, if (DISPLAY_VER(dev_priv) == 13) plane_ctl |= adlp_plane_ctl_arb_slots(plane_state); + if (GRAPHICS_VER(dev_priv) >= 20 && + fb->modifier == I915_FORMAT_MOD_4_TILED) { + plane_ctl |= PLANE_CTL_RENDER_DECOMPRESSION_ENABLE; + } + return plane_ctl; } From 6074be620c31dc2ae11af96a1a5ea95580976fb5 Mon Sep 17 00:00:00 2001 From: Manasi Navare Date: Mon, 5 Feb 2024 20:46:19 +0000 Subject: [PATCH 02/90] drm/i915/dsc: Fix the macro that calculates DSCC_/DSCA_ PPS reg address Commit bd077259d0a9 ("drm/i915/vdsc: Add function to read any PPS register") defines a new macro to calculate the DSC PPS register addresses with PPS number as an input. This macro correctly calculates the addresses till PPS 11 since the addresses increment by 4. So in that case the following macro works correctly to give correct register address: _MMIO(_DSCA_PPS_0 + (pps) * 4) However after PPS 11, the register address for PPS 12 increments by 12 because of RC Buffer memory allocation in between. Because of this discontinuity in the address space, the macro calculates wrong addresses for PPS 12 - 16 resulting into incorrect DSC PPS parameter value read/writes causing DSC corruption. This fixes it by correcting this macro to add the offset of 12 for PPS >=12. v3: Add correct paranthesis for pps argument (Jani Nikula) Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/10172 Fixes: bd077259d0a9 ("drm/i915/vdsc: Add function to read any PPS register") Cc: Suraj Kandpal Cc: Ankit Nautiyal Cc: Animesh Manna Cc: Jani Nikula Cc: Sean Paul Cc: Drew Davenport Signed-off-by: Manasi Navare Reviewed-by: Jani Nikula Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/20240205204619.1991673-1-navaremanasi@chromium.org --- drivers/gpu/drm/i915/display/intel_vdsc_regs.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_vdsc_regs.h b/drivers/gpu/drm/i915/display/intel_vdsc_regs.h index 64f440fdc22b..8b21dc8e26d5 100644 --- a/drivers/gpu/drm/i915/display/intel_vdsc_regs.h +++ b/drivers/gpu/drm/i915/display/intel_vdsc_regs.h @@ -51,8 +51,8 @@ #define DSCC_PICTURE_PARAMETER_SET_0 _MMIO(0x6BA00) #define _DSCA_PPS_0 0x6B200 #define _DSCC_PPS_0 0x6BA00 -#define DSCA_PPS(pps) _MMIO(_DSCA_PPS_0 + (pps) * 4) -#define DSCC_PPS(pps) _MMIO(_DSCC_PPS_0 + (pps) * 4) +#define DSCA_PPS(pps) _MMIO(_DSCA_PPS_0 + ((pps) < 12 ? (pps) : (pps) + 12) * 4) +#define DSCC_PPS(pps) _MMIO(_DSCC_PPS_0 + ((pps) < 12 ? (pps) : (pps) + 12) * 4) #define _ICL_DSC0_PICTURE_PARAMETER_SET_0_PB 0x78270 #define _ICL_DSC1_PICTURE_PARAMETER_SET_0_PB 0x78370 #define _ICL_DSC0_PICTURE_PARAMETER_SET_0_PC 0x78470 From 6061811d72e14f41f71b6a025510920b187bfcca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:45:52 +0200 Subject: [PATCH 03/90] drm/i915/dp: Limit SST link rate to <=8.1Gbps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Limit the link rate to HBR3 or below (<=8.1Gbps) in SST mode. UHBR (10Gbps+) link rates require 128b/132b channel encoding which we have not yet hooked up into the SST/no-sideband codepaths. Cc: stable@vger.kernel.org Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208154552.14545-1-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index ab415f41924d..5045c34a16be 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2356,6 +2356,9 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, limits->min_rate = intel_dp_common_rate(intel_dp, 0); limits->max_rate = intel_dp_max_link_rate(intel_dp); + /* FIXME 128b/132b SST support missing */ + limits->max_rate = min(limits->max_rate, 810000); + limits->min_lane_count = 1; limits->max_lane_count = intel_dp_max_lane_count(intel_dp); From c9950a5d5056b11dd04b7ab3ef8e259be0c6268a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:08 +0200 Subject: [PATCH 04/90] drm/i915: Correct for_each_old_global_obj_in_state() arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for_each_old_global_obj_in_state() gives us the old state, not the new state. Correct the name of the macro argument. Note that while the argument was misnamed the macro did work correctly regardless. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-2-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_global_state.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_global_state.h b/drivers/gpu/drm/i915/display/intel_global_state.h index 5c8545d7a76a..6506a8e32972 100644 --- a/drivers/gpu/drm/i915/display/intel_global_state.h +++ b/drivers/gpu/drm/i915/display/intel_global_state.h @@ -37,11 +37,11 @@ struct intel_global_obj { (__i)++) \ for_each_if(obj) -#define for_each_old_global_obj_in_state(__state, obj, new_obj_state, __i) \ +#define for_each_old_global_obj_in_state(__state, obj, old_obj_state, __i) \ for ((__i) = 0; \ (__i) < (__state)->num_global_objs && \ ((obj) = (__state)->global_objs[__i].ptr, \ - (new_obj_state) = (__state)->global_objs[__i].old_state, 1); \ + (old_obj_state) = (__state)->global_objs[__i].old_state, 1); \ (__i)++) \ for_each_if(obj) From dff8f3f85a1bb199af7d963c97983bdee23d7dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:09 +0200 Subject: [PATCH 05/90] drm/i915/sdvo: Convert to per-device debugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use drm_dbg_kms() instead of DRM_DEBUG_KMS() in the sdvo code to get the device name into the debug prints. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-3-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_sdvo.c | 113 +++++++++++++--------- 1 file changed, 66 insertions(+), 47 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index a086d5f51e61..a882bed5b0ad 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -252,6 +252,7 @@ static void intel_sdvo_write_sdvox(struct intel_sdvo *intel_sdvo, u32 val) static bool intel_sdvo_read_byte(struct intel_sdvo *intel_sdvo, u8 addr, u8 *ch) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); struct i2c_msg msgs[] = { { .addr = intel_sdvo->slave_addr, @@ -271,7 +272,7 @@ static bool intel_sdvo_read_byte(struct intel_sdvo *intel_sdvo, u8 addr, u8 *ch) if ((ret = i2c_transfer(intel_sdvo->i2c, msgs, 2)) == 2) return true; - DRM_DEBUG_KMS("i2c transfer returned %d\n", ret); + drm_dbg_kms(&i915->drm, "i2c transfer returned %d\n", ret); return false; } @@ -437,7 +438,8 @@ static void intel_sdvo_debug_write(struct intel_sdvo *intel_sdvo, u8 cmd, drm_WARN_ON(&dev_priv->drm, pos >= sizeof(buffer) - 1); #undef BUF_PRINT - DRM_DEBUG_KMS("%s: W: %02X %s\n", SDVO_NAME(intel_sdvo), cmd, buffer); + drm_dbg_kms(&dev_priv->drm, "%s: W: %02X %s\n", SDVO_NAME(intel_sdvo), + cmd, buffer); } static const char * const cmd_status_names[] = { @@ -462,6 +464,7 @@ static bool __intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd, const void *args, int args_len, bool unlocked) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); u8 *buf, status; struct i2c_msg *msgs; int i, ret = true; @@ -511,13 +514,13 @@ static bool __intel_sdvo_write_cmd(struct intel_sdvo *intel_sdvo, u8 cmd, else ret = __i2c_transfer(intel_sdvo->i2c, msgs, i+3); if (ret < 0) { - DRM_DEBUG_KMS("I2c transfer returned %d\n", ret); + drm_dbg_kms(&i915->drm, "I2c transfer returned %d\n", ret); ret = false; goto out; } if (ret != i+3) { /* failure in I2C transfer */ - DRM_DEBUG_KMS("I2c transfer returned %d/%d\n", ret, i+3); + drm_dbg_kms(&i915->drm, "I2c transfer returned %d/%d\n", ret, i+3); ret = false; } @@ -604,12 +607,13 @@ static bool intel_sdvo_read_response(struct intel_sdvo *intel_sdvo, drm_WARN_ON(&dev_priv->drm, pos >= sizeof(buffer) - 1); #undef BUF_PRINT - DRM_DEBUG_KMS("%s: R: %s\n", SDVO_NAME(intel_sdvo), buffer); + drm_dbg_kms(&dev_priv->drm, "%s: R: %s\n", + SDVO_NAME(intel_sdvo), buffer); return true; log_fail: - DRM_DEBUG_KMS("%s: R: ... failed %s\n", - SDVO_NAME(intel_sdvo), buffer); + drm_dbg_kms(&dev_priv->drm, "%s: R: ... failed %s\n", + SDVO_NAME(intel_sdvo), buffer); return false; } @@ -1004,6 +1008,7 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo, unsigned int if_index, u8 tx_rate, const u8 *data, unsigned int length) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); u8 set_buf_index[2] = { if_index, 0 }; u8 hbuf_size, tmp[8]; int i; @@ -1016,8 +1021,9 @@ static bool intel_sdvo_write_infoframe(struct intel_sdvo *intel_sdvo, if (!intel_sdvo_get_hbuf_size(intel_sdvo, &hbuf_size)) return false; - DRM_DEBUG_KMS("writing sdvo hbuf: %i, length %u, hbuf_size: %i\n", - if_index, length, hbuf_size); + drm_dbg_kms(&i915->drm, + "writing sdvo hbuf: %i, length %u, hbuf_size: %i\n", + if_index, length, hbuf_size); if (hbuf_size < length) return false; @@ -1042,6 +1048,7 @@ static ssize_t intel_sdvo_read_infoframe(struct intel_sdvo *intel_sdvo, unsigned int if_index, u8 *data, unsigned int length) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); u8 set_buf_index[2] = { if_index, 0 }; u8 hbuf_size, tx_rate, av_split; int i; @@ -1071,8 +1078,9 @@ static ssize_t intel_sdvo_read_infoframe(struct intel_sdvo *intel_sdvo, if (!intel_sdvo_get_hbuf_size(intel_sdvo, &hbuf_size)) return false; - DRM_DEBUG_KMS("reading sdvo hbuf: %i, length %u, hbuf_size: %i\n", - if_index, length, hbuf_size); + drm_dbg_kms(&i915->drm, + "reading sdvo hbuf: %i, length %u, hbuf_size: %i\n", + if_index, length, hbuf_size); hbuf_size = min_t(unsigned int, length, hbuf_size); @@ -1151,6 +1159,7 @@ static bool intel_sdvo_set_avi_infoframe(struct intel_sdvo *intel_sdvo, static void intel_sdvo_get_avi_infoframe(struct intel_sdvo *intel_sdvo, struct intel_crtc_state *crtc_state) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); u8 sdvo_data[HDMI_INFOFRAME_SIZE(AVI)]; union hdmi_infoframe *frame = &crtc_state->infoframes.avi; ssize_t len; @@ -1162,7 +1171,7 @@ static void intel_sdvo_get_avi_infoframe(struct intel_sdvo *intel_sdvo, len = intel_sdvo_read_infoframe(intel_sdvo, SDVO_HBUF_INDEX_AVI_IF, sdvo_data, sizeof(sdvo_data)); if (len < 0) { - DRM_DEBUG_KMS("failed to read AVI infoframe\n"); + drm_dbg_kms(&i915->drm, "failed to read AVI infoframe\n"); return; } else if (len == 0) { return; @@ -1173,13 +1182,14 @@ static void intel_sdvo_get_avi_infoframe(struct intel_sdvo *intel_sdvo, ret = hdmi_infoframe_unpack(frame, sdvo_data, len); if (ret) { - DRM_DEBUG_KMS("Failed to unpack AVI infoframe\n"); + drm_dbg_kms(&i915->drm, "Failed to unpack AVI infoframe\n"); return; } if (frame->any.type != HDMI_INFOFRAME_TYPE_AVI) - DRM_DEBUG_KMS("Found the wrong infoframe type 0x%x (expected 0x%02x)\n", - frame->any.type, HDMI_INFOFRAME_TYPE_AVI); + drm_dbg_kms(&i915->drm, + "Found the wrong infoframe type 0x%x (expected 0x%02x)\n", + frame->any.type, HDMI_INFOFRAME_TYPE_AVI); } static void intel_sdvo_get_eld(struct intel_sdvo *intel_sdvo, @@ -1348,6 +1358,7 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { + struct drm_i915_private *i915 = to_i915(encoder->base.dev); struct intel_sdvo *intel_sdvo = to_sdvo(encoder); struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(conn_state->connector); @@ -1360,7 +1371,7 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder, return -EINVAL; } - DRM_DEBUG_KMS("forcing bpc to 8 for SDVO\n"); + drm_dbg_kms(&i915->drm, "forcing bpc to 8 for SDVO\n"); /* FIXME: Don't increase pipe_bpp */ pipe_config->pipe_bpp = 8*3; pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB; @@ -1439,7 +1450,7 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder, if (!intel_sdvo_compute_avi_infoframe(intel_sdvo, pipe_config, conn_state)) { - DRM_DEBUG_KMS("bad AVI infoframe\n"); + drm_dbg_kms(&i915->drm, "bad AVI infoframe\n"); return -EINVAL; } @@ -1916,8 +1927,8 @@ static void intel_enable_sdvo(struct intel_atomic_state *state, */ if (success && !input1) { drm_dbg_kms(&dev_priv->drm, - "First %s output reported failure to " - "sync\n", SDVO_NAME(intel_sdvo)); + "First %s output reported failure to sync\n", + SDVO_NAME(intel_sdvo)); } if (0) @@ -1976,13 +1987,14 @@ intel_sdvo_mode_valid(struct drm_connector *connector, static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct intel_sdvo_caps *caps) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); BUILD_BUG_ON(sizeof(*caps) != 8); if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_DEVICE_CAPS, caps, sizeof(*caps))) return false; - DRM_DEBUG_KMS("SDVO capabilities:\n" + drm_dbg_kms(&i915->drm, "SDVO capabilities:\n" " vendor_id: %d\n" " device_id: %d\n" " device_rev_id: %d\n" @@ -2121,8 +2133,9 @@ intel_sdvo_connector_matches_edid(struct intel_sdvo_connector *sdvo, bool monitor_is_digital = drm_edid_is_digital(drm_edid); bool connector_is_digital = !!IS_DIGITAL(sdvo); - DRM_DEBUG_KMS("connector_is_digital? %d, monitor_is_digital? %d\n", - connector_is_digital, monitor_is_digital); + drm_dbg_kms(sdvo->base.base.dev, + "connector_is_digital? %d, monitor_is_digital? %d\n", + connector_is_digital, monitor_is_digital); return connector_is_digital == monitor_is_digital; } @@ -2135,8 +2148,8 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) enum drm_connector_status ret; u16 response; - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, connector->name); + drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); if (!intel_display_device_enabled(i915)) return connector_status_disconnected; @@ -2153,9 +2166,9 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) &response, 2)) return connector_status_unknown; - DRM_DEBUG_KMS("SDVO response %d %d [%x]\n", - response & 0xff, response >> 8, - intel_sdvo_connector->output_flag); + drm_dbg_kms(&i915->drm, "SDVO response %d %d [%x]\n", + response & 0xff, response >> 8, + intel_sdvo_connector->output_flag); if (response == 0) return connector_status_disconnected; @@ -2192,8 +2205,8 @@ static int intel_sdvo_get_ddc_modes(struct drm_connector *connector) int num_modes = 0; const struct drm_edid *drm_edid; - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, connector->name); + drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); /* set the bus switch and get the modes */ drm_edid = intel_sdvo_get_edid(connector); @@ -2287,6 +2300,7 @@ static const struct drm_display_mode sdvo_tv_modes[] = { static int intel_sdvo_get_tv_modes(struct drm_connector *connector) { struct intel_sdvo *intel_sdvo = intel_attached_sdvo(to_intel_connector(connector)); + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); struct intel_sdvo_connector *intel_sdvo_connector = to_intel_sdvo_connector(connector); const struct drm_connector_state *conn_state = connector->state; @@ -2295,8 +2309,8 @@ static int intel_sdvo_get_tv_modes(struct drm_connector *connector) int num_modes = 0; int i; - DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", - connector->base.id, connector->name); + drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s]\n", + connector->base.id, connector->name); /* * Read the list of supported input resolutions for the selected TV @@ -2783,10 +2797,11 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, u16 type) struct drm_encoder *encoder = &intel_sdvo->base.base; struct drm_connector *connector; struct intel_encoder *intel_encoder = to_intel_encoder(encoder); + struct drm_i915_private *i915 = to_i915(intel_encoder->base.dev); struct intel_connector *intel_connector; struct intel_sdvo_connector *intel_sdvo_connector; - DRM_DEBUG_KMS("initialising DVI type 0x%x\n", type); + drm_dbg_kms(&i915->drm, "initialising DVI type 0x%x\n", type); intel_sdvo_connector = intel_sdvo_connector_alloc(); if (!intel_sdvo_connector) @@ -2832,12 +2847,13 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, u16 type) static bool intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, u16 type) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); struct drm_encoder *encoder = &intel_sdvo->base.base; struct drm_connector *connector; struct intel_connector *intel_connector; struct intel_sdvo_connector *intel_sdvo_connector; - DRM_DEBUG_KMS("initialising TV type 0x%x\n", type); + drm_dbg_kms(&i915->drm, "initialising TV type 0x%x\n", type); intel_sdvo_connector = intel_sdvo_connector_alloc(); if (!intel_sdvo_connector) @@ -2871,12 +2887,13 @@ intel_sdvo_tv_init(struct intel_sdvo *intel_sdvo, u16 type) static bool intel_sdvo_analog_init(struct intel_sdvo *intel_sdvo, u16 type) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); struct drm_encoder *encoder = &intel_sdvo->base.base; struct drm_connector *connector; struct intel_connector *intel_connector; struct intel_sdvo_connector *intel_sdvo_connector; - DRM_DEBUG_KMS("initialising analog type 0x%x\n", type); + drm_dbg_kms(&i915->drm, "initialising analog type 0x%x\n", type); intel_sdvo_connector = intel_sdvo_connector_alloc(); if (!intel_sdvo_connector) @@ -2908,7 +2925,7 @@ intel_sdvo_lvds_init(struct intel_sdvo *intel_sdvo, u16 type) struct intel_connector *intel_connector; struct intel_sdvo_connector *intel_sdvo_connector; - DRM_DEBUG_KMS("initialising LVDS type 0x%x\n", type); + drm_dbg_kms(&i915->drm, "initialising LVDS type 0x%x\n", type); intel_sdvo_connector = intel_sdvo_connector_alloc(); if (!intel_sdvo_connector) @@ -2992,6 +3009,7 @@ static bool intel_sdvo_output_init(struct intel_sdvo *sdvo, u16 type) static bool intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); static const u16 probe_order[] = { SDVO_OUTPUT_TMDS0, SDVO_OUTPUT_TMDS1, @@ -3010,8 +3028,9 @@ intel_sdvo_output_setup(struct intel_sdvo *intel_sdvo) flags = intel_sdvo_filter_output_flags(intel_sdvo->caps.output_flags); if (flags == 0) { - DRM_DEBUG_KMS("%s: Unknown SDVO output type (0x%04x)\n", - SDVO_NAME(intel_sdvo), intel_sdvo->caps.output_flags); + drm_dbg_kms(&i915->drm, + "%s: Unknown SDVO output type (0x%04x)\n", + SDVO_NAME(intel_sdvo), intel_sdvo->caps.output_flags); return false; } @@ -3100,8 +3119,8 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo, state_assignment = response; \ drm_object_attach_property(&connector->base, \ intel_sdvo_connector->name, 0); \ - DRM_DEBUG_KMS(#name ": max %d, default %d, current %d\n", \ - data_value[0], data_value[1], response); \ + drm_dbg_kms(dev, #name ": max %d, default %d, current %d\n", \ + data_value[0], data_value[1], response); \ } \ } while (0) @@ -3112,6 +3131,7 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, struct intel_sdvo_connector *intel_sdvo_connector, struct intel_sdvo_enhancements_reply enhancements) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); struct drm_device *dev = intel_sdvo->base.base.dev; struct drm_connector *connector = &intel_sdvo_connector->base.base; struct drm_connector_state *conn_state = connector->state; @@ -3149,9 +3169,8 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, drm_object_attach_property(&connector->base, intel_sdvo_connector->right, 0); - DRM_DEBUG_KMS("h_overscan: max %d, " - "default %d, current %d\n", - data_value[0], data_value[1], response); + drm_dbg_kms(&i915->drm, "h_overscan: max %d, default %d, current %d\n", + data_value[0], data_value[1], response); } if (enhancements.overscan_v) { @@ -3185,9 +3204,8 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, drm_object_attach_property(&connector->base, intel_sdvo_connector->bottom, 0); - DRM_DEBUG_KMS("v_overscan: max %d, " - "default %d, current %d\n", - data_value[0], data_value[1], response); + drm_dbg_kms(&i915->drm, "v_overscan: max %d, default %d, current %d\n", + data_value[0], data_value[1], response); } ENHANCEMENT(&sdvo_state->tv, hpos, HPOS); @@ -3215,7 +3233,7 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, drm_object_attach_property(&connector->base, intel_sdvo_connector->dot_crawl, 0); - DRM_DEBUG_KMS("dot crawl: current %d\n", response); + drm_dbg_kms(&i915->drm, "dot crawl: current %d\n", response); } return true; @@ -3240,6 +3258,7 @@ intel_sdvo_create_enhance_property_lvds(struct intel_sdvo *intel_sdvo, static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo, struct intel_sdvo_connector *intel_sdvo_connector) { + struct drm_i915_private *i915 = to_i915(intel_sdvo->base.base.dev); union { struct intel_sdvo_enhancements_reply reply; u16 response; @@ -3251,7 +3270,7 @@ static bool intel_sdvo_create_enhance_property(struct intel_sdvo *intel_sdvo, SDVO_CMD_GET_SUPPORTED_ENHANCEMENTS, &enhancements, sizeof(enhancements)) || enhancements.response == 0) { - DRM_DEBUG_KMS("No enhancement is supported\n"); + drm_dbg_kms(&i915->drm, "No enhancement is supported\n"); return true; } From ca93f9f3a8d65a99c0f286efa503335456fdecbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:10 +0200 Subject: [PATCH 06/90] drm/i915/sdvo: Fix up code alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Realign a bunch of code that has become messy. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-4-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_sdvo.c | 104 +++++++++++----------- 1 file changed, 52 insertions(+), 52 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index a882bed5b0ad..66dab0a37e65 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -762,7 +762,7 @@ static bool intel_sdvo_get_timing(struct intel_sdvo *intel_sdvo, u8 cmd, } static bool intel_sdvo_set_input_timing(struct intel_sdvo *intel_sdvo, - struct intel_sdvo_dtd *dtd) + struct intel_sdvo_dtd *dtd) { return intel_sdvo_set_timing(intel_sdvo, SDVO_CMD_SET_INPUT_TIMINGS_PART1, dtd); @@ -930,8 +930,8 @@ static bool intel_sdvo_check_supp_encode(struct intel_sdvo *intel_sdvo) BUILD_BUG_ON(sizeof(encode) != 2); return intel_sdvo_get_value(intel_sdvo, - SDVO_CMD_GET_SUPP_ENCODE, - &encode, sizeof(encode)); + SDVO_CMD_GET_SUPP_ENCODE, + &encode, sizeof(encode)); } static bool intel_sdvo_set_encode(struct intel_sdvo *intel_sdvo, @@ -1995,30 +1995,30 @@ static bool intel_sdvo_get_capabilities(struct intel_sdvo *intel_sdvo, struct in return false; drm_dbg_kms(&i915->drm, "SDVO capabilities:\n" - " vendor_id: %d\n" - " device_id: %d\n" - " device_rev_id: %d\n" - " sdvo_version_major: %d\n" - " sdvo_version_minor: %d\n" - " sdvo_num_inputs: %d\n" - " smooth_scaling: %d\n" - " sharp_scaling: %d\n" - " up_scaling: %d\n" - " down_scaling: %d\n" - " stall_support: %d\n" - " output_flags: %d\n", - caps->vendor_id, - caps->device_id, - caps->device_rev_id, - caps->sdvo_version_major, - caps->sdvo_version_minor, - caps->sdvo_num_inputs, - caps->smooth_scaling, - caps->sharp_scaling, - caps->up_scaling, - caps->down_scaling, - caps->stall_support, - caps->output_flags); + " vendor_id: %d\n" + " device_id: %d\n" + " device_rev_id: %d\n" + " sdvo_version_major: %d\n" + " sdvo_version_minor: %d\n" + " sdvo_num_inputs: %d\n" + " smooth_scaling: %d\n" + " sharp_scaling: %d\n" + " up_scaling: %d\n" + " down_scaling: %d\n" + " stall_support: %d\n" + " output_flags: %d\n", + caps->vendor_id, + caps->device_id, + caps->device_rev_id, + caps->sdvo_version_major, + caps->sdvo_version_minor, + caps->sdvo_num_inputs, + caps->smooth_scaling, + caps->sharp_scaling, + caps->up_scaling, + caps->down_scaling, + caps->stall_support, + caps->output_flags); return true; } @@ -2050,7 +2050,7 @@ static u16 intel_sdvo_get_hotplug_support(struct intel_sdvo *intel_sdvo) return 0; if (!intel_sdvo_get_value(intel_sdvo, SDVO_CMD_GET_HOT_PLUG_SUPPORT, - &hotplug, sizeof(hotplug))) + &hotplug, sizeof(hotplug))) return 0; return hotplug; @@ -2812,7 +2812,7 @@ intel_sdvo_dvi_init(struct intel_sdvo *intel_sdvo, u16 type) intel_connector = &intel_sdvo_connector->base; connector = &intel_connector->base; if (intel_sdvo_get_hotplug_support(intel_sdvo) & - intel_sdvo_connector->output_flag) { + intel_sdvo_connector->output_flag) { intel_sdvo->hotplug_active |= intel_sdvo_connector->output_flag; /* * Some SDVO devices have one-shot hotplug interrupts. @@ -3092,8 +3092,8 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo, intel_sdvo_connector->tv_format = - drm_property_create(dev, DRM_MODE_PROP_ENUM, - "mode", intel_sdvo_connector->format_supported_num); + drm_property_create(dev, DRM_MODE_PROP_ENUM, + "mode", intel_sdvo_connector->format_supported_num); if (!intel_sdvo_connector->tv_format) return false; @@ -3168,7 +3168,7 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, return false; drm_object_attach_property(&connector->base, - intel_sdvo_connector->right, 0); + intel_sdvo_connector->right, 0); drm_dbg_kms(&i915->drm, "h_overscan: max %d, default %d, current %d\n", data_value[0], data_value[1], response); } @@ -3189,7 +3189,7 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, intel_sdvo_connector->max_vscan = data_value[0]; intel_sdvo_connector->top = drm_property_create_range(dev, 0, - "top_margin", 0, data_value[0]); + "top_margin", 0, data_value[0]); if (!intel_sdvo_connector->top) return false; @@ -3198,12 +3198,12 @@ intel_sdvo_create_enhance_property_tv(struct intel_sdvo *intel_sdvo, intel_sdvo_connector->bottom = drm_property_create_range(dev, 0, - "bottom_margin", 0, data_value[0]); + "bottom_margin", 0, data_value[0]); if (!intel_sdvo_connector->bottom) return false; drm_object_attach_property(&connector->base, - intel_sdvo_connector->bottom, 0); + intel_sdvo_connector->bottom, 0); drm_dbg_kms(&i915->drm, "v_overscan: max %d, default %d, current %d\n", data_value[0], data_value[1], response); } @@ -3491,23 +3491,23 @@ bool intel_sdvo_init(struct drm_i915_private *dev_priv, goto err_output; drm_dbg_kms(&dev_priv->drm, "%s device VID/DID: %02X:%02X.%02X, " - "clock range %dMHz - %dMHz, " - "num inputs: %d, " - "output 1: %c, output 2: %c\n", - SDVO_NAME(intel_sdvo), - intel_sdvo->caps.vendor_id, intel_sdvo->caps.device_id, - intel_sdvo->caps.device_rev_id, - intel_sdvo->pixel_clock_min / 1000, - intel_sdvo->pixel_clock_max / 1000, - intel_sdvo->caps.sdvo_num_inputs, - /* check currently supported outputs */ - intel_sdvo->caps.output_flags & - (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0 | - SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_SVID0 | - SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB0) ? 'Y' : 'N', - intel_sdvo->caps.output_flags & - (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1 | - SDVO_OUTPUT_LVDS1) ? 'Y' : 'N'); + "clock range %dMHz - %dMHz, " + "num inputs: %d, " + "output 1: %c, output 2: %c\n", + SDVO_NAME(intel_sdvo), + intel_sdvo->caps.vendor_id, intel_sdvo->caps.device_id, + intel_sdvo->caps.device_rev_id, + intel_sdvo->pixel_clock_min / 1000, + intel_sdvo->pixel_clock_max / 1000, + intel_sdvo->caps.sdvo_num_inputs, + /* check currently supported outputs */ + intel_sdvo->caps.output_flags & + (SDVO_OUTPUT_TMDS0 | SDVO_OUTPUT_RGB0 | + SDVO_OUTPUT_LVDS0 | SDVO_OUTPUT_SVID0 | + SDVO_OUTPUT_CVBS0 | SDVO_OUTPUT_YPRPB0) ? 'Y' : 'N', + intel_sdvo->caps.output_flags & + (SDVO_OUTPUT_TMDS1 | SDVO_OUTPUT_RGB1 | + SDVO_OUTPUT_LVDS1) ? 'Y' : 'N'); return true; err_output: From 0128e89a9163e64fb71c2a5513d55fdb6d8df817 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:11 +0200 Subject: [PATCH 07/90] drm/i915/color: Use per-device debugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to drm_dbg_kms() in the LUT validation code so we see which device generated the debugs. Need to plumb i915 a bit deeper to make that happen. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-5-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_color.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index c5092b7e87d5..ca7112b32cb3 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -2111,7 +2111,8 @@ static u32 intel_degamma_lut_size(const struct intel_crtc_state *crtc_state) return DISPLAY_INFO(i915)->color.degamma_lut_size; } -static int check_lut_size(const struct drm_property_blob *lut, int expected) +static int check_lut_size(struct drm_i915_private *i915, + const struct drm_property_blob *lut, int expected) { int len; @@ -2120,8 +2121,8 @@ static int check_lut_size(const struct drm_property_blob *lut, int expected) len = drm_color_lut_size(lut); if (len != expected) { - DRM_DEBUG_KMS("Invalid LUT size; got %d, expected %d\n", - len, expected); + drm_dbg_kms(&i915->drm, "Invalid LUT size; got %d, expected %d\n", + len, expected); return -EINVAL; } @@ -2146,8 +2147,8 @@ static int _check_luts(const struct intel_crtc_state *crtc_state, degamma_length = intel_degamma_lut_size(crtc_state); gamma_length = intel_gamma_lut_size(crtc_state); - if (check_lut_size(degamma_lut, degamma_length) || - check_lut_size(gamma_lut, gamma_length)) + if (check_lut_size(i915, degamma_lut, degamma_length) || + check_lut_size(i915, gamma_lut, gamma_length)) return -EINVAL; if (drm_color_lut_check(degamma_lut, degamma_tests) || From 5f67258c0872f1f0e0b1712b466b87bbe1b0c2aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:12 +0200 Subject: [PATCH 08/90] drm/i915/fb: Use per-device debugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to drm_dbg_kms() in the fb code so we see which device generated the debugs. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-6-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_fb.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_fb.c b/drivers/gpu/drm/i915/display/intel_fb.c index 0c0144eaa8fa..3ea6470d6d92 100644 --- a/drivers/gpu/drm/i915/display/intel_fb.c +++ b/drivers/gpu/drm/i915/display/intel_fb.c @@ -1849,9 +1849,10 @@ static int intel_plane_check_stride(const struct intel_plane_state *plane_state) fb->modifier, rotation); if (stride > max_stride) { - DRM_DEBUG_KMS("[FB:%d] stride (%d) exceeds [PLANE:%d:%s] max stride (%d)\n", - fb->base.id, stride, - plane->base.base.id, plane->base.name, max_stride); + drm_dbg_kms(plane->base.dev, + "[FB:%d] stride (%d) exceeds [PLANE:%d:%s] max stride (%d)\n", + fb->base.id, stride, + plane->base.base.id, plane->base.name, max_stride); return -EINVAL; } From abe6af1627c73e7086bb90d750cf5c72e9fde9c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:13 +0200 Subject: [PATCH 09/90] drm/i915/bios: Switch to kms debugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VBT code is all about displays, so switch to UT_KMS debugs from UT_DRIVER. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-7-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_bios.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 5f04e495fd27..63655b17714c 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -2911,17 +2911,17 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size) return false; if (sizeof(struct vbt_header) > size) { - DRM_DEBUG_DRIVER("VBT header incomplete\n"); + DRM_DEBUG_KMS("VBT header incomplete\n"); return false; } if (memcmp(vbt->signature, "$VBT", 4)) { - DRM_DEBUG_DRIVER("VBT invalid signature\n"); + DRM_DEBUG_KMS("VBT invalid signature\n"); return false; } if (vbt->vbt_size > size) { - DRM_DEBUG_DRIVER("VBT incomplete (vbt_size overflows)\n"); + DRM_DEBUG_KMS("VBT incomplete (vbt_size overflows)\n"); return false; } @@ -2931,13 +2931,13 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size) vbt->bdb_offset, sizeof(struct bdb_header), size)) { - DRM_DEBUG_DRIVER("BDB header incomplete\n"); + DRM_DEBUG_KMS("BDB header incomplete\n"); return false; } bdb = get_bdb_header(vbt); if (range_overflows_t(size_t, vbt->bdb_offset, bdb->bdb_size, size)) { - DRM_DEBUG_DRIVER("BDB incomplete\n"); + DRM_DEBUG_KMS("BDB incomplete\n"); return false; } From ff9bc20cd21c23a8fca0d5039e23142898383d97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:14 +0200 Subject: [PATCH 10/90] drm/i915/bios: Use per-device debugs for VBT related stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to drm_dbg_kms() in the VBT code so we see which device generated the debugs. Need to plumb i915 a bit deeper to make that happen. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-8-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_bios.c | 73 ++++++++++--------- drivers/gpu/drm/i915/display/intel_bios.h | 3 +- drivers/gpu/drm/i915/display/intel_opregion.c | 6 +- 3 files changed, 45 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 63655b17714c..fe52c06271ef 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -1759,7 +1759,8 @@ parse_mipi_config(struct drm_i915_private *i915, /* Find the sequence block and size for the given panel. */ static const u8 * -find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, +find_panel_sequence_block(struct drm_i915_private *i915, + const struct bdb_mipi_sequence *sequence, u16 panel_id, u32 *seq_size) { u32 total = get_blocksize(sequence); @@ -1776,7 +1777,7 @@ find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, for (i = 0; i < MAX_MIPI_CONFIGURATIONS && index < total; i++) { if (index + header_size > total) { - DRM_ERROR("Invalid sequence block (header)\n"); + drm_err(&i915->drm, "Invalid sequence block (header)\n"); return NULL; } @@ -1789,7 +1790,7 @@ find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, index += header_size; if (index + current_size > total) { - DRM_ERROR("Invalid sequence block\n"); + drm_err(&i915->drm, "Invalid sequence block\n"); return NULL; } @@ -1801,12 +1802,13 @@ find_panel_sequence_block(const struct bdb_mipi_sequence *sequence, index += current_size; } - DRM_ERROR("Sequence block detected but no valid configuration\n"); + drm_err(&i915->drm, "Sequence block detected but no valid configuration\n"); return NULL; } -static int goto_next_sequence(const u8 *data, int index, int total) +static int goto_next_sequence(struct drm_i915_private *i915, + const u8 *data, int index, int total) { u16 len; @@ -1836,7 +1838,7 @@ static int goto_next_sequence(const u8 *data, int index, int total) len = *(data + index + 6) + 7; break; default: - DRM_ERROR("Unknown operation byte\n"); + drm_err(&i915->drm, "Unknown operation byte\n"); return 0; } } @@ -1844,7 +1846,8 @@ static int goto_next_sequence(const u8 *data, int index, int total) return 0; } -static int goto_next_sequence_v3(const u8 *data, int index, int total) +static int goto_next_sequence_v3(struct drm_i915_private *i915, + const u8 *data, int index, int total) { int seq_end; u16 len; @@ -1855,7 +1858,7 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) * checking on the structure. */ if (total < 5) { - DRM_ERROR("Too small sequence size\n"); + drm_err(&i915->drm, "Too small sequence size\n"); return 0; } @@ -1872,7 +1875,7 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) seq_end = index + size_of_sequence; if (seq_end > total) { - DRM_ERROR("Invalid sequence size\n"); + drm_err(&i915->drm, "Invalid sequence size\n"); return 0; } @@ -1882,7 +1885,7 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) if (operation_byte == MIPI_SEQ_ELEM_END) { if (index != seq_end) { - DRM_ERROR("Invalid element structure\n"); + drm_err(&i915->drm, "Invalid element structure\n"); return 0; } return index; @@ -1904,8 +1907,8 @@ static int goto_next_sequence_v3(const u8 *data, int index, int total) case MIPI_SEQ_ELEM_PMIC: break; default: - DRM_ERROR("Unknown operation byte %u\n", - operation_byte); + drm_err(&i915->drm, "Unknown operation byte %u\n", + operation_byte); break; } } @@ -2030,7 +2033,7 @@ parse_mipi_sequence(struct drm_i915_private *i915, drm_dbg(&i915->drm, "Found MIPI sequence block v%u\n", sequence->version); - seq_data = find_panel_sequence_block(sequence, panel_type, &seq_size); + seq_data = find_panel_sequence_block(i915, sequence, panel_type, &seq_size); if (!seq_data) return; @@ -2058,9 +2061,9 @@ parse_mipi_sequence(struct drm_i915_private *i915, panel->vbt.dsi.sequence[seq_id] = data + index; if (sequence->version >= 3) - index = goto_next_sequence_v3(data, index, seq_size); + index = goto_next_sequence_v3(i915, data, index, seq_size); else - index = goto_next_sequence(data, index, seq_size); + index = goto_next_sequence(i915, data, index, seq_size); if (!index) { drm_err(&i915->drm, "Invalid sequence %u\n", seq_id); @@ -2135,12 +2138,13 @@ parse_compression_parameters(struct drm_i915_private *i915) } } -static u8 translate_iboost(u8 val) +static u8 translate_iboost(struct drm_i915_private *i915, u8 val) { static const u8 mapping[] = { 1, 3, 7 }; /* See VBT spec */ if (val >= ARRAY_SIZE(mapping)) { - DRM_DEBUG_KMS("Unsupported I_boost value found in VBT (%d), display may not work properly\n", val); + drm_dbg_kms(&i915->drm, + "Unsupported I_boost value found in VBT (%d), display may not work properly\n", val); return 0; } return mapping[val]; @@ -2897,12 +2901,14 @@ static const struct bdb_header *get_bdb_header(const struct vbt_header *vbt) /** * intel_bios_is_valid_vbt - does the given buffer contain a valid VBT + * @i915: the device * @buf: pointer to a buffer to validate * @size: size of the buffer * * Returns true on valid VBT. */ -bool intel_bios_is_valid_vbt(const void *buf, size_t size) +bool intel_bios_is_valid_vbt(struct drm_i915_private *i915, + const void *buf, size_t size) { const struct vbt_header *vbt = buf; const struct bdb_header *bdb; @@ -2911,17 +2917,17 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size) return false; if (sizeof(struct vbt_header) > size) { - DRM_DEBUG_KMS("VBT header incomplete\n"); + drm_dbg_kms(&i915->drm, "VBT header incomplete\n"); return false; } if (memcmp(vbt->signature, "$VBT", 4)) { - DRM_DEBUG_KMS("VBT invalid signature\n"); + drm_dbg_kms(&i915->drm, "VBT invalid signature\n"); return false; } if (vbt->vbt_size > size) { - DRM_DEBUG_KMS("VBT incomplete (vbt_size overflows)\n"); + drm_dbg_kms(&i915->drm, "VBT incomplete (vbt_size overflows)\n"); return false; } @@ -2931,13 +2937,13 @@ bool intel_bios_is_valid_vbt(const void *buf, size_t size) vbt->bdb_offset, sizeof(struct bdb_header), size)) { - DRM_DEBUG_KMS("BDB header incomplete\n"); + drm_dbg_kms(&i915->drm, "BDB header incomplete\n"); return false; } bdb = get_bdb_header(vbt); if (range_overflows_t(size_t, vbt->bdb_offset, bdb->bdb_size, size)) { - DRM_DEBUG_KMS("BDB incomplete\n"); + drm_dbg_kms(&i915->drm, "BDB incomplete\n"); return false; } @@ -2989,7 +2995,7 @@ static struct vbt_header *spi_oprom_get_vbt(struct drm_i915_private *i915) for (count = 0; count < vbt_size; count += 4) *(vbt + store++) = intel_spi_read(&i915->uncore, found + count); - if (!intel_bios_is_valid_vbt(vbt, vbt_size)) + if (!intel_bios_is_valid_vbt(i915, vbt, vbt_size)) goto err_free_vbt; drm_dbg_kms(&i915->drm, "Found valid VBT in SPI flash\n"); @@ -3046,7 +3052,7 @@ static struct vbt_header *oprom_get_vbt(struct drm_i915_private *i915) memcpy_fromio(vbt, p, vbt_size); - if (!intel_bios_is_valid_vbt(vbt, vbt_size)) + if (!intel_bios_is_valid_vbt(i915, vbt, vbt_size)) goto err_free_vbt; pci_unmap_rom(pdev, oprom); @@ -3398,6 +3404,7 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, struct dsc_compression_parameters_entry *dsc, int dsc_max_bpc) { + struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); struct drm_dsc_config *vdsc_cfg = &crtc_state->dsc.config; int bpc = 8; @@ -3411,8 +3418,8 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, else if (dsc->support_8bpc && dsc_max_bpc >= 8) bpc = 8; else - DRM_DEBUG_KMS("VBT: Unsupported BPC %d for DCS\n", - dsc_max_bpc); + drm_dbg_kms(&i915->drm, "VBT: Unsupported BPC %d for DCS\n", + dsc_max_bpc); crtc_state->pipe_bpp = bpc * 3; @@ -3432,16 +3439,16 @@ static void fill_dsc(struct intel_crtc_state *crtc_state, } else { /* FIXME */ if (!(dsc->slices_per_line & BIT(0))) - DRM_DEBUG_KMS("VBT: Unsupported DSC slice count for DSI\n"); + drm_dbg_kms(&i915->drm, "VBT: Unsupported DSC slice count for DSI\n"); crtc_state->dsc.slice_count = 1; } if (crtc_state->hw.adjusted_mode.crtc_hdisplay % crtc_state->dsc.slice_count != 0) - DRM_DEBUG_KMS("VBT: DSC hdisplay %d not divisible by slice count %d\n", - crtc_state->hw.adjusted_mode.crtc_hdisplay, - crtc_state->dsc.slice_count); + drm_dbg_kms(&i915->drm, "VBT: DSC hdisplay %d not divisible by slice count %d\n", + crtc_state->hw.adjusted_mode.crtc_hdisplay, + crtc_state->dsc.slice_count); /* * The VBT rc_buffer_block_size and rc_buffer_size definitions @@ -3597,7 +3604,7 @@ int intel_bios_dp_boost_level(const struct intel_bios_encoder_data *devdata) if (!devdata || devdata->i915->display.vbt.version < 196 || !devdata->child.iboost) return 0; - return translate_iboost(devdata->child.dp_iboost_level); + return translate_iboost(devdata->i915, devdata->child.dp_iboost_level); } int intel_bios_hdmi_boost_level(const struct intel_bios_encoder_data *devdata) @@ -3605,7 +3612,7 @@ int intel_bios_hdmi_boost_level(const struct intel_bios_encoder_data *devdata) if (!devdata || devdata->i915->display.vbt.version < 196 || !devdata->child.iboost) return 0; - return translate_iboost(devdata->child.hdmi_iboost_level); + return translate_iboost(devdata->i915, devdata->child.hdmi_iboost_level); } int intel_bios_hdmi_ddc_pin(const struct intel_bios_encoder_data *devdata) diff --git a/drivers/gpu/drm/i915/display/intel_bios.h b/drivers/gpu/drm/i915/display/intel_bios.h index 41bfb009d4b0..06a51be4afd8 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.h +++ b/drivers/gpu/drm/i915/display/intel_bios.h @@ -242,7 +242,8 @@ void intel_bios_init_panel_late(struct drm_i915_private *dev_priv, const struct drm_edid *drm_edid); void intel_bios_fini_panel(struct intel_panel *panel); void intel_bios_driver_remove(struct drm_i915_private *dev_priv); -bool intel_bios_is_valid_vbt(const void *buf, size_t size); +bool intel_bios_is_valid_vbt(struct drm_i915_private *i915, + const void *buf, size_t size); bool intel_bios_is_tv_present(struct drm_i915_private *dev_priv); bool intel_bios_is_lvds_present(struct drm_i915_private *dev_priv, u8 *i2c_pin); bool intel_bios_is_port_present(struct drm_i915_private *dev_priv, enum port port); diff --git a/drivers/gpu/drm/i915/display/intel_opregion.c b/drivers/gpu/drm/i915/display/intel_opregion.c index f242bb320610..fcbb083318a7 100644 --- a/drivers/gpu/drm/i915/display/intel_opregion.c +++ b/drivers/gpu/drm/i915/display/intel_opregion.c @@ -887,7 +887,7 @@ static int intel_load_vbt_firmware(struct drm_i915_private *dev_priv) return ret; } - if (intel_bios_is_valid_vbt(fw->data, fw->size)) { + if (intel_bios_is_valid_vbt(dev_priv, fw->data, fw->size)) { opregion->vbt_firmware = kmemdup(fw->data, fw->size, GFP_KERNEL); if (opregion->vbt_firmware) { drm_dbg_kms(&dev_priv->drm, @@ -1034,7 +1034,7 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv) vbt = opregion->rvda; vbt_size = opregion->asle->rvds; - if (intel_bios_is_valid_vbt(vbt, vbt_size)) { + if (intel_bios_is_valid_vbt(dev_priv, vbt, vbt_size)) { drm_dbg_kms(&dev_priv->drm, "Found valid VBT in ACPI OpRegion (RVDA)\n"); opregion->vbt = vbt; @@ -1059,7 +1059,7 @@ int intel_opregion_setup(struct drm_i915_private *dev_priv) vbt_size = (mboxes & MBOX_ASLE_EXT) ? OPREGION_ASLE_EXT_OFFSET : OPREGION_SIZE; vbt_size -= OPREGION_VBT_OFFSET; - if (intel_bios_is_valid_vbt(vbt, vbt_size)) { + if (intel_bios_is_valid_vbt(dev_priv, vbt, vbt_size)) { drm_dbg_kms(&dev_priv->drm, "Found valid VBT in ACPI OpRegion (Mailbox #4)\n"); opregion->vbt = vbt; From 8c9e4f68b861dfe75650b75ac9e57e68f5f1850a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:15 +0200 Subject: [PATCH 11/90] drm/i915/hdcp: Use per-device debugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to per-device debugs in the hdcp code so we see at least which device is involved. Should proably also print the connector/encoder/etc. in there, but left that for the future. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-9-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 3a595cd433d4..9dff4bdfeec8 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -36,8 +36,10 @@ static u32 transcoder_to_stream_enc_status(enum transcoder cpu_transcoder) } } -static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int timeout) +static void intel_dp_hdcp_wait_for_cp_irq(struct intel_connector *connector, + int timeout) { + struct intel_hdcp *hdcp = &connector->hdcp; long ret; #define C (hdcp->cp_irq_count_cached != atomic_read(&hdcp->cp_irq_count)) @@ -45,7 +47,8 @@ static void intel_dp_hdcp_wait_for_cp_irq(struct intel_hdcp *hdcp, int timeout) msecs_to_jiffies(timeout)); if (!ret) - DRM_DEBUG_KMS("Timedout at waiting for CP_IRQ\n"); + drm_dbg_kms(connector->base.dev, + "Timedout at waiting for CP_IRQ\n"); } static @@ -387,7 +390,8 @@ int hdcp2_detect_msg_availability(struct intel_connector *connector, *msg_ready = true; break; default: - DRM_ERROR("Unidentified msg_id: %d\n", msg_id); + drm_err(connector->base.dev, + "Unidentified msg_id: %d\n", msg_id); return -EINVAL; } @@ -421,7 +425,7 @@ intel_dp_hdcp2_wait_for_msg(struct intel_connector *connector, * As we want to check the msg availability at timeout, Ignoring * the timeout at wait for CP_IRQ. */ - intel_dp_hdcp_wait_for_cp_irq(hdcp, timeout); + intel_dp_hdcp_wait_for_cp_irq(connector, timeout); ret = hdcp2_detect_msg_availability(connector, msg_id, &msg_ready); if (!msg_ready) From 29d7a5b4d224d2f1cba4b21bf5db1a22c8568bee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:16 +0200 Subject: [PATCH 12/90] drm/i915/wm: Pass the whole i915 to intel_get_cxsr_latency() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just pass the whole i915 to intel_get_cxsr_latency() instead of having each caller dig out bits and pieces. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-10-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/i9xx_wm.c | 27 ++++++++++---------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/drivers/gpu/drm/i915/display/i9xx_wm.c b/drivers/gpu/drm/i915/display/i9xx_wm.c index 11ca9572e8b3..e6275ab14320 100644 --- a/drivers/gpu/drm/i915/display/i9xx_wm.c +++ b/drivers/gpu/drm/i915/display/i9xx_wm.c @@ -70,22 +70,21 @@ static const struct cxsr_latency cxsr_latency_table[] = { {0, 1, 400, 800, 6042, 36042, 6584, 36584}, /* DDR3-800 SC */ }; -static const struct cxsr_latency *intel_get_cxsr_latency(bool is_desktop, - bool is_ddr3, - int fsb, - int mem) +static const struct cxsr_latency *intel_get_cxsr_latency(struct drm_i915_private *i915) { - const struct cxsr_latency *latency; int i; - if (fsb == 0 || mem == 0) + if (i915->fsb_freq == 0 || i915->mem_freq == 0) return NULL; for (i = 0; i < ARRAY_SIZE(cxsr_latency_table); i++) { - latency = &cxsr_latency_table[i]; + const struct cxsr_latency *latency = &cxsr_latency_table[i]; + bool is_desktop = !IS_MOBILE(i915); + if (is_desktop == latency->is_desktop && - is_ddr3 == latency->is_ddr3 && - fsb == latency->fsb_freq && mem == latency->mem_freq) + i915->is_ddr3 == latency->is_ddr3 && + i915->fsb_freq == latency->fsb_freq && + i915->mem_freq == latency->mem_freq) return latency; } @@ -634,10 +633,7 @@ static void pnv_update_wm(struct drm_i915_private *dev_priv) u32 reg; unsigned int wm; - latency = intel_get_cxsr_latency(!IS_MOBILE(dev_priv), - dev_priv->is_ddr3, - dev_priv->fsb_freq, - dev_priv->mem_freq); + latency = intel_get_cxsr_latency(dev_priv); if (!latency) { drm_dbg_kms(&dev_priv->drm, "Unknown FSB/MEM found, disable CxSR\n"); @@ -4016,10 +4012,7 @@ void i9xx_wm_init(struct drm_i915_private *dev_priv) g4x_setup_wm_latency(dev_priv); dev_priv->display.funcs.wm = &g4x_wm_funcs; } else if (IS_PINEVIEW(dev_priv)) { - if (!intel_get_cxsr_latency(!IS_MOBILE(dev_priv), - dev_priv->is_ddr3, - dev_priv->fsb_freq, - dev_priv->mem_freq)) { + if (!intel_get_cxsr_latency(dev_priv)) { drm_info(&dev_priv->drm, "failed to find known CxSR latency " "(found ddr%s fsb freq %d, mem freq %d), " From 167712d82aadb317a5e912eac5abae00a9c78994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:17 +0200 Subject: [PATCH 13/90] drm/i915/wm: Use per-device debugs in pre-ilk wm code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to drm_dbg_kms() in the pre-ilk wm code so we see which device generated the debugs. Need to plumb i915 a bit deeper to make that happen. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-11-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/i9xx_wm.c | 28 ++++++++++++++++---------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/i9xx_wm.c b/drivers/gpu/drm/i915/display/i9xx_wm.c index e6275ab14320..46877fd260d5 100644 --- a/drivers/gpu/drm/i915/display/i9xx_wm.c +++ b/drivers/gpu/drm/i915/display/i9xx_wm.c @@ -88,7 +88,7 @@ static const struct cxsr_latency *intel_get_cxsr_latency(struct drm_i915_private return latency; } - DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n"); + drm_dbg_kms(&i915->drm, "Unknown FSB/MEM found, disable CxSR\n"); return NULL; } @@ -524,6 +524,7 @@ static unsigned int intel_wm_method2(unsigned int pixel_rate, /** * intel_calculate_wm - calculate watermark level + * @i915: the device * @pixel_rate: pixel clock * @wm: chip FIFO params * @fifo_size: size of the FIFO buffer @@ -541,7 +542,8 @@ static unsigned int intel_wm_method2(unsigned int pixel_rate, * past the watermark point. If the FIFO drains completely, a FIFO underrun * will occur, and a display engine hang could result. */ -static unsigned int intel_calculate_wm(int pixel_rate, +static unsigned int intel_calculate_wm(struct drm_i915_private *i915, + int pixel_rate, const struct intel_watermark_params *wm, int fifo_size, int cpp, unsigned int latency_ns) @@ -558,10 +560,10 @@ static unsigned int intel_calculate_wm(int pixel_rate, latency_ns / 100); entries = DIV_ROUND_UP(entries, wm->cacheline_size) + wm->guard_size; - DRM_DEBUG_KMS("FIFO entries required for mode: %d\n", entries); + drm_dbg_kms(&i915->drm, "FIFO entries required for mode: %d\n", entries); wm_size = fifo_size - entries; - DRM_DEBUG_KMS("FIFO watermark level: %d\n", wm_size); + drm_dbg_kms(&i915->drm, "FIFO watermark level: %d\n", wm_size); /* Don't promote wm_size to unsigned... */ if (wm_size > wm->max_wm) @@ -649,7 +651,8 @@ static void pnv_update_wm(struct drm_i915_private *dev_priv) int cpp = fb->format->cpp[0]; /* Display SR */ - wm = intel_calculate_wm(pixel_rate, &pnv_display_wm, + wm = intel_calculate_wm(dev_priv, pixel_rate, + &pnv_display_wm, pnv_display_wm.fifo_size, cpp, latency->display_sr); reg = intel_uncore_read(&dev_priv->uncore, DSPFW1); @@ -659,20 +662,23 @@ static void pnv_update_wm(struct drm_i915_private *dev_priv) drm_dbg_kms(&dev_priv->drm, "DSPFW1 register is %x\n", reg); /* cursor SR */ - wm = intel_calculate_wm(pixel_rate, &pnv_cursor_wm, + wm = intel_calculate_wm(dev_priv, pixel_rate, + &pnv_cursor_wm, pnv_display_wm.fifo_size, 4, latency->cursor_sr); intel_uncore_rmw(&dev_priv->uncore, DSPFW3, DSPFW_CURSOR_SR_MASK, FW_WM(wm, CURSOR_SR)); /* Display HPLL off SR */ - wm = intel_calculate_wm(pixel_rate, &pnv_display_hplloff_wm, + wm = intel_calculate_wm(dev_priv, pixel_rate, + &pnv_display_hplloff_wm, pnv_display_hplloff_wm.fifo_size, cpp, latency->display_hpll_disable); intel_uncore_rmw(&dev_priv->uncore, DSPFW3, DSPFW_HPLL_SR_MASK, FW_WM(wm, HPLL_SR)); /* cursor HPLL off SR */ - wm = intel_calculate_wm(pixel_rate, &pnv_cursor_hplloff_wm, + wm = intel_calculate_wm(dev_priv, pixel_rate, + &pnv_cursor_hplloff_wm, pnv_display_hplloff_wm.fifo_size, 4, latency->cursor_hpll_disable); reg = intel_uncore_read(&dev_priv->uncore, DSPFW3); @@ -2120,7 +2126,7 @@ static void i9xx_update_wm(struct drm_i915_private *dev_priv) else cpp = fb->format->cpp[0]; - planea_wm = intel_calculate_wm(crtc->config->pixel_rate, + planea_wm = intel_calculate_wm(dev_priv, crtc->config->pixel_rate, wm_info, fifo_size, cpp, pessimal_latency_ns); } else { @@ -2147,7 +2153,7 @@ static void i9xx_update_wm(struct drm_i915_private *dev_priv) else cpp = fb->format->cpp[0]; - planeb_wm = intel_calculate_wm(crtc->config->pixel_rate, + planeb_wm = intel_calculate_wm(dev_priv, crtc->config->pixel_rate, wm_info, fifo_size, cpp, pessimal_latency_ns); } else { @@ -2241,7 +2247,7 @@ static void i845_update_wm(struct drm_i915_private *dev_priv) if (crtc == NULL) return; - planea_wm = intel_calculate_wm(crtc->config->pixel_rate, + planea_wm = intel_calculate_wm(dev_priv, crtc->config->pixel_rate, &i845_wm_info, i845_get_fifo_size(dev_priv, PLANE_A), 4, pessimal_latency_ns); From 1db5efe51f9427b212e608d2cf072c9d9d780440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:18 +0200 Subject: [PATCH 14/90] drm/i915/wm: Use per-device debugs ilk wm code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Switch to drm_dbg_kms() in the ilk wm code so we see which device generated the debugs. Need to plumb i915 a bit deeper to make that happen. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-12-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/i9xx_wm.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/gpu/drm/i915/display/i9xx_wm.c b/drivers/gpu/drm/i915/display/i9xx_wm.c index 46877fd260d5..628e7192ebc9 100644 --- a/drivers/gpu/drm/i915/display/i9xx_wm.c +++ b/drivers/gpu/drm/i915/display/i9xx_wm.c @@ -2533,7 +2533,8 @@ static void ilk_compute_wm_reg_maximums(const struct drm_i915_private *dev_priv, max->fbc = ilk_fbc_wm_reg_max(dev_priv); } -static bool ilk_validate_wm_level(int level, +static bool ilk_validate_wm_level(struct drm_i915_private *i915, + int level, const struct ilk_wm_maximums *max, struct intel_wm_level *result) { @@ -2556,14 +2557,17 @@ static bool ilk_validate_wm_level(int level, */ if (level == 0 && !result->enable) { if (result->pri_val > max->pri) - DRM_DEBUG_KMS("Primary WM%d too large %u (max %u)\n", - level, result->pri_val, max->pri); + drm_dbg_kms(&i915->drm, + "Primary WM%d too large %u (max %u)\n", + level, result->pri_val, max->pri); if (result->spr_val > max->spr) - DRM_DEBUG_KMS("Sprite WM%d too large %u (max %u)\n", - level, result->spr_val, max->spr); + drm_dbg_kms(&i915->drm, + "Sprite WM%d too large %u (max %u)\n", + level, result->spr_val, max->spr); if (result->cur_val > max->cur) - DRM_DEBUG_KMS("Cursor WM%d too large %u (max %u)\n", - level, result->cur_val, max->cur); + drm_dbg_kms(&i915->drm, + "Cursor WM%d too large %u (max %u)\n", + level, result->cur_val, max->cur); result->pri_val = min_t(u32, result->pri_val, max->pri); result->spr_val = min_t(u32, result->spr_val, max->spr); @@ -2763,7 +2767,7 @@ static void ilk_setup_wm_latency(struct drm_i915_private *dev_priv) } } -static bool ilk_validate_pipe_wm(const struct drm_i915_private *dev_priv, +static bool ilk_validate_pipe_wm(struct drm_i915_private *dev_priv, struct intel_pipe_wm *pipe_wm) { /* LP0 watermark maximums depend on this pipe alone */ @@ -2778,7 +2782,7 @@ static bool ilk_validate_pipe_wm(const struct drm_i915_private *dev_priv, ilk_compute_wm_maximums(dev_priv, 0, &config, INTEL_DDB_PART_1_2, &max); /* At least LP0 must be valid */ - if (!ilk_validate_wm_level(0, &max, &pipe_wm->wm[0])) { + if (!ilk_validate_wm_level(dev_priv, 0, &max, &pipe_wm->wm[0])) { drm_dbg_kms(&dev_priv->drm, "LP0 watermark invalid\n"); return false; } @@ -2847,7 +2851,7 @@ static int ilk_compute_pipe_wm(struct intel_atomic_state *state, * register maximums since such watermarks are * always invalid. */ - if (!ilk_validate_wm_level(level, &max, wm)) { + if (!ilk_validate_wm_level(dev_priv, level, &max, wm)) { memset(wm, 0, sizeof(*wm)); break; } @@ -2978,7 +2982,7 @@ static void ilk_wm_merge(struct drm_i915_private *dev_priv, if (level > last_enabled_level) wm->enable = false; - else if (!ilk_validate_wm_level(level, max, wm)) + else if (!ilk_validate_wm_level(dev_priv, level, max, wm)) /* make sure all following levels get disabled */ last_enabled_level = level - 1; From 8dc0e7dfc5aaf63b8d615fe968e6db36de5b2dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:19 +0200 Subject: [PATCH 15/90] drm/i915/dvo/ns2501: Nuke pointless casts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dvo 'dev_priv' is void* so no need for an explicit cast. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-13-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/dvo_ns2501.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/dvo_ns2501.c b/drivers/gpu/drm/i915/display/dvo_ns2501.c index a724a8755673..32fd4aa27598 100644 --- a/drivers/gpu/drm/i915/display/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/display/dvo_ns2501.c @@ -551,7 +551,7 @@ static void ns2501_mode_set(struct intel_dvo_device *dvo, const struct drm_display_mode *adjusted_mode) { const struct ns2501_configuration *conf; - struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + struct ns2501_priv *ns = dvo->dev_priv; int mode_idx, i; DRM_DEBUG_KMS @@ -655,7 +655,7 @@ static bool ns2501_get_hw_state(struct intel_dvo_device *dvo) /* set the NS2501 power state */ static void ns2501_dpms(struct intel_dvo_device *dvo, bool enable) { - struct ns2501_priv *ns = (struct ns2501_priv *)(dvo->dev_priv); + struct ns2501_priv *ns = dvo->dev_priv; DRM_DEBUG_KMS("Trying set the dpms of the DVO to %i\n", enable); From cd1f0d3df26ad5abbf15b3d1f26a5a6417091b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Thu, 8 Feb 2024 17:17:20 +0200 Subject: [PATCH 16/90] drm/i915/dvo: Use sizeof(*variable) instead of sizeof(type) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer sizeof(*variable) to sizeof(type) to make it a bit harder to screw things up. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240208151720.7866-14-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/dvo_ch7017.c | 2 +- drivers/gpu/drm/i915/display/dvo_ch7xxx.c | 2 +- drivers/gpu/drm/i915/display/dvo_ivch.c | 2 +- drivers/gpu/drm/i915/display/dvo_ns2501.c | 2 +- drivers/gpu/drm/i915/display/dvo_sil164.c | 2 +- drivers/gpu/drm/i915/display/dvo_tfp410.c | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/dvo_ch7017.c b/drivers/gpu/drm/i915/display/dvo_ch7017.c index 0589994dde11..d0c3880d7f80 100644 --- a/drivers/gpu/drm/i915/display/dvo_ch7017.c +++ b/drivers/gpu/drm/i915/display/dvo_ch7017.c @@ -205,7 +205,7 @@ static bool ch7017_init(struct intel_dvo_device *dvo, const char *str; u8 val; - priv = kzalloc(sizeof(struct ch7017_priv), GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) return false; diff --git a/drivers/gpu/drm/i915/display/dvo_ch7xxx.c b/drivers/gpu/drm/i915/display/dvo_ch7xxx.c index 6d948520e9a6..2e8e85da5a40 100644 --- a/drivers/gpu/drm/i915/display/dvo_ch7xxx.c +++ b/drivers/gpu/drm/i915/display/dvo_ch7xxx.c @@ -216,7 +216,7 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo, u8 vendor, device; char *name, *devid; - ch7xxx = kzalloc(sizeof(struct ch7xxx_priv), GFP_KERNEL); + ch7xxx = kzalloc(sizeof(*ch7xxx), GFP_KERNEL); if (ch7xxx == NULL) return false; diff --git a/drivers/gpu/drm/i915/display/dvo_ivch.c b/drivers/gpu/drm/i915/display/dvo_ivch.c index f43d8c610d3f..eef72bb3b767 100644 --- a/drivers/gpu/drm/i915/display/dvo_ivch.c +++ b/drivers/gpu/drm/i915/display/dvo_ivch.c @@ -267,7 +267,7 @@ static bool ivch_init(struct intel_dvo_device *dvo, u16 temp; int i; - priv = kzalloc(sizeof(struct ivch_priv), GFP_KERNEL); + priv = kzalloc(sizeof(*priv), GFP_KERNEL); if (priv == NULL) return false; diff --git a/drivers/gpu/drm/i915/display/dvo_ns2501.c b/drivers/gpu/drm/i915/display/dvo_ns2501.c index 32fd4aa27598..1df212fb000e 100644 --- a/drivers/gpu/drm/i915/display/dvo_ns2501.c +++ b/drivers/gpu/drm/i915/display/dvo_ns2501.c @@ -476,7 +476,7 @@ static bool ns2501_init(struct intel_dvo_device *dvo, struct ns2501_priv *ns; unsigned char ch; - ns = kzalloc(sizeof(struct ns2501_priv), GFP_KERNEL); + ns = kzalloc(sizeof(*ns), GFP_KERNEL); if (ns == NULL) return false; diff --git a/drivers/gpu/drm/i915/display/dvo_sil164.c b/drivers/gpu/drm/i915/display/dvo_sil164.c index 4acc8ce29c0b..6c461024c8e3 100644 --- a/drivers/gpu/drm/i915/display/dvo_sil164.c +++ b/drivers/gpu/drm/i915/display/dvo_sil164.c @@ -141,7 +141,7 @@ static bool sil164_init(struct intel_dvo_device *dvo, struct sil164_priv *sil; unsigned char ch; - sil = kzalloc(sizeof(struct sil164_priv), GFP_KERNEL); + sil = kzalloc(sizeof(*sil), GFP_KERNEL); if (sil == NULL) return false; diff --git a/drivers/gpu/drm/i915/display/dvo_tfp410.c b/drivers/gpu/drm/i915/display/dvo_tfp410.c index 009d65b0f3e9..0939e097f4f9 100644 --- a/drivers/gpu/drm/i915/display/dvo_tfp410.c +++ b/drivers/gpu/drm/i915/display/dvo_tfp410.c @@ -173,7 +173,7 @@ static bool tfp410_init(struct intel_dvo_device *dvo, struct tfp410_priv *tfp; int id; - tfp = kzalloc(sizeof(struct tfp410_priv), GFP_KERNEL); + tfp = kzalloc(sizeof(*tfp), GFP_KERNEL); if (tfp == NULL) return false; From 00c3959d63ae52e98b8e6776c9dba65291f8d137 Mon Sep 17 00:00:00 2001 From: Stanislav Lisovskiy Date: Mon, 12 Feb 2024 18:20:11 +0530 Subject: [PATCH 17/90] drm/i915: Add bigjoiner force enable option to debugfs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For validation purposes, it might be useful to be able to force Bigjoiner mode, even if current dotclock/resolution do not require that. Lets add such to option to debugfs. v2: - Apparently intel_dp_need_bigjoiner can't be used, when debugfs entry is created so lets just check manually the DISPLAY_VER. v3: - Switch to intel_connector from drm_connector(Jani Nikula) - Remove redundant modeset lock(Jani Nikula) - Use kstrtobool_from_user for boolean value(Jani Nikula) v4: - Apply the changes to proper function(Jani Nikula) v5: - Removed unnecessary check from i915_bigjoiner_enable_show (Ville Syrjälä) - Added eDP connector check to intel_connector_debugfs_add (Ville Syrjälä) - Removed debug message in order to prevent dmesg flooding (Ville Syrjälä) v6: - Assume now always that m->private is intel_connector - Fixed other similar conflicts v7: - Move bigjoiner force option to intel_connector(Ville Syrjälä) - Use DEFINE_SHOW_STORE_ATTRIBUTE instead of defining fops manually.(Ville Syrjälä) v8: - Pass intel_connector to debugfs_create_file, instead of drm_connector. (Jani Nikula) Signed-off-by: Stanislav Lisovskiy Acked-by: Jani Nikula Signed-off-by: Stanislav Lisovskiy Link: https://patchwork.freedesktop.org/patch/msgid/20240212125011.66174-1-uma.shankar@intel.com --- .../drm/i915/display/intel_display_debugfs.c | 47 +++++++++++++++++++ .../drm/i915/display/intel_display_types.h | 2 + drivers/gpu/drm/i915/display/intel_dp.c | 4 +- 3 files changed, 52 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 6f2d13c8ccf7..a962b48bcf13 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -1391,6 +1391,20 @@ out: drm_modeset_unlock(&i915->drm.mode_config.connection_mutex); return ret; } +static int i915_bigjoiner_enable_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = m->private; + struct drm_crtc *crtc; + + crtc = connector->base.state->crtc; + if (connector->base.status != connector_status_connected || !crtc) + return -ENODEV; + + seq_printf(m, "Bigjoiner enable: %d\n", connector->force_bigjoiner_enable); + + return 0; +} + static ssize_t i915_dsc_output_format_write(struct file *file, const char __user *ubuf, size_t len, loff_t *offp) @@ -1412,6 +1426,30 @@ static ssize_t i915_dsc_output_format_write(struct file *file, return len; } +static ssize_t i915_bigjoiner_enable_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 drm_crtc *crtc; + bool bigjoiner_en = 0; + int ret; + + crtc = connector->base.state->crtc; + if (connector->base.status != connector_status_connected || !crtc) + return -ENODEV; + + ret = kstrtobool_from_user(ubuf, len, &bigjoiner_en); + if (ret < 0) + return ret; + + connector->force_bigjoiner_enable = bigjoiner_en; + *offp += len; + + return len; +} + static int i915_dsc_output_format_open(struct inode *inode, struct file *file) { @@ -1505,6 +1543,8 @@ static const struct file_operations i915_dsc_fractional_bpp_fops = { .write = i915_dsc_fractional_bpp_write }; +DEFINE_SHOW_STORE_ATTRIBUTE(i915_bigjoiner_enable); + /* * Returns the Current CRTC's bpc. * Example usage: cat /sys/kernel/debug/dri/0/crtc-0/i915_current_bpc @@ -1586,6 +1626,13 @@ void intel_connector_debugfs_add(struct intel_connector *connector) connector, &i915_dsc_fractional_bpp_fops); } + if (DISPLAY_VER(i915) >= 11 && + (connector_type == DRM_MODE_CONNECTOR_DisplayPort || + connector_type == DRM_MODE_CONNECTOR_eDP)) { + debugfs_create_file("i915_bigjoiner_force_enable", 0644, root, + connector, &i915_bigjoiner_enable_fops); + } + if (connector_type == DRM_MODE_CONNECTOR_DSI || connector_type == DRM_MODE_CONNECTOR_eDP || connector_type == DRM_MODE_CONNECTOR_DisplayPort || diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 01eb6e4e6049..0d4012097db1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -626,6 +626,8 @@ struct intel_connector { struct intel_dp *mst_port; + bool force_bigjoiner_enable; + struct { struct drm_dp_aux *dsc_decompression_aux; u8 dsc_dpcd[DP_DSC_RECEIVER_CAP_SIZE]; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 5045c34a16be..217196196e50 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1205,11 +1205,13 @@ bool intel_dp_need_bigjoiner(struct intel_dp *intel_dp, int hdisplay, int clock) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_connector *connector = intel_dp->attached_connector; if (!intel_dp_can_bigjoiner(intel_dp)) return false; - return clock > i915->max_dotclk_freq || hdisplay > 5120; + return clock > i915->max_dotclk_freq || hdisplay > 5120 || + connector->force_bigjoiner_enable; } static enum drm_mode_status From 5c5a7fa612139e481e2fab368ddd86ae18866547 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 12 Feb 2024 19:52:36 +0200 Subject: [PATCH 18/90] drm/i915: Prevent HW access during init from SDVO TV get_modes hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent accessing the HW from the SDVO/TV get_modes connector hook. Returning 0 from the hook will make the caller - drm_helper_probe_single_connector_modes() - return a default/EDID override mode list to users. This matches the case where intel_sdvo_get_tv_modes() fails to retrieve the current mode list due to a HW access failure. v2: Clarify the commit message wrt. which modes get_modes() returns. (Jouni) Signed-off-by: Imre Deak Reviewed-by: Jouni Högander Link: https://patchwork.freedesktop.org/patch/msgid/20240212175237.2625812-1-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_sdvo.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index 66dab0a37e65..eb65c8124e60 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -2312,6 +2312,9 @@ static int intel_sdvo_get_tv_modes(struct drm_connector *connector) drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + if (!intel_display_driver_check_access(i915)) + return 0; + /* * Read the list of supported input resolutions for the selected TV * format. From 3a86cb16ab329b235ecd26adce584e5cb0c808d4 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 12 Feb 2024 19:52:37 +0200 Subject: [PATCH 19/90] drm/i915: Prevent HW access during init from connector get_modes hooks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prevent accessing the HW from the get_modes hooks of connectors deriving the mode list from the display's EDID. drm_edid_connector_add_modes() will return the mode list based on the EDID which was cached during a previous detection/get_modes call. This also fixes the NULL deref problem (10085) which was introduced/revealed by commit bab87ef4db9a ("drm/i915: Disable hotplug detection handlers during driver init/shutdown") After the above change MST connectors will not change state during driver init/shutdown; thus some of these connectors with no I2C/DDC adapter registered for them (since the given MST port has no sink connected) may stay then in the 'unknown' connector status. The get_modes() hook should not try to use the I2C/DDC adapter in this state (which would lead to the above NULL deref) which this patch ensures. v2: - Remove the redundant check from intel_crt_ddc_get_modes(). - Rebase on latest drm-tip. - Add Fixes: line / related commit notes. Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/10085 Fixes: bab87ef4db9a ("drm/i915: Disable hotplug detection handlers during driver init/shutdown") Signed-off-by: Imre Deak Reviewed-by: Jouni Högander Link: https://patchwork.freedesktop.org/patch/msgid/20240212175237.2625812-2-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_crt.c | 3 +++ drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 ++++ drivers/gpu/drm/i915/display/intel_dvo.c | 5 +++++ drivers/gpu/drm/i915/display/intel_sdvo.c | 4 ++++ 4 files changed, 16 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index b9733a73e21d..93479db0f89f 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -933,6 +933,9 @@ static int intel_crt_get_modes(struct drm_connector *connector) struct i2c_adapter *ddc; int ret; + if (!intel_display_driver_check_access(dev_priv)) + return drm_edid_connector_add_modes(connector); + wakeref = intel_display_power_get(dev_priv, intel_encoder->power_domain); diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 5fa25a5a36b5..5307ddd4edcf 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -1197,6 +1197,7 @@ static bool intel_dp_mst_initial_fastset_check(struct intel_encoder *encoder, static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector) { struct intel_connector *intel_connector = to_intel_connector(connector); + struct drm_i915_private *i915 = to_i915(intel_connector->base.dev); struct intel_dp *intel_dp = intel_connector->mst_port; const struct drm_edid *drm_edid; int ret; @@ -1204,6 +1205,9 @@ static int intel_dp_mst_get_ddc_modes(struct drm_connector *connector) if (drm_connector_is_unregistered(connector)) return intel_connector_update_modes(connector, NULL); + if (!intel_display_driver_check_access(i915)) + return drm_edid_connector_add_modes(connector); + drm_edid = drm_dp_mst_edid_read(connector, &intel_dp->mst_mgr, intel_connector->port); ret = intel_connector_update_modes(connector, drm_edid); diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c index 8ca9ae4798a8..c076da75b066 100644 --- a/drivers/gpu/drm/i915/display/intel_dvo.c +++ b/drivers/gpu/drm/i915/display/intel_dvo.c @@ -30,6 +30,7 @@ #include #include +#include #include "i915_drv.h" #include "i915_reg.h" @@ -338,8 +339,12 @@ intel_dvo_detect(struct drm_connector *_connector, bool force) static int intel_dvo_get_modes(struct drm_connector *_connector) { struct intel_connector *connector = to_intel_connector(_connector); + struct drm_i915_private *i915 = to_i915(connector->base.dev); int num_modes; + if (!intel_display_driver_check_access(i915)) + return drm_edid_connector_add_modes(&connector->base); + /* * We should probably have an i2c driver get_modes function for those * devices which will have a fixed set of modes determined by the chip diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index eb65c8124e60..3b79488357ba 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -2202,12 +2202,16 @@ intel_sdvo_detect(struct drm_connector *connector, bool force) static int intel_sdvo_get_ddc_modes(struct drm_connector *connector) { + struct drm_i915_private *i915 = to_i915(connector->dev); int num_modes = 0; const struct drm_edid *drm_edid; drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); + if (!intel_display_driver_check_access(i915)) + return drm_edid_connector_add_modes(connector); + /* set the bus switch and get the modes */ drm_edid = intel_sdvo_get_edid(connector); From d9b904d2efdf2abb9e0f7162544da562101872cb Mon Sep 17 00:00:00 2001 From: Ravi Kumar Vodapalli Date: Wed, 14 Feb 2024 12:39:30 +0530 Subject: [PATCH 20/90] drm/i915/display: update pll values in sync with Bspec for MTL DP/eDP and HDMI C20 PHY PLL values were updated for MTL platform Signed-off-by: Ravi Kumar Vodapalli Reviewed-by: Mika Kahola Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20240214070930.1028456-1-ravi.kumar.vodapalli@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 32 ++++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 288a00e083c8..64e0f820a789 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -848,10 +848,10 @@ static const struct intel_c20pll_state mtl_c20_dp_hbr3 = { static const struct intel_c20pll_state mtl_c20_dp_uhbr10 = { .clock = 1000000, /* 10 Gbps */ .tx = { 0xbe21, /* tx cfg0 */ - 0x4800, /* tx cfg1 */ + 0xe800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ }, - .cmn = {0x0500, /* cmn cfg0*/ + .cmn = {0x0700, /* cmn cfg0*/ 0x0005, /* cmn cfg1 */ 0x0000, /* cmn cfg2 */ 0x0000, /* cmn cfg3 */ @@ -1641,7 +1641,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_594 = { static const struct intel_c20pll_state mtl_c20_hdmi_300 = { .clock = 3000000, .tx = { 0xbe98, /* tx cfg0 */ - 0x9800, /* tx cfg1 */ + 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ }, .cmn = { 0x0500, /* cmn cfg0*/ @@ -1649,8 +1649,8 @@ static const struct intel_c20pll_state mtl_c20_hdmi_300 = { 0x0000, /* cmn cfg2 */ 0x0000, /* cmn cfg3 */ }, - .mpllb = { 0x209c, /* mpllb cfg0 */ - 0x7d10, /* mpllb cfg1 */ + .mpllb = { 0x309c, /* mpllb cfg0 */ + 0x2110, /* mpllb cfg1 */ 0xca06, /* mpllb cfg2 */ 0xbe40, /* mpllb cfg3 */ 0x0000, /* mpllb cfg4 */ @@ -1666,7 +1666,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_300 = { static const struct intel_c20pll_state mtl_c20_hdmi_600 = { .clock = 6000000, .tx = { 0xbe98, /* tx cfg0 */ - 0x9800, /* tx cfg1 */ + 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ }, .cmn = { 0x0500, /* cmn cfg0*/ @@ -1674,8 +1674,8 @@ static const struct intel_c20pll_state mtl_c20_hdmi_600 = { 0x0000, /* cmn cfg2 */ 0x0000, /* cmn cfg3 */ }, - .mpllb = { 0x009c, /* mpllb cfg0 */ - 0x7d08, /* mpllb cfg1 */ + .mpllb = { 0x109c, /* mpllb cfg0 */ + 0x2108, /* mpllb cfg1 */ 0xca06, /* mpllb cfg2 */ 0xbe40, /* mpllb cfg3 */ 0x0000, /* mpllb cfg4 */ @@ -1691,7 +1691,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_600 = { static const struct intel_c20pll_state mtl_c20_hdmi_800 = { .clock = 8000000, .tx = { 0xbe98, /* tx cfg0 */ - 0x9800, /* tx cfg1 */ + 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ }, .cmn = { 0x0500, /* cmn cfg0*/ @@ -1699,8 +1699,8 @@ static const struct intel_c20pll_state mtl_c20_hdmi_800 = { 0x0000, /* cmn cfg2 */ 0x0000, /* cmn cfg3 */ }, - .mpllb = { 0x00d0, /* mpllb cfg0 */ - 0x7d08, /* mpllb cfg1 */ + .mpllb = { 0x10d0, /* mpllb cfg0 */ + 0x2108, /* mpllb cfg1 */ 0x4a06, /* mpllb cfg2 */ 0xbe40, /* mpllb cfg3 */ 0x0000, /* mpllb cfg4 */ @@ -1716,7 +1716,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_800 = { static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { .clock = 10000000, .tx = { 0xbe98, /* tx cfg0 */ - 0x9800, /* tx cfg1 */ + 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ }, .cmn = { 0x0500, /* cmn cfg0*/ @@ -1725,7 +1725,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { 0x0000, /* cmn cfg3 */ }, .mpllb = { 0x1104, /* mpllb cfg0 */ - 0x7d08, /* mpllb cfg1 */ + 0x2108, /* mpllb cfg1 */ 0x0a06, /* mpllb cfg2 */ 0xbe40, /* mpllb cfg3 */ 0x0000, /* mpllb cfg4 */ @@ -1741,7 +1741,7 @@ static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { static const struct intel_c20pll_state mtl_c20_hdmi_1200 = { .clock = 12000000, .tx = { 0xbe98, /* tx cfg0 */ - 0x9800, /* tx cfg1 */ + 0x8800, /* tx cfg1 */ 0x0000, /* tx cfg2 */ }, .cmn = { 0x0500, /* cmn cfg0*/ @@ -1749,8 +1749,8 @@ static const struct intel_c20pll_state mtl_c20_hdmi_1200 = { 0x0000, /* cmn cfg2 */ 0x0000, /* cmn cfg3 */ }, - .mpllb = { 0x0138, /* mpllb cfg0 */ - 0x7d08, /* mpllb cfg1 */ + .mpllb = { 0x1138, /* mpllb cfg0 */ + 0x2108, /* mpllb cfg1 */ 0x5486, /* mpllb cfg2 */ 0xfe40, /* mpllb cfg3 */ 0x0000, /* mpllb cfg4 */ From 425b463859eda4f4c071e517267acdd1c0d731bd Mon Sep 17 00:00:00 2001 From: Gustavo Sousa Date: Wed, 14 Feb 2024 11:46:30 -0300 Subject: [PATCH 21/90] drm/i915: Update ADL-N PCI IDs Extend the list of ADL-N PCI IDs to contain two new entries. Bspec: 68397 Signed-off-by: Gustavo Sousa Reviewed-by: Dnyaneshwar Bhadane Signed-off-by: Matt Roper Link: https://patchwork.freedesktop.org/patch/msgid/20240214144629.106702-2-gustavo.sousa@intel.com --- include/drm/i915_pciids.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/drm/i915_pciids.h b/include/drm/i915_pciids.h index 07779a11758e..28a96aa1e08f 100644 --- a/include/drm/i915_pciids.h +++ b/include/drm/i915_pciids.h @@ -672,7 +672,9 @@ #define INTEL_ADLN_IDS(info) \ INTEL_VGA_DEVICE(0x46D0, info), \ INTEL_VGA_DEVICE(0x46D1, info), \ - INTEL_VGA_DEVICE(0x46D2, info) + INTEL_VGA_DEVICE(0x46D2, info), \ + INTEL_VGA_DEVICE(0x46D3, info), \ + INTEL_VGA_DEVICE(0x46D4, info) /* RPL-S */ #define INTEL_RPLS_IDS(info) \ From 733c454e0d149356eaf10eaf77cdbcd4ae3d9924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 9 Feb 2024 20:38:05 +0200 Subject: [PATCH 22/90] drm/i915: Fix PLL state check for gmch platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GMCH DPLL state check was mistakenly removed in commit 87fc875a2b85 ("drm/i915/dg2: Skip shared DPLL handling"). Bring it back. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240209183809.16887-2-ville.syrjala@linux.intel.com Reviewed-by: Mika Kahola --- drivers/gpu/drm/i915/display/intel_display.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 1b844cac4c58..ba1c84257702 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -5216,9 +5216,11 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_BOOL(double_wide); - if (dev_priv->display.dpll.mgr) { + if (dev_priv->display.dpll.mgr) PIPE_CONF_CHECK_P(shared_dpll); + /* FIXME convert everything over the dpll_mgr */ + if (dev_priv->display.dpll.mgr || HAS_GMCH(dev_priv)) { PIPE_CONF_CHECK_X(dpll_hw_state.dpll); PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md); PIPE_CONF_CHECK_X(dpll_hw_state.fp0); From 8d18f12f97bff220ec1d8f0133906045f68a5701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 9 Feb 2024 20:38:06 +0200 Subject: [PATCH 23/90] drm/i915: Include the CRTC name in the ELD buffer mismatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most crtc state mismatches include the CRTC id+name in the prints. Also include it in the ELD buffer mismatch prints. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240209183809.16887-3-ville.syrjala@linux.intel.com Reviewed-by: Mika Kahola --- drivers/gpu/drm/i915/display/intel_display.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index ba1c84257702..2ff60bfedbb3 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4852,10 +4852,12 @@ memcmp_diff_len(const u8 *a, const u8 *b, size_t len) } static void -pipe_config_buffer_mismatch(struct drm_i915_private *dev_priv, - bool fastset, const char *name, +pipe_config_buffer_mismatch(bool fastset, const struct intel_crtc *crtc, + const char *name, const u8 *a, const u8 *b, size_t len) { + struct drm_i915_private *dev_priv = to_i915(crtc->base.dev); + if (fastset) { if (!drm_debug_enabled(DRM_UT_KMS)) return; @@ -4864,7 +4866,8 @@ pipe_config_buffer_mismatch(struct drm_i915_private *dev_priv, len = memcmp_diff_len(a, b, len); drm_dbg_kms(&dev_priv->drm, - "fastset requirement not met in %s buffer\n", name); + "[CRTC:%d:%s] fastset requirement not met in %s buffer\n", + crtc->base.base.id, crtc->base.name, name); print_hex_dump(KERN_DEBUG, "expected: ", DUMP_PREFIX_NONE, 16, 0, a, len, false); print_hex_dump(KERN_DEBUG, "found: ", DUMP_PREFIX_NONE, @@ -4873,7 +4876,8 @@ pipe_config_buffer_mismatch(struct drm_i915_private *dev_priv, /* only dump up to the last difference */ len = memcmp_diff_len(a, b, len); - drm_err(&dev_priv->drm, "mismatch in %s buffer\n", name); + drm_err(&dev_priv->drm, "[CRTC:%d:%s] mismatch in %s buffer\n", + crtc->base.base.id, crtc->base.name, name); print_hex_dump(KERN_ERR, "expected: ", DUMP_PREFIX_NONE, 16, 0, a, len, false); print_hex_dump(KERN_ERR, "found: ", DUMP_PREFIX_NONE, @@ -5072,7 +5076,7 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, BUILD_BUG_ON(sizeof(current_config->name) != (len)); \ BUILD_BUG_ON(sizeof(pipe_config->name) != (len)); \ if (!intel_compare_buffer(current_config->name, pipe_config->name, (len))) { \ - pipe_config_buffer_mismatch(dev_priv, fastset, __stringify(name), \ + pipe_config_buffer_mismatch(fastset, crtc, __stringify(name), \ current_config->name, \ pipe_config->name, \ (len)); \ From b56e24be5916cf8471f3cea998ad4352a78671ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 9 Feb 2024 20:38:07 +0200 Subject: [PATCH 24/90] drm/i915: Reuse ibx_dump_hw_state() for gmch platforms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GMCH platform DPLLs are similar to the IBX+ PCH DPLLs so we can just use the same state dump function for both. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240209183809.16887-4-ville.syrjala@linux.intel.com Reviewed-by: Mika Kahola --- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index e7e0a4cf9f93..c6cc7465b92c 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4458,13 +4458,7 @@ void intel_dpll_dump_hw_state(struct drm_i915_private *i915, /* fallback for platforms that don't use the shared dpll * infrastructure */ - drm_dbg_kms(&i915->drm, - "dpll_hw_state: dpll: 0x%x, dpll_md: 0x%x, " - "fp0: 0x%x, fp1: 0x%x\n", - hw_state->dpll, - hw_state->dpll_md, - hw_state->fp0, - hw_state->fp1); + ibx_dump_hw_state(i915, hw_state); } } From 7ab52cb3489e4001444acdc23979fbf59305570c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 9 Feb 2024 20:38:08 +0200 Subject: [PATCH 25/90] drm/i915: Add PLL .compare_hw_state() vfunc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Chunk up the humongous dpll_hw_state comparison check into per-platform variants, implemented in the dpll_mgr. This is step one in allowing each platform (or perhaps even PLL) type to have a custom hw state structure instead of having to smash it all into one. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240209183809.16887-5-ville.syrjala@linux.intel.com Reviewed-by: Mika Kahola --- drivers/gpu/drm/i915/display/intel_display.c | 78 ++++++++------- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 95 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_dpll_mgr.h | 3 + 3 files changed, 141 insertions(+), 35 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 2ff60bfedbb3..3d09900bf562 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4908,6 +4908,36 @@ pipe_config_mismatch(bool fastset, const struct intel_crtc *crtc, va_end(args); } +static void +pipe_config_pll_mismatch(bool fastset, + const struct intel_crtc *crtc, + const char *name, + const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b) +{ + struct drm_i915_private *i915 = to_i915(crtc->base.dev); + + if (fastset) { + if (!drm_debug_enabled(DRM_UT_KMS)) + return; + + drm_dbg_kms(&i915->drm, + "[CRTC:%d:%s] fastset requirement not met in %s\n", + crtc->base.base.id, crtc->base.name, name); + drm_dbg_kms(&i915->drm, "expected:\n"); + intel_dpll_dump_hw_state(i915, a); + drm_dbg_kms(&i915->drm, "found:\n"); + intel_dpll_dump_hw_state(i915, b); + } else { + drm_err(&i915->drm, "[CRTC:%d:%s] mismatch in %s buffer\n", + crtc->base.base.id, crtc->base.name, name); + drm_err(&i915->drm, "expected:\n"); + intel_dpll_dump_hw_state(i915, a); + drm_err(&i915->drm, "found:\n"); + intel_dpll_dump_hw_state(i915, b); + } +} + static bool fastboot_enabled(struct drm_i915_private *dev_priv) { /* Enable fastboot by default on Skylake and newer */ @@ -5017,7 +5047,17 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, } \ } while (0) -#define PIPE_CONF_CHECK_TIMINGS(name) do { \ +#define PIPE_CONF_CHECK_PLL(name) do { \ + if (!intel_dpll_compare_hw_state(dev_priv, ¤t_config->name, \ + &pipe_config->name)) { \ + pipe_config_pll_mismatch(fastset, crtc, __stringify(name), \ + ¤t_config->name, \ + &pipe_config->name); \ + ret = false; \ + } \ +} while (0) + +#define PIPE_CONF_CHECK_TIMINGS(name) do { \ PIPE_CONF_CHECK_I(name.crtc_hdisplay); \ PIPE_CONF_CHECK_I(name.crtc_htotal); \ PIPE_CONF_CHECK_I(name.crtc_hblank_start); \ @@ -5224,40 +5264,8 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_P(shared_dpll); /* FIXME convert everything over the dpll_mgr */ - if (dev_priv->display.dpll.mgr || HAS_GMCH(dev_priv)) { - PIPE_CONF_CHECK_X(dpll_hw_state.dpll); - PIPE_CONF_CHECK_X(dpll_hw_state.dpll_md); - PIPE_CONF_CHECK_X(dpll_hw_state.fp0); - PIPE_CONF_CHECK_X(dpll_hw_state.fp1); - PIPE_CONF_CHECK_X(dpll_hw_state.wrpll); - PIPE_CONF_CHECK_X(dpll_hw_state.spll); - PIPE_CONF_CHECK_X(dpll_hw_state.ctrl1); - PIPE_CONF_CHECK_X(dpll_hw_state.cfgcr1); - PIPE_CONF_CHECK_X(dpll_hw_state.cfgcr2); - PIPE_CONF_CHECK_X(dpll_hw_state.cfgcr0); - PIPE_CONF_CHECK_X(dpll_hw_state.div0); - PIPE_CONF_CHECK_X(dpll_hw_state.ebb0); - PIPE_CONF_CHECK_X(dpll_hw_state.ebb4); - PIPE_CONF_CHECK_X(dpll_hw_state.pll0); - PIPE_CONF_CHECK_X(dpll_hw_state.pll1); - PIPE_CONF_CHECK_X(dpll_hw_state.pll2); - PIPE_CONF_CHECK_X(dpll_hw_state.pll3); - PIPE_CONF_CHECK_X(dpll_hw_state.pll6); - PIPE_CONF_CHECK_X(dpll_hw_state.pll8); - PIPE_CONF_CHECK_X(dpll_hw_state.pll9); - PIPE_CONF_CHECK_X(dpll_hw_state.pll10); - PIPE_CONF_CHECK_X(dpll_hw_state.pcsdw12); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_refclkin_ctl); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_clktop2_coreclkctl1); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_clktop2_hsclkctl); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_div0); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_div1); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_lf); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_frac_lock); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_ssc); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_bias); - PIPE_CONF_CHECK_X(dpll_hw_state.mg_pll_tdc_coldst_bias); - } + if (dev_priv->display.dpll.mgr || HAS_GMCH(dev_priv)) + PIPE_CONF_CHECK_PLL(dpll_hw_state); PIPE_CONF_CHECK_X(dsi_pll.ctrl); PIPE_CONF_CHECK_X(dsi_pll.div); diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index c6cc7465b92c..ff480f171f75 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -109,6 +109,8 @@ struct intel_dpll_mgr { void (*update_ref_clks)(struct drm_i915_private *i915); void (*dump_hw_state)(struct drm_i915_private *i915, const struct intel_dpll_hw_state *hw_state); + bool (*compare_hw_state)(const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b); }; static void @@ -644,6 +646,15 @@ static void ibx_dump_hw_state(struct drm_i915_private *i915, hw_state->fp1); } +static bool ibx_compare_hw_state(const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b) +{ + return a->dpll == b->dpll && + a->dpll_md == b->dpll_md && + a->fp0 == b->fp0 && + a->fp1 == b->fp1; +} + static const struct intel_shared_dpll_funcs ibx_pch_dpll_funcs = { .enable = ibx_pch_dpll_enable, .disable = ibx_pch_dpll_disable, @@ -662,6 +673,7 @@ static const struct intel_dpll_mgr pch_pll_mgr = { .get_dplls = ibx_get_dpll, .put_dplls = intel_put_dpll, .dump_hw_state = ibx_dump_hw_state, + .compare_hw_state = ibx_compare_hw_state, }; static void hsw_ddi_wrpll_enable(struct drm_i915_private *i915, @@ -1220,6 +1232,13 @@ static void hsw_dump_hw_state(struct drm_i915_private *i915, hw_state->wrpll, hw_state->spll); } +static bool hsw_compare_hw_state(const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b) +{ + return a->wrpll == b->wrpll && + a->spll == b->spll; +} + static const struct intel_shared_dpll_funcs hsw_ddi_wrpll_funcs = { .enable = hsw_ddi_wrpll_enable, .disable = hsw_ddi_wrpll_disable, @@ -1278,6 +1297,7 @@ static const struct intel_dpll_mgr hsw_pll_mgr = { .put_dplls = intel_put_dpll, .update_ref_clks = hsw_update_dpll_ref_clks, .dump_hw_state = hsw_dump_hw_state, + .compare_hw_state = hsw_compare_hw_state, }; struct skl_dpll_regs { @@ -1929,6 +1949,14 @@ static void skl_dump_hw_state(struct drm_i915_private *i915, hw_state->cfgcr2); } +static bool skl_compare_hw_state(const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b) +{ + return a->ctrl1 == b->ctrl1 && + a->cfgcr1 == b->cfgcr1 && + a->cfgcr2 == b->cfgcr2; +} + static const struct intel_shared_dpll_funcs skl_ddi_pll_funcs = { .enable = skl_ddi_pll_enable, .disable = skl_ddi_pll_disable, @@ -1959,6 +1987,7 @@ static const struct intel_dpll_mgr skl_pll_mgr = { .put_dplls = intel_put_dpll, .update_ref_clks = skl_update_dpll_ref_clks, .dump_hw_state = skl_dump_hw_state, + .compare_hw_state = skl_compare_hw_state, }; static void bxt_ddi_pll_enable(struct drm_i915_private *i915, @@ -2392,6 +2421,21 @@ static void bxt_dump_hw_state(struct drm_i915_private *i915, hw_state->pcsdw12); } +static bool bxt_compare_hw_state(const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b) +{ + return a->ebb0 == b->ebb0 && + a->ebb4 == b->ebb4 && + a->pll0 == b->pll0 && + a->pll1 == b->pll1 && + a->pll2 == b->pll2 && + a->pll3 == b->pll3 && + a->pll6 == b->pll6 && + a->pll8 == b->pll8 && + a->pll10 == b->pll10 && + a->pcsdw12 == b->pcsdw12; +} + static const struct intel_shared_dpll_funcs bxt_ddi_pll_funcs = { .enable = bxt_ddi_pll_enable, .disable = bxt_ddi_pll_disable, @@ -2413,6 +2457,7 @@ static const struct intel_dpll_mgr bxt_pll_mgr = { .put_dplls = intel_put_dpll, .update_ref_clks = bxt_update_dpll_ref_clks, .dump_hw_state = bxt_dump_hw_state, + .compare_hw_state = bxt_compare_hw_state, }; static void icl_wrpll_get_multipliers(int bestdiv, int *pdiv, @@ -4005,6 +4050,25 @@ static void icl_dump_hw_state(struct drm_i915_private *i915, hw_state->mg_pll_tdc_coldst_bias); } +static bool icl_compare_hw_state(const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b) +{ + /* FIXME split combo vs. mg more thoroughly */ + return a->cfgcr0 == b->cfgcr0 && + a->cfgcr1 == b->cfgcr1 && + a->div0 == b->div0 && + a->mg_refclkin_ctl == b->mg_refclkin_ctl && + a->mg_clktop2_coreclkctl1 == b->mg_clktop2_coreclkctl1 && + a->mg_clktop2_hsclkctl == b->mg_clktop2_hsclkctl && + a->mg_pll_div0 == b->mg_pll_div0 && + a->mg_pll_div1 == b->mg_pll_div1 && + a->mg_pll_lf == b->mg_pll_lf && + a->mg_pll_frac_lock == b->mg_pll_frac_lock && + a->mg_pll_ssc == b->mg_pll_ssc && + a->mg_pll_bias == b->mg_pll_bias && + a->mg_pll_tdc_coldst_bias == b->mg_pll_tdc_coldst_bias; +} + static const struct intel_shared_dpll_funcs combo_pll_funcs = { .enable = combo_pll_enable, .disable = combo_pll_disable, @@ -4046,6 +4110,7 @@ static const struct intel_dpll_mgr icl_pll_mgr = { .update_active_dpll = icl_update_active_dpll, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = icl_dump_hw_state, + .compare_hw_state = icl_compare_hw_state, }; static const struct dpll_info ehl_plls[] = { @@ -4063,6 +4128,7 @@ static const struct intel_dpll_mgr ehl_pll_mgr = { .put_dplls = icl_put_dplls, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = icl_dump_hw_state, + .compare_hw_state = icl_compare_hw_state, }; static const struct intel_shared_dpll_funcs dkl_pll_funcs = { @@ -4094,6 +4160,7 @@ static const struct intel_dpll_mgr tgl_pll_mgr = { .update_active_dpll = icl_update_active_dpll, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = icl_dump_hw_state, + .compare_hw_state = icl_compare_hw_state, }; static const struct dpll_info rkl_plls[] = { @@ -4110,6 +4177,7 @@ static const struct intel_dpll_mgr rkl_pll_mgr = { .put_dplls = icl_put_dplls, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = icl_dump_hw_state, + .compare_hw_state = icl_compare_hw_state, }; static const struct dpll_info dg1_plls[] = { @@ -4127,6 +4195,7 @@ static const struct intel_dpll_mgr dg1_pll_mgr = { .put_dplls = icl_put_dplls, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = icl_dump_hw_state, + .compare_hw_state = icl_compare_hw_state, }; static const struct dpll_info adls_plls[] = { @@ -4144,6 +4213,7 @@ static const struct intel_dpll_mgr adls_pll_mgr = { .put_dplls = icl_put_dplls, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = icl_dump_hw_state, + .compare_hw_state = icl_compare_hw_state, }; static const struct dpll_info adlp_plls[] = { @@ -4166,6 +4236,7 @@ static const struct intel_dpll_mgr adlp_pll_mgr = { .update_active_dpll = icl_update_active_dpll, .update_ref_clks = icl_update_dpll_ref_clks, .dump_hw_state = icl_dump_hw_state, + .compare_hw_state = icl_compare_hw_state, }; /** @@ -4462,6 +4533,30 @@ void intel_dpll_dump_hw_state(struct drm_i915_private *i915, } } +/** + * intel_dpll_compare_hw_state - compare the two states + * @i915: i915 drm device + * @a: first DPLL hw state + * @b: second DPLL hw state + * + * Compare DPLL hw states @a and @b. + * + * Returns: true if the states are equal, false if the differ + */ +bool intel_dpll_compare_hw_state(struct drm_i915_private *i915, + const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b) +{ + if (i915->display.dpll.mgr) { + return i915->display.dpll.mgr->compare_hw_state(a, b); + } else { + /* fallback for platforms that don't use the shared dpll + * infrastructure + */ + return ibx_compare_hw_state(a, b); + } +} + static void verify_single_dpll_state(struct drm_i915_private *i915, struct intel_shared_dpll *pll, diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h index 616afe861b46..cc0e1386309d 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.h +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.h @@ -378,6 +378,9 @@ void intel_dpll_sanitize_state(struct drm_i915_private *i915); void intel_dpll_dump_hw_state(struct drm_i915_private *i915, const struct intel_dpll_hw_state *hw_state); +bool intel_dpll_compare_hw_state(struct drm_i915_private *i915, + const struct intel_dpll_hw_state *a, + const struct intel_dpll_hw_state *b); enum intel_dpll_id icl_tc_port_to_pll_id(enum tc_port tc_port); bool intel_dpll_is_combophy(enum intel_dpll_id id); From 1b923307a1b0067a302b394e73311aeaebc06f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Fri, 9 Feb 2024 20:38:09 +0200 Subject: [PATCH 26/90] drm/i915: Enable fastboot across the board MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's nothing magical about vlv+ platforms vs. fastboot. If it works somewhere it should work everywhere, assuming we've not missed any crucial state checks. That seems unlikely on older platforms with less state to check anyway. Just enable fastboot across the board, and the remove the remnants of the optional stuff (we already removed the modparam for fastboot anyway). Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240209183809.16887-6-ville.syrjala@linux.intel.com Reviewed-by: Mika Kahola --- drivers/gpu/drm/i915/display/intel_display.c | 22 -------------------- 1 file changed, 22 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 3d09900bf562..607117ac2168 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4938,20 +4938,6 @@ pipe_config_pll_mismatch(bool fastset, } } -static bool fastboot_enabled(struct drm_i915_private *dev_priv) -{ - /* Enable fastboot by default on Skylake and newer */ - if (DISPLAY_VER(dev_priv) >= 9) - return true; - - /* Enable fastboot by default on VLV and CHV */ - if (IS_VALLEYVIEW(dev_priv) || IS_CHERRYVIEW(dev_priv)) - return true; - - /* Disabled by default on all others */ - return false; -} - bool intel_pipe_config_compare(const struct intel_crtc_state *current_config, const struct intel_crtc_state *pipe_config, @@ -4960,14 +4946,6 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, struct drm_i915_private *dev_priv = to_i915(current_config->uapi.crtc->dev); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); bool ret = true; - bool fixup_inherited = fastset && - current_config->inherited && !pipe_config->inherited; - - if (fixup_inherited && !fastboot_enabled(dev_priv)) { - drm_dbg_kms(&dev_priv->drm, - "initial modeset and fastboot not set\n"); - ret = false; - } #define PIPE_CONF_CHECK_X(name) do { \ if (current_config->name != pipe_config->name) { \ From 26aba0d1c35ff78434d8cb1dd97bd6b4cbdfe851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 7 Feb 2024 03:33:31 +0200 Subject: [PATCH 27/90] drm/i915/cdclk: Extract cdclk_divider() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the cdclk divider calculation into a helper. We'll have more users of this soon. We can now also get rid of the intermediate 'unsquashed_cdclk' variable. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240207013334.29606-2-ville.syrjala@linux.intel.com Reviewed-by: Gustavo Sousa --- drivers/gpu/drm/i915/display/intel_cdclk.c | 31 ++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 26200ee3e23f..d79485e17ed1 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1406,6 +1406,20 @@ static const struct intel_cdclk_vals lnl_cdclk_table[] = { {} }; +static const int cdclk_squash_len = 16; + +static int cdclk_squash_divider(u16 waveform) +{ + return hweight16(waveform ?: 0xffff); +} + +static int cdclk_divider(int cdclk, int vco, u16 waveform) +{ + /* 2 * cd2x divider */ + return DIV_ROUND_CLOSEST(vco * cdclk_squash_divider(waveform), + cdclk * cdclk_squash_len); +} + static int bxt_calc_cdclk(struct drm_i915_private *dev_priv, int min_cdclk) { const struct intel_cdclk_vals *table = dev_priv->display.cdclk.table; @@ -1744,10 +1758,10 @@ static u32 bxt_cdclk_cd2x_pipe(struct drm_i915_private *dev_priv, enum pipe pipe } static u32 bxt_cdclk_cd2x_div_sel(struct drm_i915_private *dev_priv, - int cdclk, int vco) + int cdclk, int vco, u16 waveform) { /* cdclk = vco / 2 / div{1,1.5,2,4} */ - switch (DIV_ROUND_CLOSEST(vco, cdclk)) { + switch (cdclk_divider(cdclk, vco, waveform)) { default: drm_WARN_ON(&dev_priv->drm, cdclk != dev_priv->display.cdclk.hw.bypass); @@ -1826,13 +1840,6 @@ static bool cdclk_pll_is_unknown(unsigned int vco) return vco == ~0; } -static const int cdclk_squash_len = 16; - -static int cdclk_squash_divider(u16 waveform) -{ - return hweight16(waveform ?: 0xffff); -} - static bool cdclk_compute_crawl_and_squash_midpoint(struct drm_i915_private *i915, const struct intel_cdclk_config *old_cdclk_config, const struct intel_cdclk_config *new_cdclk_config, @@ -1906,16 +1913,12 @@ static u32 bxt_cdclk_ctl(struct drm_i915_private *i915, { int cdclk = cdclk_config->cdclk; int vco = cdclk_config->vco; - int unsquashed_cdclk; u16 waveform; u32 val; waveform = cdclk_squash_waveform(i915, cdclk); - unsquashed_cdclk = DIV_ROUND_CLOSEST(cdclk * cdclk_squash_len, - cdclk_squash_divider(waveform)); - - val = bxt_cdclk_cd2x_div_sel(i915, unsquashed_cdclk, vco) | + val = bxt_cdclk_cd2x_div_sel(i915, cdclk, vco, waveform) | bxt_cdclk_cd2x_pipe(i915, pipe); /* From 76184fa3d32ed9b43da7c451cbbb45e567d20196 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 7 Feb 2024 03:33:32 +0200 Subject: [PATCH 28/90] drm/i915/cdclk: Squash waveform is 16 bits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have cdclk_squash_waveform() return a u16 since that's how many bits we have in the waveform. We alreday use u16 everywhere else. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240207013334.29606-3-ville.syrjala@linux.intel.com Reviewed-by: Gustavo Sousa --- drivers/gpu/drm/i915/display/intel_cdclk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index d79485e17ed1..a0013e37d53c 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1778,7 +1778,7 @@ static u32 bxt_cdclk_cd2x_div_sel(struct drm_i915_private *dev_priv, } } -static u32 cdclk_squash_waveform(struct drm_i915_private *dev_priv, +static u16 cdclk_squash_waveform(struct drm_i915_private *dev_priv, int cdclk) { const struct intel_cdclk_vals *table = dev_priv->display.cdclk.table; From c0151c695fbefcef816432e00d2f8d427ebf3125 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 7 Feb 2024 03:33:33 +0200 Subject: [PATCH 29/90] drm/i915/cdclk: Remove the hardcoded divider from cdclk_compute_crawl_and_squash_midpoint() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cdclk_compute_crawl_and_squash_midpoint() was still assuming that cd2x divider == 1 (ie. full divider == 2). Remove that assumption by computing the dividers properly. We'll also toss in a WARN in case the divider somehow ends up different between the old and new cdclk configs. That should never happen given we have div==2 in all the cdclk table entries for the affected platforms. If in the future we need a config where the divider also needs to be changed then we likely need to add an extra step into the cdclk programming sequence to make sure things stay within legal limits throughout the process. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240207013334.29606-4-ville.syrjala@linux.intel.com Reviewed-by: Gustavo Sousa --- drivers/gpu/drm/i915/display/intel_cdclk.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index a0013e37d53c..ca00586fdbc8 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1846,7 +1846,7 @@ static bool cdclk_compute_crawl_and_squash_midpoint(struct drm_i915_private *i91 struct intel_cdclk_config *mid_cdclk_config) { u16 old_waveform, new_waveform, mid_waveform; - int div = 2; + int old_div, new_div, mid_div; /* Return if PLL is in an unknown state, force a complete disable and re-enable. */ if (cdclk_pll_is_unknown(old_cdclk_config->vco)) @@ -1865,6 +1865,18 @@ static bool cdclk_compute_crawl_and_squash_midpoint(struct drm_i915_private *i91 old_waveform == new_waveform) return false; + old_div = cdclk_divider(old_cdclk_config->cdclk, + old_cdclk_config->vco, old_waveform); + new_div = cdclk_divider(new_cdclk_config->cdclk, + new_cdclk_config->vco, new_waveform); + + /* + * Should not happen currently. We might need more midpoint + * transitions if we need to also change the cd2x divider. + */ + if (drm_WARN_ON(&i915->drm, old_div != new_div)) + return false; + *mid_cdclk_config = *new_cdclk_config; /* @@ -1877,15 +1889,17 @@ static bool cdclk_compute_crawl_and_squash_midpoint(struct drm_i915_private *i91 if (cdclk_squash_divider(new_waveform) > cdclk_squash_divider(old_waveform)) { mid_cdclk_config->vco = old_cdclk_config->vco; + mid_div = old_div; mid_waveform = new_waveform; } else { mid_cdclk_config->vco = new_cdclk_config->vco; + mid_div = new_div; mid_waveform = old_waveform; } mid_cdclk_config->cdclk = DIV_ROUND_CLOSEST(cdclk_squash_divider(mid_waveform) * mid_cdclk_config->vco, - cdclk_squash_len * div); + cdclk_squash_len * mid_div); /* make sure the mid clock came out sane */ From 79e2ea2eaaa699916ebd74232c8f2ef949f8cb90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Wed, 7 Feb 2024 03:33:34 +0200 Subject: [PATCH 30/90] drm/i915/cdclk: Document CDCLK update methods MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a bit of documentation to briefly explain the methods by which we can change the CDCLK frequency. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20240207013334.29606-5-ville.syrjala@linux.intel.com Reviewed-by: Gustavo Sousa --- drivers/gpu/drm/i915/display/intel_cdclk.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index ca00586fdbc8..30dae4fef6cb 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -63,6 +63,15 @@ * DMC will not change the active CDCLK frequency however, so that part * will still be performed by the driver directly. * + * Several methods exist to change the CDCLK frequency, which ones are + * supported depends on the platform: + * - Full PLL disable + re-enable with new VCO frequency. Pipes must be inactive. + * - CD2X divider update. Single pipe can be active as the divider update + * can be synchronized with the pipe's start of vblank. + * - Crawl the PLL smoothly to the new VCO frequency. Pipes can be active. + * - Squash waveform update. Pipes can be active. + * - Crawl and squash can also be done back to back. Pipes can be active. + * * RAWCLK is a fixed frequency clock, often used by various auxiliary * blocks such as AUX CH or backlight PWM. Hence the only thing we * really need to know about RAWCLK is its frequency so that various From a9b5f422339f3581270c55c674b73987083234c7 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:06 +0100 Subject: [PATCH 31/90] drm/i915: remove unused intel_dvo_dev_ops hooks struct intel_dvo_dev_ops's ::create_resources(), ::prepare(), ::commit::, and get_modes() are all unused since their addition in 79e539453b34 (DRM: i915: add mode setting support). Drop all of them. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-2-jirislaby@kernel.org --- drivers/gpu/drm/i915/display/intel_dvo_dev.h | 25 -------------------- 1 file changed, 25 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dvo_dev.h b/drivers/gpu/drm/i915/display/intel_dvo_dev.h index f7e98e1c6470..af7b04539b93 100644 --- a/drivers/gpu/drm/i915/display/intel_dvo_dev.h +++ b/drivers/gpu/drm/i915/display/intel_dvo_dev.h @@ -53,12 +53,6 @@ struct intel_dvo_dev_ops { bool (*init)(struct intel_dvo_device *dvo, struct i2c_adapter *i2cbus); - /* - * Called to allow the output a chance to create properties after the - * RandR objects have been created. - */ - void (*create_resources)(struct intel_dvo_device *dvo); - /* * Turn on/off output. * @@ -79,16 +73,6 @@ struct intel_dvo_dev_ops { enum drm_mode_status (*mode_valid)(struct intel_dvo_device *dvo, struct drm_display_mode *mode); - /* - * Callback for preparing mode changes on an output - */ - void (*prepare)(struct intel_dvo_device *dvo); - - /* - * Callback for committing mode changes on an output - */ - void (*commit)(struct intel_dvo_device *dvo); - /* * Callback for setting up a video mode after fixups have been made. * @@ -111,15 +95,6 @@ struct intel_dvo_dev_ops { */ bool (*get_hw_state)(struct intel_dvo_device *dev); - /** - * Query the device for the modes it provides. - * - * This function may also update MonInfo, mm_width, and mm_height. - * - * \return singly-linked list of modes or NULL if no modes found. - */ - struct drm_display_mode *(*get_modes)(struct intel_dvo_device *dvo); - /** * Clean up driver-specific bits of the output */ From 172c52e276d55b94f93f17b58c3db4836184c9b2 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:07 +0100 Subject: [PATCH 32/90] drm/i915: remove structs intel_vgpu_pipe_format and intel_vgpu_fb_format Both struct intel_vgpu_pipe_format and intel_vgpu_fb_format were never used since its addition in commit 9f31d1063b43 (drm/i915/gvt: Add framebuffer decoder support). Drop them. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-3-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/fb_decoder.h | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/fb_decoder.h b/drivers/gpu/drm/i915/gvt/fb_decoder.h index 4eff44194439..fa6503900c84 100644 --- a/drivers/gpu/drm/i915/gvt/fb_decoder.h +++ b/drivers/gpu/drm/i915/gvt/fb_decoder.h @@ -152,17 +152,6 @@ struct intel_vgpu_cursor_plane_format { u32 y_hot; /* in pixels */ }; -struct intel_vgpu_pipe_format { - struct intel_vgpu_primary_plane_format primary; - struct intel_vgpu_sprite_plane_format sprite; - struct intel_vgpu_cursor_plane_format cursor; - enum DDI_PORT ddi_port; /* the DDI port that pipe is connected to */ -}; - -struct intel_vgpu_fb_format { - struct intel_vgpu_pipe_format pipes[I915_MAX_PIPES]; -}; - int intel_vgpu_decode_primary_plane(struct intel_vgpu *vgpu, struct intel_vgpu_primary_plane_format *plane); int intel_vgpu_decode_cursor_plane(struct intel_vgpu *vgpu, From de2fa4ef66b8ed30bd5d2a1ed42bec09cd9c1ca8 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:08 +0100 Subject: [PATCH 33/90] drm/i915: remove intel_dsi::{port_bits,hs} intel_dsi::port_bits is unused since commit 369602d370fa (drm/i915: Add support for port enable/disable for dual link configuration) and ::hs is unused likely since commit 063c86f60ad4 (drm/i915/dsi: remove intel_dsi_cmd.c and the unused functions therein). Drop them. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-4-jirislaby@kernel.org --- drivers/gpu/drm/i915/display/intel_dsi.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dsi.h b/drivers/gpu/drm/i915/display/intel_dsi.h index 083390e5e442..e99c94edfaae 100644 --- a/drivers/gpu/drm/i915/display/intel_dsi.h +++ b/drivers/gpu/drm/i915/display/intel_dsi.h @@ -57,9 +57,6 @@ struct intel_dsi { u16 phys; /* ICL DSI */ }; - /* if true, use HS mode, otherwise LP */ - bool hs; - /* virtual channel */ int channel; @@ -93,7 +90,6 @@ struct intel_dsi { bool bgr_enabled; u8 pixel_overlap; - u32 port_bits; u32 bw_timer; u32 dphy_reg; From fe7ed0e9dc92296c511c3fad37ae31026a48d53e Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:09 +0100 Subject: [PATCH 34/90] drm/i915: remove intel_gvt_gtt::{mm_alloc_page_table, mm_free_page_table} intel_gvt_gtt::{mm_alloc_page_table,mm_free_page_table} are not used since commit ede9d0cfcb78 (drm/i915/gvt: Rework shadow graphic memory management code). Drop them. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-5-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/gtt.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index 4cb183e06e95..fb96ea454fd1 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -93,8 +93,6 @@ struct intel_gvt_gtt_gma_ops { struct intel_gvt_gtt { const struct intel_gvt_gtt_pte_ops *pte_ops; const struct intel_gvt_gtt_gma_ops *gma_ops; - int (*mm_alloc_page_table)(struct intel_vgpu_mm *mm); - void (*mm_free_page_table)(struct intel_vgpu_mm *mm); struct list_head oos_page_use_list_head; struct list_head oos_page_free_list_head; struct mutex ppgtt_mm_lock; From 9fda1fd90984554559b93cbff9444457ddd7a9ae Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:10 +0100 Subject: [PATCH 35/90] drm/i915: remove intel_gvt_mmio_info::{device, addr_range} intel_gvt_mmio_info::device is not used since commit e0f74ed4634d (i915/gvt: Separate the MMIO tracking table from GVT-g) and ::addr_range was never used since its addition in commit 12d14cc43b34 (drm/i915/gvt: Introduce a framework for tracking HW registers.). Drop them. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-6-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/mmio.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/mmio.h b/drivers/gpu/drm/i915/gvt/mmio.h index bba154e38705..32ebacb078e8 100644 --- a/drivers/gpu/drm/i915/gvt/mmio.h +++ b/drivers/gpu/drm/i915/gvt/mmio.h @@ -62,10 +62,8 @@ typedef int (*gvt_mmio_func)(struct intel_vgpu *, unsigned int, void *, struct intel_gvt_mmio_info { u32 offset; u64 ro_mask; - u32 device; gvt_mmio_func read; gvt_mmio_func write; - u32 addr_range; struct hlist_node node; }; From 8266cdae3c5dc3a00cae06a9fc97221eb396dffd Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:11 +0100 Subject: [PATCH 36/90] drm/i915: remove intel_vgpu_workload::{ring_context, restore_inhibit} intel_vgpu_workload::ring_context was never used since its addition in commit 28c4c6ca7f79 (drm/i915/gvt: vGPU workload submission) and ::restore_inhibit since its addition in commit e473405783c0 (drm/i915/gvt: vGPU workload scheduler). Drop them. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-7-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/scheduler.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/scheduler.h b/drivers/gpu/drm/i915/gvt/scheduler.h index 1f391b3da2cc..cd94993278b6 100644 --- a/drivers/gpu/drm/i915/gvt/scheduler.h +++ b/drivers/gpu/drm/i915/gvt/scheduler.h @@ -104,10 +104,8 @@ struct intel_vgpu_workload { /* execlist context information */ struct execlist_ctx_descriptor_format ctx_desc; - struct execlist_ring_context *ring_context; unsigned long rb_head, rb_tail, rb_ctl, rb_start, rb_len; unsigned long guest_rb_head; - bool restore_inhibit; struct intel_vgpu_elsp_dwords elsp_dwords; bool emulate_schedule_in; atomic_t shadow_ctx_active; From dd0eb202bf4a01c7d1175b37b007b9d2086b4872 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:12 +0100 Subject: [PATCH 37/90] drm/i915: remove intel_vbt_panel_data::edp::initialized intel_vbt_panel_data::edp::initialized is not used since commit 9f0e7ff4b366 (drm/i915: fetch eDP configuration data from the VBT). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-8-jirislaby@kernel.org --- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 0d4012097db1..578763e202c0 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -327,7 +327,6 @@ struct intel_vbt_panel_data { struct edp_power_seq pps; u8 drrs_msa_timing_delay; bool low_vswing; - bool initialized; bool hobl; } edp; From 394a1376d82751651eb8b41ccdbc07dca74e682a Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:13 +0100 Subject: [PATCH 38/90] drm/i915: remove intel_guc::ads_engine_usage_size intel_guc::ads_engine_usage_size was never used since its addition in commit 77cdd054dd2c (drm/i915/pmu: Connect engine busyness stats from GuC to pmu). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-9-jirislaby@kernel.org --- drivers/gpu/drm/i915/gt/uc/intel_guc.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc.h b/drivers/gpu/drm/i915/gt/uc/intel_guc.h index 813cc888e6fa..be70c46604b4 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc.h +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc.h @@ -206,8 +206,6 @@ struct intel_guc { u32 ads_golden_ctxt_size; /** @ads_capture_size: size of register lists in the ADS used for error capture */ u32 ads_capture_size; - /** @ads_engine_usage_size: size of engine usage in the ADS */ - u32 ads_engine_usage_size; /** @lrc_desc_pool_v69: object allocated to hold the GuC LRC descriptor pool */ struct i915_vma *lrc_desc_pool_v69; From c5d859e34c917acaaad3cb62a089e8147bcbbe8d Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:14 +0100 Subject: [PATCH 39/90] drm/i915: remove i915_drm_client::id i915_drm_client::id is not used since commit e894b724c316 (drm/i915: Use the fdinfo helper). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-10-jirislaby@kernel.org --- drivers/gpu/drm/i915/i915_drm_client.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/i915_drm_client.h b/drivers/gpu/drm/i915/i915_drm_client.h index a439dd789936..2e7a50d16a88 100644 --- a/drivers/gpu/drm/i915/i915_drm_client.h +++ b/drivers/gpu/drm/i915/i915_drm_client.h @@ -24,8 +24,6 @@ struct drm_printer; struct i915_drm_client { struct kref kref; - unsigned int id; - spinlock_t ctx_lock; /* For add/remove from ctx_list. */ struct list_head ctx_list; /* List of contexts belonging to client. */ From 0b8722d65af555016a1457500ae30da55b030b12 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:15 +0100 Subject: [PATCH 40/90] drm/i915: remove i915_perf_stream::size_exponent i915_perf_stream::size_exponent was never used since its addition in commit a37f08a882b0 (drm/i915/perf: Refactor oa object to better manage resources). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-11-jirislaby@kernel.org --- drivers/gpu/drm/i915/i915_perf_types.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_perf_types.h b/drivers/gpu/drm/i915/i915_perf_types.h index 46445248d193..39fb6ce4a7ef 100644 --- a/drivers/gpu/drm/i915/i915_perf_types.h +++ b/drivers/gpu/drm/i915/i915_perf_types.h @@ -288,7 +288,6 @@ struct i915_perf_stream { struct i915_vma *vma; u8 *vaddr; u32 last_ctx_id; - int size_exponent; /** * @oa_buffer.ptr_lock: Locks reads and writes to all From dcbe15d9206346f4dd51c118abb9539d756fac59 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:16 +0100 Subject: [PATCH 41/90] drm/i915: remove intel_vgpu_gtt::active_ppgtt_mm_bitmap intel_vgpu_gtt::active_ppgtt_mm_bitmap was never used since its addition in commit 2707e4446688 (drm/i915/gvt: vGPU graphics memory virtualization). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-12-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/gtt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gtt.h b/drivers/gpu/drm/i915/gvt/gtt.h index fb96ea454fd1..cb50700e6cc9 100644 --- a/drivers/gpu/drm/i915/gvt/gtt.h +++ b/drivers/gpu/drm/i915/gvt/gtt.h @@ -208,7 +208,6 @@ struct intel_vgpu_scratch_pt { struct intel_vgpu_gtt { struct intel_vgpu_mm *ggtt_mm; - unsigned long active_ppgtt_mm_bitmap; struct list_head ppgtt_mm_list_head; struct radix_tree_root spt_tree; struct list_head oos_page_list_head; From 0c274be3f2bc12ee2d2cfc3a57a5f7dceb6b725c Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:17 +0100 Subject: [PATCH 42/90] drm/i915: remove intel_vgpu_fence::base intel_vgpu_fence::base was never used since its addition in commit 28a60dee2ce6 (drm/i915/gvt: vGPU HW resource management). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-13-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/gvt.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index c57aba09091f..b6fe17f1a16f 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -89,7 +89,6 @@ struct intel_vgpu_gm { /* Fences owned by a vGPU */ struct intel_vgpu_fence { struct i915_fence_reg *regs[INTEL_GVT_MAX_NUM_FENCES]; - u32 base; u32 size; }; @@ -444,7 +443,6 @@ int intel_gvt_load_firmware(struct intel_gvt *gvt); #define vgpu_hidden_gmadr_end(vgpu) \ (vgpu_hidden_gmadr_base(vgpu) + vgpu_hidden_sz(vgpu) - 1) -#define vgpu_fence_base(vgpu) (vgpu->fence.base) #define vgpu_fence_sz(vgpu) (vgpu->fence.size) /* ring context size i.e. the first 0x50 dwords*/ From c1d8fa8163bc2f13fba118c91200dce64cbc1606 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:18 +0100 Subject: [PATCH 43/90] drm/i915: remove intel_vgpu_opregion::mapped intel_vgpu_opregion::mapped is not used since commit 367748066eeb (drm/i915/gvt: remove enum hypervisor_type). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-14-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/gvt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index b6fe17f1a16f..8e5d696fc79c 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -118,7 +118,6 @@ struct intel_vgpu_irq { }; struct intel_vgpu_opregion { - bool mapped; void *va; u32 gfn[INTEL_GVT_OPREGION_PAGES]; }; From 7c25ef44547f0d1560483fe40d2ca84f49a29776 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:19 +0100 Subject: [PATCH 44/90] drm/i915: remove intel_vgpu::intx_trigger intel_vgpu::intx_trigger was never used since its addition in commit f30437c5e7bf (drm/i915/gvt: add KVMGT support). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-15-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/gvt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index 8e5d696fc79c..b8b8ffe4d566 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -221,7 +221,6 @@ struct intel_vgpu { struct vfio_region *region; int num_regions; - struct eventfd_ctx *intx_trigger; struct eventfd_ctx *msi_trigger; /* From 58b67c04cd8a07a025111971362cc24b15d41414 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:20 +0100 Subject: [PATCH 45/90] drm/i915: remove gvt_mmio_block::device gvt_mmio_block::device is not used since commit e0f74ed4634d (i915/gvt: Separate the MMIO tracking table from GVT-g). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-16-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/gvt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/gvt.h b/drivers/gpu/drm/i915/gvt/gvt.h index b8b8ffe4d566..2c95aeef4e41 100644 --- a/drivers/gpu/drm/i915/gvt/gvt.h +++ b/drivers/gpu/drm/i915/gvt/gvt.h @@ -253,7 +253,6 @@ struct intel_gvt_fence { /* Special MMIO blocks. */ struct gvt_mmio_block { - unsigned int device; i915_reg_t offset; unsigned int size; gvt_mmio_func read; From ad180eb410e38a7128454d5ccecc8901cdd7f5ff Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:21 +0100 Subject: [PATCH 46/90] drm/i915: remove intel_gvt_irq_info::warned intel_gvt_irq_info::warned was never used since its addition in commit c8fe6a6811a7 (drm/i915/gvt: vGPU interrupt virtualization.). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-17-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/interrupt.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/interrupt.c b/drivers/gpu/drm/i915/gvt/interrupt.c index de3f5903d1a7..27bd04053239 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.c +++ b/drivers/gpu/drm/i915/gvt/interrupt.c @@ -40,7 +40,6 @@ struct intel_gvt_irq_info { char *name; i915_reg_t reg_base; enum intel_gvt_event_type bit_to_event[INTEL_GVT_IRQ_BITWIDTH]; - unsigned long warned; int group; DECLARE_BITMAP(downstream_irq_bitmap, INTEL_GVT_IRQ_BITWIDTH); bool has_upstream_irq; From 4ced7884ac69bb1f0d0da90f3b8cb112520c11df Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:22 +0100 Subject: [PATCH 47/90] drm/i915: remove intel_gvt_event_info::policy intel_gvt_event_info::policy was never used since its addition in commit c8fe6a6811a7 (drm/i915/gvt: vGPU interrupt virtualization.). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-18-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/interrupt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h b/drivers/gpu/drm/i915/gvt/interrupt.h index e60ad476fe60..b1fd6ed4e34a 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.h +++ b/drivers/gpu/drm/i915/gvt/interrupt.h @@ -177,7 +177,6 @@ enum intel_gvt_irq_type { /* per-event information */ struct intel_gvt_event_info { int bit; /* map to register bit */ - int policy; /* forwarding policy */ struct intel_gvt_irq_info *info; /* register info */ gvt_event_virt_handler_t v_handler; /* for v_event */ }; From 7be6ff785af2a53cc3339241ef48d08d1c148f03 Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:23 +0100 Subject: [PATCH 48/90] drm/i915: remove intel_gvt_irq::pending_events intel_gvt_irq::pending_events was never used since its addition in commit c8fe6a6811a7 (drm/i915/gvt: vGPU interrupt virtualization.). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-19-jirislaby@kernel.org --- drivers/gpu/drm/i915/gvt/interrupt.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/gvt/interrupt.h b/drivers/gpu/drm/i915/gvt/interrupt.h index b1fd6ed4e34a..cd214be98668 100644 --- a/drivers/gpu/drm/i915/gvt/interrupt.h +++ b/drivers/gpu/drm/i915/gvt/interrupt.h @@ -187,7 +187,6 @@ struct intel_gvt_irq { struct intel_gvt_irq_info *info[INTEL_GVT_IRQ_INFO_MAX]; DECLARE_BITMAP(irq_info_bitmap, INTEL_GVT_IRQ_INFO_MAX); struct intel_gvt_event_info events[INTEL_GVT_EVENT_MAX]; - DECLARE_BITMAP(pending_events, INTEL_GVT_EVENT_MAX); struct intel_gvt_irq_map *irq_map; }; From 95f4e97f5e00f2cc56821ada09cd92bfca62184a Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:24 +0100 Subject: [PATCH 49/90] drm/i915: remove execute_cb::signal execute_cb::signal is not used since commit 5ac545b8b014 (drm/i915/request: Remove the hook from await_execution). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-20-jirislaby@kernel.org --- drivers/gpu/drm/i915/i915_request.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_request.c b/drivers/gpu/drm/i915/i915_request.c index f59081066a19..519e096c607c 100644 --- a/drivers/gpu/drm/i915/i915_request.c +++ b/drivers/gpu/drm/i915/i915_request.c @@ -52,7 +52,6 @@ struct execute_cb { struct irq_work work; struct i915_sw_fence *fence; - struct i915_request *signal; }; static struct kmem_cache *slab_requests; From 4c1f2a1e2b734a93f0146a5c1d59eae9106283ba Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:25 +0100 Subject: [PATCH 50/90] drm/i915: remove i915_vma::obj_hash i915_vma::obj_hash was never used since its addition in commit 4ff4b44cbb70 (drm/i915: Store a direct lookup from object handle to vma). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-21-jirislaby@kernel.org --- drivers/gpu/drm/i915/i915_vma_types.h | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/gpu/drm/i915/i915_vma_types.h b/drivers/gpu/drm/i915/i915_vma_types.h index 64472b7f0e77..559de74d0b11 100644 --- a/drivers/gpu/drm/i915/i915_vma_types.h +++ b/drivers/gpu/drm/i915/i915_vma_types.h @@ -290,7 +290,6 @@ struct i915_vma { struct list_head obj_link; /* Link in the object's VMA list */ struct rb_node obj_node; - struct hlist_node obj_hash; /** This vma's place in the eviction list */ struct list_head evict_link; From 4101f25ae26da122948dc81ae7568b28f0e0036a Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Fri, 16 Feb 2024 07:53:26 +0100 Subject: [PATCH 51/90] drm/i915: remove intel_memory_region_ops::flags intel_memory_region_ops::flags was never used since its addition in commit 232a6ebae419 (drm/i915: introduce intel_memory_region). Drop it. Found by https://github.com/jirislaby/clang-struct. Signed-off-by: Jiri Slaby (SUSE) Cc: Jani Nikula Cc: Joonas Lahtinen Cc: Rodrigo Vivi Cc: Tvrtko Ursulin Cc: intel-gfx@lists.freedesktop.org Acked-by: Jani Nikula Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240216065326.6910-22-jirislaby@kernel.org --- drivers/gpu/drm/i915/intel_memory_region.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/gpu/drm/i915/intel_memory_region.h b/drivers/gpu/drm/i915/intel_memory_region.h index 40810cfb3fd9..8c927e303c4a 100644 --- a/drivers/gpu/drm/i915/intel_memory_region.h +++ b/drivers/gpu/drm/i915/intel_memory_region.h @@ -50,8 +50,6 @@ enum intel_region_id { for_each_if((mr) = (i915)->mm.regions[id]) struct intel_memory_region_ops { - unsigned int flags; - int (*init)(struct intel_memory_region *mem); int (*release)(struct intel_memory_region *mem); From bf7626f19d6ff14b9722273e23700400cc4d78ba Mon Sep 17 00:00:00 2001 From: Maxime Ripard Date: Tue, 20 Feb 2024 14:12:51 +0100 Subject: [PATCH 52/90] drm/i915/tv: Fix TV mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1fd4a5a36f9f ("drm/connector: Rename legacy TV property") failed to update all the users of the struct drm_tv_connector_state mode field, which resulted in a build failure in i915. However, a subsequent commit in the same series reintroduced a mode field in that structure, with a different semantic but the same type, with the assumption that all previous users were updated. Since that didn't happen, the i915 driver now compiles, but mixes accesses to the legacy_mode field and the newer mode field, but with the previous semantics. This obviously doesn't work very well, so we need to update the accesses that weren't in the legacy renaming commit. Fixes: 1fd4a5a36f9f ("drm/connector: Rename legacy TV property") Reported-by: Ville Syrjälä Signed-off-by: Maxime Ripard Reviewed-by: Rodrigo Vivi Signed-off-by: Rodrigo Vivi Link: https://patchwork.freedesktop.org/patch/msgid/20240220131251.453060-1-mripard@kernel.org --- drivers/gpu/drm/i915/display/intel_sdvo.c | 10 +++++----- drivers/gpu/drm/i915/display/intel_tv.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index 3b79488357ba..d8b096779e12 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -1220,7 +1220,7 @@ static bool intel_sdvo_set_tv_format(struct intel_sdvo *intel_sdvo, struct intel_sdvo_tv_format format; u32 format_map; - format_map = 1 << conn_state->tv.mode; + format_map = 1 << conn_state->tv.legacy_mode; memset(&format, 0, sizeof(format)); memcpy(&format, &format_map, min(sizeof(format), sizeof(format_map))); @@ -2323,7 +2323,7 @@ static int intel_sdvo_get_tv_modes(struct drm_connector *connector) * Read the list of supported input resolutions for the selected TV * format. */ - format_map = 1 << conn_state->tv.mode; + format_map = 1 << conn_state->tv.legacy_mode; memcpy(&tv_res, &format_map, min(sizeof(format_map), sizeof(struct intel_sdvo_sdtv_resolution_request))); @@ -2388,7 +2388,7 @@ intel_sdvo_connector_atomic_get_property(struct drm_connector *connector, int i; for (i = 0; i < intel_sdvo_connector->format_supported_num; i++) - if (state->tv.mode == intel_sdvo_connector->tv_format_supported[i]) { + if (state->tv.legacy_mode == intel_sdvo_connector->tv_format_supported[i]) { *val = i; return 0; @@ -2444,7 +2444,7 @@ intel_sdvo_connector_atomic_set_property(struct drm_connector *connector, struct intel_sdvo_connector_state *sdvo_state = to_intel_sdvo_connector_state(state); if (property == intel_sdvo_connector->tv_format) { - state->tv.mode = intel_sdvo_connector->tv_format_supported[val]; + state->tv.legacy_mode = intel_sdvo_connector->tv_format_supported[val]; if (state->crtc) { struct drm_crtc_state *crtc_state = @@ -3108,7 +3108,7 @@ static bool intel_sdvo_tv_create_property(struct intel_sdvo *intel_sdvo, drm_property_add_enum(intel_sdvo_connector->tv_format, i, tv_format_names[intel_sdvo_connector->tv_format_supported[i]]); - intel_sdvo_connector->base.base.state->tv.mode = intel_sdvo_connector->tv_format_supported[0]; + intel_sdvo_connector->base.base.state->tv.legacy_mode = intel_sdvo_connector->tv_format_supported[0]; drm_object_attach_property(&intel_sdvo_connector->base.base.base, intel_sdvo_connector->tv_format, 0); return true; diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index a96bcfcf90a3..2b77d399f1a1 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -950,7 +950,7 @@ intel_disable_tv(struct intel_atomic_state *state, static const struct tv_mode *intel_tv_mode_find(const struct drm_connector_state *conn_state) { - int format = conn_state->tv.mode; + int format = conn_state->tv.legacy_mode; return &tv_modes[format]; } @@ -1705,7 +1705,7 @@ static void intel_tv_find_better_format(struct drm_connector *connector) break; } - connector->state->tv.mode = i; + connector->state->tv.legacy_mode = i; } static int @@ -1863,7 +1863,7 @@ static int intel_tv_atomic_check(struct drm_connector *connector, old_state = drm_atomic_get_old_connector_state(state, connector); new_crtc_state = drm_atomic_get_new_crtc_state(state, new_state->crtc); - if (old_state->tv.mode != new_state->tv.mode || + if (old_state->tv.legacy_mode != new_state->tv.legacy_mode || old_state->tv.margins.left != new_state->tv.margins.left || old_state->tv.margins.right != new_state->tv.margins.right || old_state->tv.margins.top != new_state->tv.margins.top || @@ -1900,7 +1900,7 @@ static void intel_tv_add_properties(struct drm_connector *connector) conn_state->tv.margins.right = 46; conn_state->tv.margins.bottom = 37; - conn_state->tv.mode = 0; + conn_state->tv.legacy_mode = 0; /* Create TV properties then attach current values */ for (i = 0; i < ARRAY_SIZE(tv_modes); i++) { @@ -1914,7 +1914,7 @@ static void intel_tv_add_properties(struct drm_connector *connector) drm_object_attach_property(&connector->base, i915->drm.mode_config.legacy_tv_mode_property, - conn_state->tv.mode); + conn_state->tv.legacy_mode); drm_object_attach_property(&connector->base, i915->drm.mode_config.tv_left_margin_property, conn_state->tv.margins.left); From 3d890f32877376ac689f752d8b74ecaea0c42232 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Thu, 1 Feb 2024 14:21:58 +0530 Subject: [PATCH 53/90] drm/i915/lnl: Add pkgc related register Add the register that needs to read and written onto for deep pkgc programming. Signed-off-by: Suraj Kandpal Reviewed-by: Chaitanya Kumar Borah Reviewed-by: Vinod Govindapillai Signed-off-by: Animesh Manna Link: https://patchwork.freedesktop.org/patch/msgid/20240201085158.1000285-2-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/skl_watermark_regs.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpu/drm/i915/display/skl_watermark_regs.h b/drivers/gpu/drm/i915/display/skl_watermark_regs.h index 628c5920ad49..20b30c9a6613 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark_regs.h +++ b/drivers/gpu/drm/i915/display/skl_watermark_regs.h @@ -157,4 +157,8 @@ #define MTL_LATENCY_SAGV _MMIO(0x4578c) #define MTL_LATENCY_QCLK_SAGV REG_GENMASK(12, 0) +#define LNL_PKG_C_LATENCY _MMIO(0x46460) +#define LNL_ADDED_WAKE_TIME_MASK REG_GENMASK(28, 16) +#define LNL_PKG_C_LATENCY_MASK REG_GENMASK(12, 0) + #endif /* __SKL_WATERMARK_REGS_H__ */ From 131288c4681bbc2727f20c4b31c89a93464aa9ca Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 19 Feb 2024 12:06:38 +0530 Subject: [PATCH 54/90] drm/i915/lnl: Program PKGC_LATENCY register If fixed refresh rate program the PKGC_LATENCY register with the highest latency from level 1 and above LP registers and program ADDED_WAKE_TIME = DSB execution time. else program PKGC_LATENCY with all 1's and ADDED_WAKE_TIME as 0. This is used to improve package C residency by sending the highest latency tolerance requirement (LTR) when the planes are done with the frame until the next frame programming window (set context latency, window 2) starts. Bspec: 68986 --v2 -Fix indentation [Chaitanya] --v3 -Take into account if fixed refrersh rate or not [Vinod] -Added wake time dependengt on DSB execution time [Vinod] -Use REG_FIELD_PREP [Jani] -Call program_pkgc_latency from appropriate place [Jani] -no need for the ~0 while setting max latency [Jani] -change commit message to add the new changes made in. --v4 -Remove extra blank line [Vinod] -move the vrr.enable check to previous loop [Vinod] Signed-off-by: Suraj Kandpal Reviewed-by: Chaitanya Kumar Borah Reviewed-by: Vinod Govindapillai Signed-off-by: Animesh Manna Link: https://patchwork.freedesktop.org/patch/msgid/20240219063638.1467114-1-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_dsb.c | 2 +- drivers/gpu/drm/i915/display/skl_watermark.c | 54 +++++++++++++++++++- drivers/gpu/drm/i915/display/skl_watermark.h | 4 +- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index a6c7122fd671..d62e050185e7 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -325,7 +325,7 @@ static int intel_dsb_dewake_scanline(const struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(crtc_state->uapi.crtc->dev); const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; - unsigned int latency = skl_watermark_max_latency(i915); + unsigned int latency = skl_watermark_max_latency(i915, 0); int vblank_start; if (crtc_state->vrr.enable) { diff --git a/drivers/gpu/drm/i915/display/skl_watermark.c b/drivers/gpu/drm/i915/display/skl_watermark.c index 614f319d754e..c6b9be80d83c 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.c +++ b/drivers/gpu/drm/i915/display/skl_watermark.c @@ -23,6 +23,12 @@ #include "skl_watermark.h" #include "skl_watermark_regs.h" +/*It is expected that DSB can do posted writes to every register in + * the pipe and planes within 100us. For flip queue use case, the + * recommended DSB execution time is 100us + one SAGV block time. + */ +#define DSB_EXE_TIME 100 + static void skl_sagv_disable(struct drm_i915_private *i915); /* Stores plane specific WM parameters */ @@ -2904,12 +2910,51 @@ static int skl_wm_add_affected_planes(struct intel_atomic_state *state, return 0; } +/* + * If Fixed Refresh Rate: + * Program DEEP PKG_C_LATENCY Pkg C with highest valid latency from + * watermark level1 and up and above. If watermark level 1 is + * invalid program it with all 1's. + * Program PKG_C_LATENCY Added Wake Time = DSB execution time + * If Variable Refresh Rate: + * Program DEEP PKG_C_LATENCY Pkg C with all 1's. + * Program PKG_C_LATENCY Added Wake Time = 0 + */ +static void +skl_program_dpkgc_latency(struct drm_i915_private *i915, bool vrr_enabled) +{ + u32 max_latency = 0; + u32 clear = 0, val = 0; + u32 added_wake_time = 0; + + if (DISPLAY_VER(i915) < 20) + return; + + if (vrr_enabled) { + max_latency = LNL_PKG_C_LATENCY_MASK; + added_wake_time = 0; + } else { + max_latency = skl_watermark_max_latency(i915, 1); + if (max_latency == 0) + max_latency = LNL_PKG_C_LATENCY_MASK; + added_wake_time = DSB_EXE_TIME + + i915->display.sagv.block_time_us; + } + + clear |= LNL_ADDED_WAKE_TIME_MASK | LNL_PKG_C_LATENCY_MASK; + val |= REG_FIELD_PREP(LNL_PKG_C_LATENCY_MASK, max_latency); + val |= REG_FIELD_PREP(LNL_ADDED_WAKE_TIME_MASK, added_wake_time); + + intel_uncore_rmw(&i915->uncore, LNL_PKG_C_LATENCY, clear, val); +} + static int skl_compute_wm(struct intel_atomic_state *state) { struct intel_crtc *crtc; struct intel_crtc_state __maybe_unused *new_crtc_state; int ret, i; + bool vrr_enabled = false; for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) { ret = skl_build_pipe_wm(state, crtc); @@ -2934,8 +2979,13 @@ skl_compute_wm(struct intel_atomic_state *state) ret = skl_wm_add_affected_planes(state, crtc); if (ret) return ret; + + if (new_crtc_state->vrr.enable) + vrr_enabled = true; } + skl_program_dpkgc_latency(to_i915(state->base.dev), vrr_enabled); + skl_print_wm_changes(state); return 0; @@ -3731,11 +3781,11 @@ void skl_watermark_debugfs_register(struct drm_i915_private *i915) &intel_sagv_status_fops); } -unsigned int skl_watermark_max_latency(struct drm_i915_private *i915) +unsigned int skl_watermark_max_latency(struct drm_i915_private *i915, int initial_wm_level) { int level; - for (level = i915->display.wm.num_levels - 1; level >= 0; level--) { + for (level = i915->display.wm.num_levels - 1; level >= initial_wm_level; level--) { unsigned int latency = skl_wm_latency(i915, level, NULL); if (latency) diff --git a/drivers/gpu/drm/i915/display/skl_watermark.h b/drivers/gpu/drm/i915/display/skl_watermark.h index fb0da36fd3ec..e3d1d74a7b17 100644 --- a/drivers/gpu/drm/i915/display/skl_watermark.h +++ b/drivers/gpu/drm/i915/display/skl_watermark.h @@ -46,8 +46,8 @@ void skl_watermark_ipc_update(struct drm_i915_private *i915); bool skl_watermark_ipc_enabled(struct drm_i915_private *i915); void skl_watermark_debugfs_register(struct drm_i915_private *i915); -unsigned int skl_watermark_max_latency(struct drm_i915_private *i915); - +unsigned int skl_watermark_max_latency(struct drm_i915_private *i915, + int initial_wm_level); void skl_wm_init(struct drm_i915_private *i915); struct intel_dbuf_state { From 0be4e0a5203d38d40d3de44c9dab6c3acc44fef5 Mon Sep 17 00:00:00 2001 From: Rodrigo Vivi Date: Wed, 21 Feb 2024 10:54:53 -0500 Subject: [PATCH 55/90] drm/i915: Fix doc build issue on intel_cdclk.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixing some doc build issues: Documentation/gpu/i915:222: drivers/gpu/drm/i915/display/intel_cdclk.c:69: ERROR: Unexpected indentation. Documentation/gpu/i915:222: ./drivers/gpu/drm/i915/display/intel_cdclk.c:70: WARNING: Block quote ends without a blank line; unexpected unindent. v2: Minimize the empty lines (Gustavo) Closes: https://lore.kernel.org/all/20240219161747.0e867406@canb.auug.org.au/ Fixes: 79e2ea2eaaa6 ("drm/i915/cdclk: Document CDCLK update methods") Cc: Ville Syrjälä Cc: Gustavo Sousa Reported-by: Stephen Rothwell Signed-off-by: Rodrigo Vivi Reviewed-by: Gustavo Sousa Link: https://patchwork.freedesktop.org/patch/msgid/20240221155453.94208-1-rodrigo.vivi@intel.com --- drivers/gpu/drm/i915/display/intel_cdclk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 30dae4fef6cb..ed89b86ea625 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -65,6 +65,7 @@ * * Several methods exist to change the CDCLK frequency, which ones are * supported depends on the platform: + * * - Full PLL disable + re-enable with new VCO frequency. Pipes must be inactive. * - CD2X divider update. Single pipe can be active as the divider update * can be synchronized with the pipe's start of vblank. From 20af10845864c9fe3d112f411c255ba5dae6e3ca Mon Sep 17 00:00:00 2001 From: Bhanuprakash Modem Date: Wed, 21 Feb 2024 16:42:23 +0530 Subject: [PATCH 56/90] drm/i915/display/debugfs: New entry "DRRS capable" to i915_drrs_status If the connected panel supports both DRRS & PSR, driver gives preference to PSR ("DRRS enabled: no"). Even though the hardware supports DRRS, IGT treats ("DRRS enabled: yes") as not capable. Introduce a new entry "DRRS capable" to debugfs i915_drrs_status, so that IGT will read the DRRS capability as "DRRS capable: yes". Signed-off-by: Bhanuprakash Modem Reviewed-by: Mitul Golani Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240221111223.2313692-1-bhanuprakash.modem@intel.com --- drivers/gpu/drm/i915/display/intel_drrs.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_drrs.c b/drivers/gpu/drm/i915/display/intel_drrs.c index 6282ec0fc9b4..169ef38ff188 100644 --- a/drivers/gpu/drm/i915/display/intel_drrs.c +++ b/drivers/gpu/drm/i915/display/intel_drrs.c @@ -299,6 +299,7 @@ void intel_drrs_crtc_init(struct intel_crtc *crtc) static int intel_drrs_debugfs_status_show(struct seq_file *m, void *unused) { struct intel_crtc *crtc = m->private; + struct drm_i915_private *i915 = to_i915(crtc->base.dev); const struct intel_crtc_state *crtc_state; int ret; @@ -310,6 +311,11 @@ static int intel_drrs_debugfs_status_show(struct seq_file *m, void *unused) mutex_lock(&crtc->drrs.mutex); + seq_printf(m, "DRRS capable: %s\n", + str_yes_no(crtc_state->has_drrs || + HAS_DOUBLE_BUFFERED_M_N(i915) || + intel_cpu_transcoder_has_m2_n2(i915, crtc_state->cpu_transcoder))); + seq_printf(m, "DRRS enabled: %s\n", str_yes_no(crtc_state->has_drrs)); From 287c0de8b29489cdb20957980ca08c33ae4a67b9 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 23 Feb 2024 13:44:42 +0530 Subject: [PATCH 57/90] drm/i915/hdcp: Move to direct reads for HDCP Even for MST scenarios we need to do direct reads only on the immediate downstream device the rest of the authentication is taken care by that device. Remote reads will only be used to check capability of the monitors in MST topology. --v2 -Add fixes tag [Ankit] -Derive aux where needed rather than through a function [Ankit] Fixes: ae4f902bb344 ("drm/i915/hdcp: Send the correct aux for DPMST HDCP scenario") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240223081453.1576918-3-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 31 ++++++-------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 9dff4bdfeec8..92642e8d82db 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -333,23 +333,13 @@ static const struct hdcp2_dp_msg_data hdcp2_dp_msg_data[] = { 0, 0 }, }; -static struct drm_dp_aux * -intel_dp_hdcp_get_aux(struct intel_connector *connector) -{ - struct intel_digital_port *dig_port = intel_attached_dig_port(connector); - - if (intel_encoder_is_mst(connector->encoder)) - return &connector->port->aux; - else - return &dig_port->dp.aux; -} - static int intel_dp_hdcp2_read_rx_status(struct intel_connector *connector, u8 *rx_status) { struct drm_i915_private *i915 = to_i915(connector->base.dev); - struct drm_dp_aux *aux = intel_dp_hdcp_get_aux(connector); + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_dp_aux *aux = &dig_port->dp.aux; ssize_t ret; ret = drm_dp_dpcd_read(aux, @@ -458,8 +448,9 @@ int intel_dp_hdcp2_write_msg(struct intel_connector *connector, unsigned int offset; u8 *byte = buf; ssize_t ret, bytes_to_write, len; + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_dp_aux *aux = &dig_port->dp.aux; const struct hdcp2_dp_msg_data *hdcp2_msg_data; - struct drm_dp_aux *aux; hdcp2_msg_data = get_hdcp2_dp_msg_data(*byte); if (!hdcp2_msg_data) @@ -467,8 +458,6 @@ int intel_dp_hdcp2_write_msg(struct intel_connector *connector, offset = hdcp2_msg_data->offset; - aux = intel_dp_hdcp_get_aux(connector); - /* No msg_id in DP HDCP2.2 msgs */ bytes_to_write = size - 1; byte++; @@ -494,7 +483,8 @@ static ssize_t get_receiver_id_list_rx_info(struct intel_connector *connector, u32 *dev_cnt, u8 *byte) { - struct drm_dp_aux *aux = intel_dp_hdcp_get_aux(connector); + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_dp_aux *aux = &dig_port->dp.aux; ssize_t ret; u8 *rx_info = byte; @@ -520,7 +510,7 @@ int intel_dp_hdcp2_read_msg(struct intel_connector *connector, struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_hdcp *hdcp = &connector->hdcp; - struct drm_dp_aux *aux; + struct drm_dp_aux *aux = &dig_port->dp.aux; unsigned int offset; u8 *byte = buf; ssize_t ret, bytes_to_recv, len; @@ -534,8 +524,6 @@ int intel_dp_hdcp2_read_msg(struct intel_connector *connector, return -EINVAL; offset = hdcp2_msg_data->offset; - aux = intel_dp_hdcp_get_aux(connector); - ret = intel_dp_hdcp2_wait_for_msg(connector, hdcp2_msg_data); if (ret < 0) return ret; @@ -655,12 +643,11 @@ static int intel_dp_hdcp2_capable(struct intel_connector *connector, bool *capable) { - struct drm_dp_aux *aux; + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_dp_aux *aux = &dig_port->dp.aux; u8 rx_caps[3]; int ret; - aux = intel_dp_hdcp_get_aux(connector); - *capable = false; ret = drm_dp_dpcd_read(aux, DP_HDCP_2_2_REG_RX_CAPS_OFFSET, From 26f7d01ca7c2bc01b1ad2e9fd73bfab23d9d6944 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 23 Feb 2024 13:44:43 +0530 Subject: [PATCH 58/90] drm/i915/hdcp: Move source hdcp2 checks into its own function Move checks on the source side for HDCP2.2 into its own function so that they can be used in the HDCP remote capability check function. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240223081453.1576918-4-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_hdcp.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index c3e692e7f790..4593ac10e2fa 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -160,12 +160,14 @@ bool intel_hdcp_capable(struct intel_connector *connector) return capable; } -/* Is HDCP2.2 capable on Platform and Sink */ -bool intel_hdcp2_capable(struct intel_connector *connector) +/* + * Check if the source has all the building blocks ready to make + * HDCP 2.2 work + */ +static bool intel_hdcp2_prerequisite(struct intel_connector *connector) { struct drm_i915_private *i915 = to_i915(connector->base.dev); struct intel_hdcp *hdcp = &connector->hdcp; - bool capable = false; /* I915 support for HDCP2.2 */ if (!hdcp->hdcp2_supported) @@ -185,6 +187,18 @@ bool intel_hdcp2_capable(struct intel_connector *connector) } mutex_unlock(&i915->display.hdcp.hdcp_mutex); + return true; +} + +/* Is HDCP2.2 capable on Platform and Sink */ +bool intel_hdcp2_capable(struct intel_connector *connector) +{ + struct intel_hdcp *hdcp = &connector->hdcp; + bool capable = false; + + if (!intel_hdcp2_prerequisite(connector)) + return false; + /* Sink's capability for HDCP2.2 */ hdcp->shim->hdcp_2_2_capable(connector, &capable); From 55d9b2b8e44033f6ba6069de2b44d1cfbc2e0081 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 23 Feb 2024 13:44:44 +0530 Subject: [PATCH 59/90] drm/i915/hdcp: Refactor intel_dp_hdcp2_capable Break intel_dp_hdcp2_capable so that the common the code can be reused for the remote capability check. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240223081453.1576918-5-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 92642e8d82db..6767b5338ae7 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -640,11 +640,9 @@ int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port, } static -int intel_dp_hdcp2_capable(struct intel_connector *connector, - bool *capable) +int _intel_dp_hdcp2_capable(struct drm_dp_aux *aux, + bool *capable) { - struct intel_digital_port *dig_port = intel_attached_dig_port(connector); - struct drm_dp_aux *aux = &dig_port->dp.aux; u8 rx_caps[3]; int ret; @@ -662,6 +660,16 @@ int intel_dp_hdcp2_capable(struct intel_connector *connector, return 0; } +static +int intel_dp_hdcp2_capable(struct intel_connector *connector, + bool *capable) +{ + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct drm_dp_aux *aux = &dig_port->dp.aux; + + return _intel_dp_hdcp2_capable(aux, capable); +} + static const struct intel_hdcp_shim intel_dp_hdcp_shim = { .write_an_aksv = intel_dp_hdcp_write_an_aksv, .read_bksv = intel_dp_hdcp_read_bksv, From 4ae5a09779017a95d573a3a55ae5ee39cc409791 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 23 Feb 2024 13:44:45 +0530 Subject: [PATCH 60/90] drm/i915/hdcp: Pass drm_dp_aux to read_bcaps function Pass drm_dp_aux to intel_dp_hdcp_read_bcaps function so as to aid in reading the bcaps for the remote monitor later on. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240223081453.1576918-6-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 6767b5338ae7..5394391acbe1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -125,13 +125,13 @@ static int intel_dp_hdcp_read_bstatus(struct intel_digital_port *dig_port, } static -int intel_dp_hdcp_read_bcaps(struct intel_digital_port *dig_port, +int intel_dp_hdcp_read_bcaps(struct drm_dp_aux *aux, + struct drm_i915_private *i915, u8 *bcaps) { - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); ssize_t ret; - ret = drm_dp_dpcd_read(&dig_port->dp.aux, DP_AUX_HDCP_BCAPS, + ret = drm_dp_dpcd_read(aux, DP_AUX_HDCP_BCAPS, bcaps, 1); if (ret != 1) { drm_dbg_kms(&i915->drm, @@ -146,10 +146,11 @@ static int intel_dp_hdcp_repeater_present(struct intel_digital_port *dig_port, bool *repeater_present) { + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); ssize_t ret; u8 bcaps; - ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps); + ret = intel_dp_hdcp_read_bcaps(&dig_port->dp.aux, i915, &bcaps); if (ret) return ret; @@ -271,10 +272,11 @@ static int intel_dp_hdcp_capable(struct intel_digital_port *dig_port, bool *hdcp_capable) { + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); ssize_t ret; u8 bcaps; - ret = intel_dp_hdcp_read_bcaps(dig_port, &bcaps); + ret = intel_dp_hdcp_read_bcaps(&dig_port->dp.aux, i915, &bcaps); if (ret) return ret; From 8e754d9e9e8a068d18cba6618e099a1f24347c98 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 23 Feb 2024 13:44:46 +0530 Subject: [PATCH 61/90] drm/i915/hdcp: Rename hdcp capable functions Rename hdcp_capable and hdcp_2_2_capable to hdcp_get_capability and hdcp_2_2_get_capability to properly reflect what these functions are doing. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240223081453.1576918-7-suraj.kandpal@intel.com --- .../drm/i915/display/intel_display_debugfs.c | 4 ++-- .../drm/i915/display/intel_display_types.h | 8 +++---- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 22 +++++++++---------- drivers/gpu/drm/i915/display/intel_hdcp.c | 18 +++++++-------- drivers/gpu/drm/i915/display/intel_hdcp.h | 4 ++-- drivers/gpu/drm/i915/display/intel_hdmi.c | 6 ++--- 6 files changed, 31 insertions(+), 31 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index a962b48bcf13..d8f1a23ac2b1 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -197,8 +197,8 @@ static void intel_hdcp_info(struct seq_file *m, goto out; } - hdcp_cap = intel_hdcp_capable(intel_connector); - hdcp2_cap = intel_hdcp2_capable(intel_connector); + hdcp_cap = intel_hdcp_get_capability(intel_connector); + hdcp2_cap = intel_hdcp2_get_capability(intel_connector); if (hdcp_cap) seq_puts(m, "HDCP1.4 "); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 578763e202c0..b6f86129c0bc 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -498,15 +498,15 @@ struct intel_hdcp_shim { struct intel_connector *connector); /* Detects panel's hdcp capability. This is optional for HDMI. */ - int (*hdcp_capable)(struct intel_digital_port *dig_port, - bool *hdcp_capable); + int (*hdcp_get_capability)(struct intel_digital_port *dig_port, + bool *hdcp_capable); /* HDCP adaptation(DP/HDMI) required on the port */ enum hdcp_wired_protocol protocol; /* Detects whether sink is HDCP2.2 capable */ - int (*hdcp_2_2_capable)(struct intel_connector *connector, - bool *capable); + int (*hdcp_2_2_get_capability)(struct intel_connector *connector, + bool *capable); /* Write HDCP2.2 messages */ int (*write_2_2_msg)(struct intel_connector *connector, diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 5394391acbe1..bf90e9024feb 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -269,8 +269,8 @@ bool intel_dp_hdcp_check_link(struct intel_digital_port *dig_port, } static -int intel_dp_hdcp_capable(struct intel_digital_port *dig_port, - bool *hdcp_capable) +int intel_dp_hdcp_get_capability(struct intel_digital_port *dig_port, + bool *hdcp_capable) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); ssize_t ret; @@ -642,8 +642,8 @@ int intel_dp_hdcp2_check_link(struct intel_digital_port *dig_port, } static -int _intel_dp_hdcp2_capable(struct drm_dp_aux *aux, - bool *capable) +int _intel_dp_hdcp2_get_capability(struct drm_dp_aux *aux, + bool *capable) { u8 rx_caps[3]; int ret; @@ -663,13 +663,13 @@ int _intel_dp_hdcp2_capable(struct drm_dp_aux *aux, } static -int intel_dp_hdcp2_capable(struct intel_connector *connector, - bool *capable) +int intel_dp_hdcp2_get_capability(struct intel_connector *connector, + bool *capable) { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct drm_dp_aux *aux = &dig_port->dp.aux; - return _intel_dp_hdcp2_capable(aux, capable); + return _intel_dp_hdcp2_get_capability(aux, capable); } static const struct intel_hdcp_shim intel_dp_hdcp_shim = { @@ -683,12 +683,12 @@ static const struct intel_hdcp_shim intel_dp_hdcp_shim = { .read_v_prime_part = intel_dp_hdcp_read_v_prime_part, .toggle_signalling = intel_dp_hdcp_toggle_signalling, .check_link = intel_dp_hdcp_check_link, - .hdcp_capable = intel_dp_hdcp_capable, + .hdcp_get_capability = intel_dp_hdcp_get_capability, .write_2_2_msg = intel_dp_hdcp2_write_msg, .read_2_2_msg = intel_dp_hdcp2_read_msg, .config_stream_type = intel_dp_hdcp2_config_stream_type, .check_2_2_link = intel_dp_hdcp2_check_link, - .hdcp_2_2_capable = intel_dp_hdcp2_capable, + .hdcp_2_2_get_capability = intel_dp_hdcp2_get_capability, .protocol = HDCP_PROTOCOL_DP, }; @@ -813,13 +813,13 @@ static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = { .toggle_signalling = intel_dp_hdcp_toggle_signalling, .stream_encryption = intel_dp_mst_hdcp_stream_encryption, .check_link = intel_dp_hdcp_check_link, - .hdcp_capable = intel_dp_hdcp_capable, + .hdcp_get_capability = intel_dp_hdcp_get_capability, .write_2_2_msg = intel_dp_hdcp2_write_msg, .read_2_2_msg = intel_dp_hdcp2_read_msg, .config_stream_type = intel_dp_hdcp2_config_stream_type, .stream_2_2_encryption = intel_dp_mst_hdcp2_stream_encryption, .check_2_2_link = intel_dp_mst_hdcp2_check_link, - .hdcp_2_2_capable = intel_dp_hdcp2_capable, + .hdcp_2_2_get_capability = intel_dp_hdcp2_get_capability, .protocol = HDCP_PROTOCOL_DP, }; diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 4593ac10e2fa..c1a32f9f1199 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -140,7 +140,7 @@ int intel_hdcp_read_valid_bksv(struct intel_digital_port *dig_port, } /* Is HDCP1.4 capable on Platform and Sink */ -bool intel_hdcp_capable(struct intel_connector *connector) +bool intel_hdcp_get_capability(struct intel_connector *connector) { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); const struct intel_hdcp_shim *shim = connector->hdcp.shim; @@ -150,8 +150,8 @@ bool intel_hdcp_capable(struct intel_connector *connector) if (!shim) return capable; - if (shim->hdcp_capable) { - shim->hdcp_capable(dig_port, &capable); + if (shim->hdcp_get_capability) { + shim->hdcp_get_capability(dig_port, &capable); } else { if (!intel_hdcp_read_valid_bksv(dig_port, shim, bksv)) capable = true; @@ -191,7 +191,7 @@ static bool intel_hdcp2_prerequisite(struct intel_connector *connector) } /* Is HDCP2.2 capable on Platform and Sink */ -bool intel_hdcp2_capable(struct intel_connector *connector) +bool intel_hdcp2_get_capability(struct intel_connector *connector) { struct intel_hdcp *hdcp = &connector->hdcp; bool capable = false; @@ -200,7 +200,7 @@ bool intel_hdcp2_capable(struct intel_connector *connector) return false; /* Sink's capability for HDCP2.2 */ - hdcp->shim->hdcp_2_2_capable(connector, &capable); + hdcp->shim->hdcp_2_2_get_capability(connector, &capable); return capable; } @@ -740,8 +740,8 @@ static int intel_hdcp_auth(struct intel_connector *connector) * whether the display supports HDCP before we write An. For HDMI * displays, this is not necessary. */ - if (shim->hdcp_capable) { - ret = shim->hdcp_capable(dig_port, &hdcp_capable); + if (shim->hdcp_get_capability) { + ret = shim->hdcp_get_capability(dig_port, &hdcp_capable); if (ret) return ret; if (!hdcp_capable) { @@ -2388,7 +2388,7 @@ static int _intel_hdcp_enable(struct intel_atomic_state *state, * Considering that HDCP2.2 is more secure than HDCP1.4, If the setup * is capable of HDCP2.2, it is preferred to use HDCP2.2. */ - if (intel_hdcp2_capable(connector)) { + if (intel_hdcp2_get_capability(connector)) { ret = intel_hdcp_set_streams(dig_port, state); if (!ret) { ret = _intel_hdcp2_enable(connector); @@ -2406,7 +2406,7 @@ static int _intel_hdcp_enable(struct intel_atomic_state *state, * When HDCP2.2 fails and Content Type is not Type1, HDCP1.4 will * be attempted. */ - if (ret && intel_hdcp_capable(connector) && + if (ret && intel_hdcp_get_capability(connector) && hdcp->content_type != DRM_MODE_HDCP_CONTENT_TYPE1) { ret = intel_hdcp1_enable(connector); } diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h index a9c784fd9ba5..aeefb3c13d2c 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.h +++ b/drivers/gpu/drm/i915/display/intel_hdcp.h @@ -38,8 +38,8 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); bool is_hdcp_supported(struct drm_i915_private *i915, enum port port); -bool intel_hdcp_capable(struct intel_connector *connector); -bool intel_hdcp2_capable(struct intel_connector *connector); +bool intel_hdcp_get_capability(struct intel_connector *connector); +bool intel_hdcp2_get_capability(struct intel_connector *connector); void intel_hdcp_component_init(struct drm_i915_private *i915); void intel_hdcp_component_fini(struct drm_i915_private *i915); void intel_hdcp_cleanup(struct intel_connector *connector); diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 7020e5806109..90d2236fede3 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -1732,8 +1732,8 @@ int intel_hdmi_hdcp2_check_link(struct intel_digital_port *dig_port, } static -int intel_hdmi_hdcp2_capable(struct intel_connector *connector, - bool *capable) +int intel_hdmi_hdcp2_get_capability(struct intel_connector *connector, + bool *capable) { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); u8 hdcp2_version; @@ -1762,7 +1762,7 @@ static const struct intel_hdcp_shim intel_hdmi_hdcp_shim = { .write_2_2_msg = intel_hdmi_hdcp2_write_msg, .read_2_2_msg = intel_hdmi_hdcp2_read_msg, .check_2_2_link = intel_hdmi_hdcp2_check_link, - .hdcp_2_2_capable = intel_hdmi_hdcp2_capable, + .hdcp_2_2_get_capability = intel_hdmi_hdcp2_get_capability, .protocol = HDCP_PROTOCOL_HDMI, }; From 813cca96e4aca3dbbac22623b70628c6be7d8e38 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 26 Feb 2024 10:40:18 +0530 Subject: [PATCH 62/90] drm/i915/hdcp: Add new remote capability check shim function Create a remote HDCP capability shim function which can read the remote monitor HDCP capability when in MST configuration. --v2 -Add an assertion to make sure only mst encoder call this remote_cap function [Ankit] --v3 -rename remote_hdcp_cap to remote_hdcp_capability [Jani] --v4 -fix hdcp2_prerequisite check condition -Move intel_dp_hdcp_get_remote_capability to dp_mst shim instead of having it in dp shim [Ankit] Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240226051017.1652970-2-suraj.kandpal@intel.com --- .../drm/i915/display/intel_display_types.h | 4 +++ drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 27 +++++++++++++++++++ drivers/gpu/drm/i915/display/intel_hdcp.c | 16 +++++++++++ drivers/gpu/drm/i915/display/intel_hdcp.h | 3 +++ 4 files changed, 50 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b6f86129c0bc..8ce986fadd9a 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -531,6 +531,10 @@ struct intel_hdcp_shim { /* HDCP2.2 Link Integrity Check */ int (*check_2_2_link)(struct intel_digital_port *dig_port, struct intel_connector *connector); + + /* HDCP remote sink cap */ + int (*get_remote_hdcp_capability)(struct intel_connector *connector, + bool *hdcp_capable, bool *hdcp2_capable); }; struct intel_hdcp { diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index bf90e9024feb..eab6e9fab4e6 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -672,6 +672,32 @@ int intel_dp_hdcp2_get_capability(struct intel_connector *connector, return _intel_dp_hdcp2_get_capability(aux, capable); } +static +int intel_dp_hdcp_get_remote_capability(struct intel_connector *connector, + bool *hdcp_capable, + bool *hdcp2_capable) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + struct drm_dp_aux *aux = &connector->port->aux; + u8 bcaps; + int ret; + + if (!intel_encoder_is_mst(connector->encoder)) + return -EINVAL; + + ret = _intel_dp_hdcp2_get_capability(aux, hdcp2_capable); + if (ret) + return ret; + + ret = intel_dp_hdcp_read_bcaps(aux, i915, &bcaps); + if (ret) + return ret; + + *hdcp_capable = bcaps & DP_BCAPS_HDCP_CAPABLE; + + return 0; +} + static const struct intel_hdcp_shim intel_dp_hdcp_shim = { .write_an_aksv = intel_dp_hdcp_write_an_aksv, .read_bksv = intel_dp_hdcp_read_bksv, @@ -820,6 +846,7 @@ static const struct intel_hdcp_shim intel_dp_mst_hdcp_shim = { .stream_2_2_encryption = intel_dp_mst_hdcp2_stream_encryption, .check_2_2_link = intel_dp_mst_hdcp2_check_link, .hdcp_2_2_get_capability = intel_dp_hdcp2_get_capability, + .get_remote_hdcp_capability = intel_dp_hdcp_get_remote_capability, .protocol = HDCP_PROTOCOL_DP, }; diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index c1a32f9f1199..801b8f0495bb 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -205,6 +205,22 @@ bool intel_hdcp2_get_capability(struct intel_connector *connector) return capable; } +void intel_hdcp_get_remote_capability(struct intel_connector *connector, + bool *hdcp_capable, + bool *hdcp2_capable) +{ + struct intel_hdcp *hdcp = &connector->hdcp; + + if (!hdcp->shim->get_remote_hdcp_capability) + return; + + hdcp->shim->get_remote_hdcp_capability(connector, hdcp_capable, + hdcp2_capable); + + if (!intel_hdcp2_prerequisite(connector)) + *hdcp2_capable = false; +} + static bool intel_hdcp_in_use(struct drm_i915_private *i915, enum transcoder cpu_transcoder, enum port port) { diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h index aeefb3c13d2c..477f2d2bb120 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.h +++ b/drivers/gpu/drm/i915/display/intel_hdcp.h @@ -40,6 +40,9 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state, bool is_hdcp_supported(struct drm_i915_private *i915, enum port port); bool intel_hdcp_get_capability(struct intel_connector *connector); bool intel_hdcp2_get_capability(struct intel_connector *connector); +void intel_hdcp_get_remote_capability(struct intel_connector *connector, + bool *hdcp_capable, + bool *hdcp2_capable); void intel_hdcp_component_init(struct drm_i915_private *i915); void intel_hdcp_component_fini(struct drm_i915_private *i915); void intel_hdcp_cleanup(struct intel_connector *connector); From dfaf305b5c42185a4780aad58afa5a76421890e1 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 23 Feb 2024 13:44:48 +0530 Subject: [PATCH 63/90] drm/i915/hdcp: HDCP Capability for the downstream device Currently we are only checking capability of remote device and not immediate downstream device but during capability check we need are concerned with only the HDCP capability of downstream device. During i915_display_info reporting we need HDCP Capability for both the monitors and downstream branch device if any this patch adds that. --v2 -Use MST Hub HDCP version [Ankit] --v3 -Redefined how we seprate remote and direct read to make sure HDMI shim functions are not touched [Ankit] --v4 - Fix the conditions so that hdcp_info with remote_req true is sent only when encoder is mst [Ankit] --v5 -No need to have the MST Hub version in i915_hdcp_sink_capability[Ankit] Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240223081453.1576918-9-suraj.kandpal@intel.com --- .../drm/i915/display/intel_display_debugfs.c | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index d8f1a23ac2b1..b99c024b0934 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -188,7 +188,8 @@ static void intel_panel_info(struct seq_file *m, } static void intel_hdcp_info(struct seq_file *m, - struct intel_connector *intel_connector) + struct intel_connector *intel_connector, + bool remote_req) { bool hdcp_cap, hdcp2_cap; @@ -197,8 +198,14 @@ static void intel_hdcp_info(struct seq_file *m, goto out; } - hdcp_cap = intel_hdcp_get_capability(intel_connector); - hdcp2_cap = intel_hdcp2_get_capability(intel_connector); + if (remote_req) { + intel_hdcp_get_remote_capability(intel_connector, + &hdcp_cap, + &hdcp2_cap); + } else { + hdcp_cap = intel_hdcp_get_capability(intel_connector); + hdcp2_cap = intel_hdcp2_get_capability(intel_connector); + } if (hdcp_cap) seq_puts(m, "HDCP1.4 "); @@ -285,7 +292,11 @@ static void intel_connector_info(struct seq_file *m, } seq_puts(m, "\tHDCP version: "); - intel_hdcp_info(m, intel_connector); + if (intel_encoder_is_mst(encoder)) { + intel_hdcp_info(m, intel_connector, true); + seq_puts(m, "\tMST Hub HDCP version: "); + } + intel_hdcp_info(m, intel_connector, false); seq_printf(m, "\tmax bpc: %u\n", connector->display_info.bpc); @@ -1131,7 +1142,7 @@ static int i915_hdcp_sink_capability_show(struct seq_file *m, void *data) seq_printf(m, "%s:%d HDCP version: ", connector->base.name, connector->base.base.id); - intel_hdcp_info(m, connector); + intel_hdcp_info(m, connector, false); out: drm_modeset_unlock(&i915->drm.mode_config.connection_mutex); From 429ccbd1c39baefc6114b482ae98c188f007afcd Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Fri, 23 Feb 2024 13:44:49 +0530 Subject: [PATCH 64/90] drm/i915/hdcp: Remove additional timing for reading mst hdcp message Now that we have moved back to direct reads the additional timing is not required hence this can be removed. --v2 -Add Fixes tag [Ankit] Fixes: 3974f9c17bb9 ("drm/i915/hdcp: Adjust timeout for read in DPMST Scenario") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240223081453.1576918-10-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index eab6e9fab4e6..16ed489e09ec 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -555,13 +555,8 @@ int intel_dp_hdcp2_read_msg(struct intel_connector *connector, /* Entire msg read timeout since initiate of msg read */ if (bytes_to_recv == size - 1 && hdcp2_msg_data->msg_read_timeout > 0) { - if (intel_encoder_is_mst(connector->encoder)) - msg_end = ktime_add_ms(ktime_get_raw(), - hdcp2_msg_data->msg_read_timeout * - connector->port->parent->num_ports); - else - msg_end = ktime_add_ms(ktime_get_raw(), - hdcp2_msg_data->msg_read_timeout); + msg_end = ktime_add_ms(ktime_get_raw(), + hdcp2_msg_data->msg_read_timeout); } ret = drm_dp_dpcd_read(aux, offset, From 909fff3e46c08eb6fcbb52e7a49dfb359007ae79 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 26 Feb 2024 12:00:48 +0530 Subject: [PATCH 65/90] drm/i915/hdcp: Extract hdcp structure from correct connector Currently intel_hdcp is not being extracted from primary connector this patch fixes that. Fixes: 524240b231ea ("drm/i915/hdcp: Propagate aux info in DP HDCP functions") Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240226063051.1685326-3-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 16ed489e09ec..99220f970039 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -395,7 +395,9 @@ intel_dp_hdcp2_wait_for_msg(struct intel_connector *connector, const struct hdcp2_dp_msg_data *hdcp2_msg_data) { struct drm_i915_private *i915 = to_i915(connector->base.dev); - struct intel_hdcp *hdcp = &connector->hdcp; + struct intel_digital_port *dig_port = intel_attached_dig_port(connector); + struct intel_dp *dp = &dig_port->dp; + struct intel_hdcp *hdcp = &dp->attached_connector->hdcp; u8 msg_id = hdcp2_msg_data->msg_id; int ret, timeout; bool msg_ready = false; @@ -511,8 +513,9 @@ int intel_dp_hdcp2_read_msg(struct intel_connector *connector, { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - struct intel_hdcp *hdcp = &connector->hdcp; struct drm_dp_aux *aux = &dig_port->dp.aux; + struct intel_dp *dp = &dig_port->dp; + struct intel_hdcp *hdcp = &dp->attached_connector->hdcp; unsigned int offset; u8 *byte = buf; ssize_t ret, bytes_to_recv, len; From 47ef55a8b7846e7f4924cab10c6640ee681ce4e4 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 26 Feb 2024 12:00:49 +0530 Subject: [PATCH 66/90] drm/i915/hdcp: Don't enable HDCP2.2 directly from check_link Whenever LIC fails instead of moving from ENABLED to DESIRED CP property we directly enable HDCP2.2 without informing the userspace of this failure in link integrity check. Now we will just update the value to DESIRED send the event to userspace and then continue with the normal flow of HDCP enablement. --v2 -Don't change the function prototype in this function [Ankit] Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240226063051.1685326-4-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_hdcp.c | 25 ++--------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 801b8f0495bb..1023153ba9d4 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -2068,17 +2068,6 @@ static int intel_hdcp2_check_link(struct intel_connector *connector) drm_dbg_kms(&i915->drm, "HDCP2.2 Downstream topology change\n"); - ret = hdcp2_authenticate_repeater_topology(connector); - if (!ret) { - intel_hdcp_update_value(connector, - DRM_MODE_CONTENT_PROTECTION_ENABLED, - true); - goto out; - } - drm_dbg_kms(&i915->drm, - "[CONNECTOR:%d:%s] Repeater topology auth failed.(%d)\n", - connector->base.base.id, connector->base.name, - ret); } else { drm_dbg_kms(&i915->drm, "[CONNECTOR:%d:%s] HDCP2.2 link failed, retrying auth\n", @@ -2095,18 +2084,8 @@ static int intel_hdcp2_check_link(struct intel_connector *connector) goto out; } - ret = _intel_hdcp2_enable(connector); - if (ret) { - drm_dbg_kms(&i915->drm, - "[CONNECTOR:%d:%s] Failed to enable hdcp2.2 (%d)\n", - connector->base.base.id, connector->base.name, - ret); - intel_hdcp_update_value(connector, - DRM_MODE_CONTENT_PROTECTION_DESIRED, - true); - goto out; - } - + intel_hdcp_update_value(connector, + DRM_MODE_CONTENT_PROTECTION_DESIRED, true); out: mutex_unlock(&dig_port->hdcp_mutex); mutex_unlock(&hdcp->mutex); From 483f7d94a0453564ad9295288c0242136c5f36a0 Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 26 Feb 2024 12:00:50 +0530 Subject: [PATCH 67/90] drm/i915/hdcp: Don't enable HDCP1.4 directly from check_link Whenever LIC fails instead of moving from ENABLED to DESIRED CP property we directly enable HDCP1.4 without informing the userspace of this failure in link integrity check. Now we will just update the value to DESIRED send the event to userspace and then continue with the normal flow of HDCP enablement. Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240226063051.1685326-5-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_hdcp.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 1023153ba9d4..321c6dd8dbf5 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -1088,15 +1088,9 @@ static int intel_hdcp_check_link(struct intel_connector *connector) goto out; } - ret = intel_hdcp1_enable(connector); - if (ret) { - drm_err(&i915->drm, "Failed to enable hdcp (%d)\n", ret); - intel_hdcp_update_value(connector, - DRM_MODE_CONTENT_PROTECTION_DESIRED, - true); - goto out; - } - + intel_hdcp_update_value(connector, + DRM_MODE_CONTENT_PROTECTION_DESIRED, + true); out: mutex_unlock(&dig_port->hdcp_mutex); mutex_unlock(&hdcp->mutex); From 66bd692404a6284ee2cf85968a4018cf212398be Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 26 Feb 2024 12:00:51 +0530 Subject: [PATCH 68/90] drm/i915/hdcp: Allocate stream id after HDCP AKE stage Allocate stream id after HDCP AKE stage and not before so that it can also be done during link integrity check. Right now for MST scenarios LIC fails after hdcp enablement for this reason. --v2 -no need for else block in prepare_streams function [Ankit] --v3 -remove intel_hdcp argument from required_content_stream function [Ankit] Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240226063051.1685326-6-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_hdcp.c | 137 ++++++++++------------ 1 file changed, 65 insertions(+), 72 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 321c6dd8dbf5..9edac27bab26 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -30,7 +30,7 @@ #define KEY_LOAD_TRIES 5 #define HDCP2_LC_RETRY_CNT 3 -static int intel_conn_to_vcpi(struct drm_atomic_state *state, +static int intel_conn_to_vcpi(struct intel_atomic_state *state, struct intel_connector *connector) { struct drm_dp_mst_topology_mgr *mgr; @@ -43,7 +43,7 @@ static int intel_conn_to_vcpi(struct drm_atomic_state *state, return 0; mgr = connector->port->mgr; - drm_modeset_lock(&mgr->base.lock, state->acquire_ctx); + drm_modeset_lock(&mgr->base.lock, state->base.acquire_ctx); mst_state = to_drm_dp_mst_topology_state(mgr->base.state); payload = drm_atomic_get_mst_payload_state(mst_state, connector->port); if (drm_WARN_ON(mgr->dev, !payload)) @@ -68,19 +68,51 @@ static int intel_conn_to_vcpi(struct drm_atomic_state *state, * DP MST topology. Though it is not compulsory, security fw should change its * policy to mark different content_types for different streams. */ -static void -intel_hdcp_required_content_stream(struct intel_digital_port *dig_port) +static int +intel_hdcp_required_content_stream(struct intel_atomic_state *state, + struct intel_digital_port *dig_port) { + struct drm_connector_list_iter conn_iter; + struct intel_digital_port *conn_dig_port; + struct intel_connector *connector; + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct hdcp_port_data *data = &dig_port->hdcp_port_data; bool enforce_type0 = false; int k; if (dig_port->hdcp_auth_status) - return; + return 0; + + data->k = 0; if (!dig_port->hdcp_mst_type1_capable) enforce_type0 = true; + drm_connector_list_iter_begin(&i915->drm, &conn_iter); + for_each_intel_connector_iter(connector, &conn_iter) { + if (connector->base.status == connector_status_disconnected) + continue; + + if (!intel_encoder_is_mst(intel_attached_encoder(connector))) + continue; + + conn_dig_port = intel_attached_dig_port(connector); + if (conn_dig_port != dig_port) + continue; + + data->streams[data->k].stream_id = + intel_conn_to_vcpi(state, connector); + data->k++; + + /* if there is only one active stream */ + if (dig_port->dp.active_mst_links <= 1) + break; + } + drm_connector_list_iter_end(&conn_iter); + + if (drm_WARN_ON(&i915->drm, data->k > INTEL_NUM_PIPES(i915) || data->k == 0)) + return -EINVAL; + /* * Apply common protection level across all streams in DP MST Topology. * Use highest supported content type for all streams in DP MST Topology. @@ -88,19 +120,25 @@ intel_hdcp_required_content_stream(struct intel_digital_port *dig_port) for (k = 0; k < data->k; k++) data->streams[k].stream_type = enforce_type0 ? DRM_MODE_HDCP_CONTENT_TYPE0 : DRM_MODE_HDCP_CONTENT_TYPE1; + + return 0; } -static void intel_hdcp_prepare_streams(struct intel_connector *connector) +static int intel_hdcp_prepare_streams(struct intel_atomic_state *state, + struct intel_connector *connector) { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct hdcp_port_data *data = &dig_port->hdcp_port_data; struct intel_hdcp *hdcp = &connector->hdcp; - if (!intel_encoder_is_mst(intel_attached_encoder(connector))) { - data->streams[0].stream_type = hdcp->content_type; - } else { - intel_hdcp_required_content_stream(dig_port); - } + if (intel_encoder_is_mst(intel_attached_encoder(connector))) + return intel_hdcp_required_content_stream(state, dig_port); + + data->k = 1; + data->streams[0].stream_id = 0; + data->streams[0].stream_type = hdcp->content_type; + + return 0; } static @@ -1895,7 +1933,8 @@ hdcp2_propagate_stream_management_info(struct intel_connector *connector) return ret; } -static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector) +static int hdcp2_authenticate_and_encrypt(struct intel_atomic_state *state, + struct intel_connector *connector) { struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct drm_i915_private *i915 = to_i915(connector->base.dev); @@ -1904,7 +1943,13 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector) for (i = 0; i < tries && !dig_port->hdcp_auth_status; i++) { ret = hdcp2_authenticate_sink(connector); if (!ret) { - intel_hdcp_prepare_streams(connector); + ret = intel_hdcp_prepare_streams(state, connector); + if (ret) { + drm_dbg_kms(&i915->drm, + "Prepare stream failed.(%d)\n", + ret); + break; + } ret = hdcp2_propagate_stream_management_info(connector); if (ret) { @@ -1949,7 +1994,8 @@ static int hdcp2_authenticate_and_encrypt(struct intel_connector *connector) return ret; } -static int _intel_hdcp2_enable(struct intel_connector *connector) +static int _intel_hdcp2_enable(struct intel_atomic_state *state, + struct intel_connector *connector) { struct drm_i915_private *i915 = to_i915(connector->base.dev); struct intel_hdcp *hdcp = &connector->hdcp; @@ -1959,7 +2005,7 @@ static int _intel_hdcp2_enable(struct intel_connector *connector) connector->base.base.id, connector->base.name, hdcp->content_type); - ret = hdcp2_authenticate_and_encrypt(connector); + ret = hdcp2_authenticate_and_encrypt(state, connector); if (ret) { drm_dbg_kms(&i915->drm, "HDCP2 Type%d Enabling Failed. (%d)\n", hdcp->content_type, ret); @@ -2287,52 +2333,6 @@ int intel_hdcp_init(struct intel_connector *connector, return 0; } -static int -intel_hdcp_set_streams(struct intel_digital_port *dig_port, - struct intel_atomic_state *state) -{ - struct drm_connector_list_iter conn_iter; - struct intel_digital_port *conn_dig_port; - struct intel_connector *connector; - struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); - struct hdcp_port_data *data = &dig_port->hdcp_port_data; - - if (!intel_encoder_is_mst(&dig_port->base)) { - data->k = 1; - data->streams[0].stream_id = 0; - return 0; - } - - data->k = 0; - - drm_connector_list_iter_begin(&i915->drm, &conn_iter); - for_each_intel_connector_iter(connector, &conn_iter) { - if (connector->base.status == connector_status_disconnected) - continue; - - if (!intel_encoder_is_mst(intel_attached_encoder(connector))) - continue; - - conn_dig_port = intel_attached_dig_port(connector); - if (conn_dig_port != dig_port) - continue; - - data->streams[data->k].stream_id = - intel_conn_to_vcpi(&state->base, connector); - data->k++; - - /* if there is only one active stream */ - if (dig_port->dp.active_mst_links <= 1) - break; - } - drm_connector_list_iter_end(&conn_iter); - - if (drm_WARN_ON(&i915->drm, data->k > INTEL_NUM_PIPES(i915) || data->k == 0)) - return -EINVAL; - - return 0; -} - static int _intel_hdcp_enable(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *pipe_config, @@ -2378,17 +2378,10 @@ static int _intel_hdcp_enable(struct intel_atomic_state *state, * is capable of HDCP2.2, it is preferred to use HDCP2.2. */ if (intel_hdcp2_get_capability(connector)) { - ret = intel_hdcp_set_streams(dig_port, state); - if (!ret) { - ret = _intel_hdcp2_enable(connector); - if (!ret) - check_link_interval = - DRM_HDCP2_CHECK_PERIOD_MS; - } else { - drm_dbg_kms(&i915->drm, - "Set content streams failed: (%d)\n", - ret); - } + ret = _intel_hdcp2_enable(state, connector); + if (!ret) + check_link_interval = + DRM_HDCP2_CHECK_PERIOD_MS; } /* From 610d7bc2e23c83024cd2c943bf91acb83b7e17fd Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 26 Feb 2024 12:00:52 +0530 Subject: [PATCH 69/90] drm/i915/hdcp: Read Rxcaps for robustibility We see some monitors and docks report incorrect hdcp version and capability in first few reads so we read rx_caps three times before we conclude the monitor's or docks HDCP capability --v2 -Add comment to justify the 3 time read loop for hdcp capability[Ankit] Signed-off-by: Suraj Kandpal Reviewed-by: Ankit Nautiyal Signed-off-by: Ankit Nautiyal Link: https://patchwork.freedesktop.org/patch/msgid/20240226063051.1685326-7-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_dp_hdcp.c | 29 ++++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c index 99220f970039..b98a87883fef 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_dp_hdcp.c @@ -644,18 +644,29 @@ int _intel_dp_hdcp2_get_capability(struct drm_dp_aux *aux, bool *capable) { u8 rx_caps[3]; - int ret; + int ret, i; *capable = false; - ret = drm_dp_dpcd_read(aux, - DP_HDCP_2_2_REG_RX_CAPS_OFFSET, - rx_caps, HDCP_2_2_RXCAPS_LEN); - if (ret != HDCP_2_2_RXCAPS_LEN) - return ret >= 0 ? -EIO : ret; - if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL && - HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2])) - *capable = true; + /* + * Some HDCP monitors act really shady by not giving the correct hdcp + * capability on the first rx_caps read and usually take an extra read + * to give the capability. We read rx_caps three times before we + * declare a monitor not capable of HDCP 2.2. + */ + for (i = 0; i < 3; i++) { + ret = drm_dp_dpcd_read(aux, + DP_HDCP_2_2_REG_RX_CAPS_OFFSET, + rx_caps, HDCP_2_2_RXCAPS_LEN); + if (ret != HDCP_2_2_RXCAPS_LEN) + return ret >= 0 ? -EIO : ret; + + if (rx_caps[0] == HDCP_2_2_RX_CAPS_VERSION_VAL && + HDCP_2_2_DP_HDCP_CAPABLE(rx_caps[2])) { + *capable = true; + break; + } + } return 0; } From 1e59ab501abac4fd664de143485be99b341bc78f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 26 Feb 2024 20:52:43 +0200 Subject: [PATCH 70/90] drm/dp: Add drm_dp_max_dprx_data_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Copy intel_dp_max_data_rate() to DRM core. It will be needed by a follow-up DP tunnel patch, checking the maximum rate the DPRX (sink) supports. Accordingly use the drm_dp_max_dprx_data_rate() name for clarity. This patchset will also switch calling the new DRM function in i915 instead of intel_dp_max_data_rate(). While at it simplify the function documentation/comments, removing parts described already by drm_dp_bw_channel_coding_efficiency(). v2: (Ville) - Remove max_link_rate_kbps. - Simplify the function documentation. v3: - Rebased on latest drm-tip. Cc: Ville Syrjälä Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240226185246.1276018-1-imre.deak@intel.com --- drivers/gpu/drm/display/drm_dp_helper.c | 30 +++++++++++++++++++++++++ include/drm/display/drm_dp_helper.h | 1 + 2 files changed, 31 insertions(+) diff --git a/drivers/gpu/drm/display/drm_dp_helper.c b/drivers/gpu/drm/display/drm_dp_helper.c index d72b6f9a352c..d9f8581a5a5f 100644 --- a/drivers/gpu/drm/display/drm_dp_helper.c +++ b/drivers/gpu/drm/display/drm_dp_helper.c @@ -4059,3 +4059,33 @@ int drm_dp_bw_channel_coding_efficiency(bool is_uhbr) return 800000; } EXPORT_SYMBOL(drm_dp_bw_channel_coding_efficiency); + +/** + * drm_dp_max_dprx_data_rate - Get the max data bandwidth of a DPRX sink + * @max_link_rate: max DPRX link rate in 10kbps units + * @max_lanes: max DPRX lane count + * + * Given a link rate and lanes, get the data bandwidth. + * + * Data bandwidth is the actual payload rate, which depends on the data + * bandwidth efficiency and the link rate. + * + * Note that protocol layers above the DPRX link level considered here can + * further limit the maximum data rate. Such layers are the MST topology (with + * limits on the link between the source and first branch device as well as on + * the whole MST path until the DPRX link) and (Thunderbolt) DP tunnels - + * which in turn can encapsulate an MST link with its own limit - with each + * SST or MST encapsulated tunnel sharing the BW of a tunnel group. + * + * Returns the maximum data rate in kBps units. + */ +int drm_dp_max_dprx_data_rate(int max_link_rate, int max_lanes) +{ + int ch_coding_efficiency = + drm_dp_bw_channel_coding_efficiency(drm_dp_is_uhbr_rate(max_link_rate)); + + return DIV_ROUND_DOWN_ULL(mul_u32_u32(max_link_rate * 10 * max_lanes, + ch_coding_efficiency), + 1000000 * 8); +} +EXPORT_SYMBOL(drm_dp_max_dprx_data_rate); diff --git a/include/drm/display/drm_dp_helper.h b/include/drm/display/drm_dp_helper.h index 863b2e7add29..aff7f02971db 100644 --- a/include/drm/display/drm_dp_helper.h +++ b/include/drm/display/drm_dp_helper.h @@ -812,5 +812,6 @@ int drm_dp_bw_overhead(int lane_count, int hactive, int dsc_slice_count, int bpp_x16, unsigned long flags); int drm_dp_bw_channel_coding_efficiency(bool is_uhbr); +int drm_dp_max_dprx_data_rate(int max_link_rate, int max_lanes); #endif /* _DRM_DP_HELPER_H_ */ From 295654f7e554a9f089bdab0b2bb9a9aad7c402c0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 26 Feb 2024 20:52:44 +0200 Subject: [PATCH 71/90] drm/dp: Add support for DP tunneling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Display Port tunneling. For now this includes the support for Bandwidth Allocation Mode (BWA), leaving adding Panel Replay support for later. BWA allows using displays that share the same (Thunderbolt) link with their maximum resolution. Atm, this may not be possible due to the coarse granularity of partitioning the link BW among the displays on the link: the BW allocation policy is in a SW/FW/HW component on the link (on Thunderbolt it's the SW or FW Connection Manager), independent of the driver. This policy will set the DPRX maximum rate and lane count DPCD registers the GFX driver will see (0x00000, 0x00001, 0x02200, 0x02201) based on the available link BW. The granularity of the current BW allocation policy is coarse, based on the required link rate in the 1.62Gbs..8.1Gbps range and it may prevent using higher resolutions all together: the display connected first will get a share of the link BW which corresponds to its full DPRX capability (regardless of the actual mode it uses). A subsequent display connected will only get the remaining BW, which could be well below its full capability. BWA solves the above coarse granularity (reducing it to a 250Mbs..1Gps range) and first-come/first-served issues by letting the driver request the BW for each display on a link which reflects the actual modes the displays use. This patch adds the DRM core helper functions, while a follow-up change in the patchset takes them into use in the i915 driver. v2: - Fix prepare_to_wait vs. wake-up cond check order in allocate_tunnel_bw(). (Ville) - Move tunnel==NULL checks from callers in drivers to here. (Ville) - Avoid var inits in declaration blocks that can fail or have side-effects. (Ville) - Use u8 for driver and group IDs. (Ville) - Simplify API removing drm_dp_tunnel_get/put_untracked(). (Ville) - Reuse str_yes_no() instead of a local yes_no_chr(). (Ville) - s/drm_dp_tunnel_atomic_clear_state()/free_tunnel_state() and unexport the function. (Ville) - s/clear_tunnel_group_state()/free_group_state() and move kfree() to this function. (Ville) - Add separate group_free_bw() helper and describe what the tunnel estimated BW includes. (Ville) - Improve help text for CONFIG_DRM_DISPLAY_DP_TUNNEL. (Ville) - Add code comment explaining the purpose of DPCD reg read helpers. (Ville) - Add code comment describing the tunnel group name prefix format. (Ville) - Report the allocated BW as undetermined until the first allocation request. - Skip allocation requests matching the previous request. - Clear any stale BW request status flags before a new request. - Add missing error return check of drm_dp_tunnel_atomic_get_group_state() in drm_dp_tunnel_atomic_set_stream_bw(). - Add drm_dp_tunnel_get_allocated_bw(). - s/drm_dp_tunnel_atomic_get_tunnel_bw/drm_dp_tunnel_atomic_get_required_bw - Fix return value description in function doc of drm_dp_tunnel_detect(). - Add function documentation to all exported functions. v3: - Improve grouping of fields in drm_dp_tunnel_group struct. (Uma) - Fix validating the BW granularity DPCD reg value. (Uma) - Document return value of check_and_clear_status_change(). (Uma) - Fix resetting drm_dp_tunnel_ref::tunnel in drm_dp_tunnel_ref_put(). (Ville) - Allow for ALLOCATED_BW to change after a BWA enable/disable sequence. Cc: Ville Syrjälä Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240226185246.1276018-2-imre.deak@intel.com --- drivers/gpu/drm/display/Kconfig | 21 + drivers/gpu/drm/display/Makefile | 2 + drivers/gpu/drm/display/drm_dp_tunnel.c | 1949 +++++++++++++++++++++++ include/drm/display/drm_dp.h | 60 + include/drm/display/drm_dp_tunnel.h | 248 +++ 5 files changed, 2280 insertions(+) create mode 100644 drivers/gpu/drm/display/drm_dp_tunnel.c create mode 100644 include/drm/display/drm_dp_tunnel.h diff --git a/drivers/gpu/drm/display/Kconfig b/drivers/gpu/drm/display/Kconfig index 09712b88a5b8..c0f56888c328 100644 --- a/drivers/gpu/drm/display/Kconfig +++ b/drivers/gpu/drm/display/Kconfig @@ -17,6 +17,27 @@ config DRM_DISPLAY_DP_HELPER help DRM display helpers for DisplayPort. +config DRM_DISPLAY_DP_TUNNEL + bool + select DRM_DISPLAY_DP_HELPER + help + Enable support for DisplayPort tunnels. This allows drivers to use + DP tunnel features like the Bandwidth Allocation mode to maximize the + BW utilization for display streams on Thunderbolt links. + +config DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + bool "Enable debugging the DP tunnel state" + depends on REF_TRACKER + depends on DRM_DISPLAY_DP_TUNNEL + depends on DEBUG_KERNEL + depends on EXPERT + help + Enables debugging the DP tunnel manager's state, including the + consistency of all managed tunnels' reference counting and the state of + streams contained in tunnels. + + If in doubt, say "N". + config DRM_DISPLAY_HDCP_HELPER bool depends on DRM_DISPLAY_HELPER diff --git a/drivers/gpu/drm/display/Makefile b/drivers/gpu/drm/display/Makefile index 17ac4a1006a8..7ca61333c669 100644 --- a/drivers/gpu/drm/display/Makefile +++ b/drivers/gpu/drm/display/Makefile @@ -8,6 +8,8 @@ drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_HELPER) += \ drm_dp_helper.o \ drm_dp_mst_topology.o \ drm_dsc_helper.o +drm_display_helper-$(CONFIG_DRM_DISPLAY_DP_TUNNEL) += \ + drm_dp_tunnel.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDCP_HELPER) += drm_hdcp_helper.o drm_display_helper-$(CONFIG_DRM_DISPLAY_HDMI_HELPER) += \ drm_hdmi_helper.o \ diff --git a/drivers/gpu/drm/display/drm_dp_tunnel.c b/drivers/gpu/drm/display/drm_dp_tunnel.c new file mode 100644 index 000000000000..120e0de674c1 --- /dev/null +++ b/drivers/gpu/drm/display/drm_dp_tunnel.c @@ -0,0 +1,1949 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include +#include + +#include + +#include +#include +#include +#include +#include + +#define to_group(__private_obj) \ + container_of(__private_obj, struct drm_dp_tunnel_group, base) + +#define to_group_state(__private_state) \ + container_of(__private_state, struct drm_dp_tunnel_group_state, base) + +#define is_dp_tunnel_private_obj(__obj) \ + ((__obj)->funcs == &tunnel_group_funcs) + +#define for_each_new_group_in_state(__state, __new_group_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->num_private_objs; \ + (__i)++) \ + for_each_if ((__state)->private_objs[__i].ptr && \ + is_dp_tunnel_private_obj((__state)->private_objs[__i].ptr) && \ + ((__new_group_state) = \ + to_group_state((__state)->private_objs[__i].new_state), 1)) + +#define for_each_old_group_in_state(__state, __old_group_state, __i) \ + for ((__i) = 0; \ + (__i) < (__state)->num_private_objs; \ + (__i)++) \ + for_each_if ((__state)->private_objs[__i].ptr && \ + is_dp_tunnel_private_obj((__state)->private_objs[__i].ptr) && \ + ((__old_group_state) = \ + to_group_state((__state)->private_objs[__i].old_state), 1)) + +#define for_each_tunnel_in_group(__group, __tunnel) \ + list_for_each_entry(__tunnel, &(__group)->tunnels, node) + +#define for_each_tunnel_state(__group_state, __tunnel_state) \ + list_for_each_entry(__tunnel_state, &(__group_state)->tunnel_states, node) + +#define for_each_tunnel_state_safe(__group_state, __tunnel_state, __tunnel_state_tmp) \ + list_for_each_entry_safe(__tunnel_state, __tunnel_state_tmp, \ + &(__group_state)->tunnel_states, node) + +#define kbytes_to_mbits(__kbytes) \ + DIV_ROUND_UP((__kbytes) * 8, 1000) + +#define DPTUN_BW_ARG(__bw) ((__bw) < 0 ? (__bw) : kbytes_to_mbits(__bw)) + +#define __tun_prn(__tunnel, __level, __type, __fmt, ...) \ + drm_##__level##__type((__tunnel)->group->mgr->dev, \ + "[DPTUN %s][%s] " __fmt, \ + drm_dp_tunnel_name(__tunnel), \ + (__tunnel)->aux->name, ## \ + __VA_ARGS__) + +#define tun_dbg(__tunnel, __fmt, ...) \ + __tun_prn(__tunnel, dbg, _kms, __fmt, ## __VA_ARGS__) + +#define tun_dbg_stat(__tunnel, __err, __fmt, ...) do { \ + if (__err) \ + __tun_prn(__tunnel, dbg, _kms, __fmt " (Failed, err: %pe)\n", \ + ## __VA_ARGS__, ERR_PTR(__err)); \ + else \ + __tun_prn(__tunnel, dbg, _kms, __fmt " (Ok)\n", \ + ## __VA_ARGS__); \ +} while (0) + +#define tun_dbg_atomic(__tunnel, __fmt, ...) \ + __tun_prn(__tunnel, dbg, _atomic, __fmt, ## __VA_ARGS__) + +#define tun_grp_dbg(__group, __fmt, ...) \ + drm_dbg_kms((__group)->mgr->dev, \ + "[DPTUN %s] " __fmt, \ + drm_dp_tunnel_group_name(__group), ## \ + __VA_ARGS__) + +#define DP_TUNNELING_BASE DP_TUNNELING_OUI + +#define __DPTUN_REG_RANGE(__start, __size) \ + GENMASK_ULL((__start) + (__size) - 1, (__start)) + +#define DPTUN_REG_RANGE(__addr, __size) \ + __DPTUN_REG_RANGE((__addr) - DP_TUNNELING_BASE, (__size)) + +#define DPTUN_REG(__addr) DPTUN_REG_RANGE(__addr, 1) + +#define DPTUN_INFO_REG_MASK ( \ + DPTUN_REG_RANGE(DP_TUNNELING_OUI, DP_TUNNELING_OUI_BYTES) | \ + DPTUN_REG_RANGE(DP_TUNNELING_DEV_ID, DP_TUNNELING_DEV_ID_BYTES) | \ + DPTUN_REG(DP_TUNNELING_HW_REV) | \ + DPTUN_REG(DP_TUNNELING_SW_REV_MAJOR) | \ + DPTUN_REG(DP_TUNNELING_SW_REV_MINOR) | \ + DPTUN_REG(DP_TUNNELING_CAPABILITIES) | \ + DPTUN_REG(DP_IN_ADAPTER_INFO) | \ + DPTUN_REG(DP_USB4_DRIVER_ID) | \ + DPTUN_REG(DP_USB4_DRIVER_BW_CAPABILITY) | \ + DPTUN_REG(DP_IN_ADAPTER_TUNNEL_INFORMATION) | \ + DPTUN_REG(DP_BW_GRANULARITY) | \ + DPTUN_REG(DP_ESTIMATED_BW) | \ + DPTUN_REG(DP_ALLOCATED_BW) | \ + DPTUN_REG(DP_TUNNELING_MAX_LINK_RATE) | \ + DPTUN_REG(DP_TUNNELING_MAX_LANE_COUNT) | \ + DPTUN_REG(DP_DPTX_BW_ALLOCATION_MODE_CONTROL)) + +static const DECLARE_BITMAP(dptun_info_regs, 64) = { + DPTUN_INFO_REG_MASK & -1UL, +#if BITS_PER_LONG == 32 + DPTUN_INFO_REG_MASK >> 32, +#endif +}; + +struct drm_dp_tunnel_regs { + u8 buf[HWEIGHT64(DPTUN_INFO_REG_MASK)]; +}; + +struct drm_dp_tunnel_group; + +struct drm_dp_tunnel { + struct drm_dp_tunnel_group *group; + + struct list_head node; + + struct kref kref; + struct ref_tracker *tracker; + struct drm_dp_aux *aux; + char name[8]; + + int bw_granularity; + int estimated_bw; + int allocated_bw; + + int max_dprx_rate; + u8 max_dprx_lane_count; + + u8 adapter_id; + + bool bw_alloc_supported:1; + bool bw_alloc_enabled:1; + bool has_io_error:1; + bool destroyed:1; +}; + +struct drm_dp_tunnel_group_state; + +struct drm_dp_tunnel_state { + struct drm_dp_tunnel_group_state *group_state; + + struct drm_dp_tunnel_ref tunnel_ref; + + struct list_head node; + + u32 stream_mask; + int *stream_bw; +}; + +struct drm_dp_tunnel_group_state { + struct drm_private_state base; + + struct list_head tunnel_states; +}; + +struct drm_dp_tunnel_group { + struct drm_private_obj base; + struct drm_dp_tunnel_mgr *mgr; + + struct list_head tunnels; + + /* available BW including the allocated_bw of all tunnels in the group */ + int available_bw; + + u8 drv_group_id; + char name[8]; + + bool active:1; +}; + +struct drm_dp_tunnel_mgr { + struct drm_device *dev; + + int group_count; + struct drm_dp_tunnel_group *groups; + wait_queue_head_t bw_req_queue; + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + struct ref_tracker_dir ref_tracker; +#endif +}; + +/* + * The following helpers provide a way to read out the tunneling DPCD + * registers with a minimal amount of AUX transfers (1 transfer per contiguous + * range, as permitted by the 16 byte per transfer AUX limit), not accessing + * other registers to avoid any read side-effects. + */ +static int next_reg_area(int *offset) +{ + *offset = find_next_bit(dptun_info_regs, 64, *offset); + + return find_next_zero_bit(dptun_info_regs, 64, *offset + 1) - *offset; +} + +#define tunnel_reg_ptr(__regs, __address) ({ \ + WARN_ON(!test_bit((__address) - DP_TUNNELING_BASE, dptun_info_regs)); \ + &(__regs)->buf[bitmap_weight(dptun_info_regs, (__address) - DP_TUNNELING_BASE)]; \ +}) + +static int read_tunnel_regs(struct drm_dp_aux *aux, struct drm_dp_tunnel_regs *regs) +{ + int offset = 0; + int len; + + while ((len = next_reg_area(&offset))) { + int address = DP_TUNNELING_BASE + offset; + + if (drm_dp_dpcd_read(aux, address, tunnel_reg_ptr(regs, address), len) < 0) + return -EIO; + + offset += len; + } + + return 0; +} + +static u8 tunnel_reg(const struct drm_dp_tunnel_regs *regs, int address) +{ + return *tunnel_reg_ptr(regs, address); +} + +static u8 tunnel_reg_drv_group_id(const struct drm_dp_tunnel_regs *regs) +{ + u8 drv_id = tunnel_reg(regs, DP_USB4_DRIVER_ID) & DP_USB4_DRIVER_ID_MASK; + u8 group_id = tunnel_reg(regs, DP_IN_ADAPTER_TUNNEL_INFORMATION) & DP_GROUP_ID_MASK; + + if (!group_id) + return 0; + + return (drv_id << DP_GROUP_ID_BITS) | group_id; +} + +/* Return granularity in kB/s units */ +static int tunnel_reg_bw_granularity(const struct drm_dp_tunnel_regs *regs) +{ + int gr = tunnel_reg(regs, DP_BW_GRANULARITY) & DP_BW_GRANULARITY_MASK; + + if (gr > 2) + return -1; + + return (250000 << gr) / 8; +} + +static int tunnel_reg_max_dprx_rate(const struct drm_dp_tunnel_regs *regs) +{ + u8 bw_code = tunnel_reg(regs, DP_TUNNELING_MAX_LINK_RATE); + + return drm_dp_bw_code_to_link_rate(bw_code); +} + +static int tunnel_reg_max_dprx_lane_count(const struct drm_dp_tunnel_regs *regs) +{ + return tunnel_reg(regs, DP_TUNNELING_MAX_LANE_COUNT) & + DP_TUNNELING_MAX_LANE_COUNT_MASK; +} + +static bool tunnel_reg_bw_alloc_supported(const struct drm_dp_tunnel_regs *regs) +{ + u8 cap_mask = DP_TUNNELING_SUPPORT | DP_IN_BW_ALLOCATION_MODE_SUPPORT; + + if ((tunnel_reg(regs, DP_TUNNELING_CAPABILITIES) & cap_mask) != cap_mask) + return false; + + return tunnel_reg(regs, DP_USB4_DRIVER_BW_CAPABILITY) & + DP_USB4_DRIVER_BW_ALLOCATION_MODE_SUPPORT; +} + +static bool tunnel_reg_bw_alloc_enabled(const struct drm_dp_tunnel_regs *regs) +{ + return tunnel_reg(regs, DP_DPTX_BW_ALLOCATION_MODE_CONTROL) & + DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE; +} + +static u8 tunnel_group_drv_id(u8 drv_group_id) +{ + return drv_group_id >> DP_GROUP_ID_BITS; +} + +static u8 tunnel_group_id(u8 drv_group_id) +{ + return drv_group_id & DP_GROUP_ID_MASK; +} + +const char *drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->name; +} +EXPORT_SYMBOL(drm_dp_tunnel_name); + +static const char *drm_dp_tunnel_group_name(const struct drm_dp_tunnel_group *group) +{ + return group->name; +} + +static struct drm_dp_tunnel_group * +lookup_or_alloc_group(struct drm_dp_tunnel_mgr *mgr, u8 drv_group_id) +{ + struct drm_dp_tunnel_group *group = NULL; + int i; + + for (i = 0; i < mgr->group_count; i++) { + /* + * A tunnel group with 0 group ID shouldn't have more than one + * tunnels. + */ + if (tunnel_group_id(drv_group_id) && + mgr->groups[i].drv_group_id == drv_group_id) + return &mgr->groups[i]; + + if (!group && !mgr->groups[i].active) + group = &mgr->groups[i]; + } + + if (!group) { + drm_dbg_kms(mgr->dev, + "DPTUN: Can't allocate more tunnel groups\n"); + return NULL; + } + + group->drv_group_id = drv_group_id; + group->active = true; + + /* + * The group name format here and elsewhere: Driver-ID:Group-ID:* + * (* standing for all DP-Adapters/tunnels in the group). + */ + snprintf(group->name, sizeof(group->name), "%d:%d:*", + tunnel_group_drv_id(drv_group_id) & ((1 << DP_GROUP_ID_BITS) - 1), + tunnel_group_id(drv_group_id) & ((1 << DP_USB4_DRIVER_ID_BITS) - 1)); + + return group; +} + +static void free_group(struct drm_dp_tunnel_group *group) +{ + struct drm_dp_tunnel_mgr *mgr = group->mgr; + + if (drm_WARN_ON(mgr->dev, !list_empty(&group->tunnels))) + return; + + group->drv_group_id = 0; + group->available_bw = -1; + group->active = false; +} + +static struct drm_dp_tunnel * +tunnel_get(struct drm_dp_tunnel *tunnel) +{ + kref_get(&tunnel->kref); + + return tunnel; +} + +static void free_tunnel(struct kref *kref) +{ + struct drm_dp_tunnel *tunnel = container_of(kref, typeof(*tunnel), kref); + struct drm_dp_tunnel_group *group = tunnel->group; + + list_del(&tunnel->node); + if (list_empty(&group->tunnels)) + free_group(group); + + kfree(tunnel); +} + +static void tunnel_put(struct drm_dp_tunnel *tunnel) +{ + kref_put(&tunnel->kref, free_tunnel); +} + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE +static void track_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + ref_tracker_alloc(&tunnel->group->mgr->ref_tracker, + tracker, GFP_KERNEL); +} + +static void untrack_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + ref_tracker_free(&tunnel->group->mgr->ref_tracker, + tracker); +} +#else +static void track_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ +} + +static void untrack_tunnel_ref(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ +} +#endif + +/** + * drm_dp_tunnel_get - Get a reference for a DP tunnel + * @tunnel: Tunnel object + * @tracker: Debug tracker for the reference + * + * Get a reference for @tunnel, along with a debug tracker to help locating + * the source of a reference leak/double reference put etc. issue. + * + * The reference must be dropped after use calling drm_dp_tunnel_put() + * passing @tunnel and *@tracker returned from here. + * + * Returns @tunnel - as a convenience - along with *@tracker. + */ +struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + track_tunnel_ref(tunnel, tracker); + + return tunnel_get(tunnel); +} +EXPORT_SYMBOL(drm_dp_tunnel_get); + +/** + * drm_dp_tunnel_put - Put a reference for a DP tunnel + * @tunnel - Tunnel object + * @tracker - Debug tracker for the reference + * + * Put a reference for @tunnel along with its debug *@tracker, which + * was obtained with drm_dp_tunnel_get(). + */ +void drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, + struct ref_tracker **tracker) +{ + untrack_tunnel_ref(tunnel, tracker); + + tunnel_put(tunnel); +} +EXPORT_SYMBOL(drm_dp_tunnel_put); + +static bool add_tunnel_to_group(struct drm_dp_tunnel_mgr *mgr, + u8 drv_group_id, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group *group; + + group = lookup_or_alloc_group(mgr, drv_group_id); + if (!group) + return false; + + tunnel->group = group; + list_add(&tunnel->node, &group->tunnels); + + return true; +} + +static struct drm_dp_tunnel * +create_tunnel(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux, + const struct drm_dp_tunnel_regs *regs) +{ + u8 drv_group_id = tunnel_reg_drv_group_id(regs); + struct drm_dp_tunnel *tunnel; + + tunnel = kzalloc(sizeof(*tunnel), GFP_KERNEL); + if (!tunnel) + return NULL; + + INIT_LIST_HEAD(&tunnel->node); + + kref_init(&tunnel->kref); + + tunnel->aux = aux; + + tunnel->adapter_id = tunnel_reg(regs, DP_IN_ADAPTER_INFO) & DP_IN_ADAPTER_NUMBER_MASK; + + snprintf(tunnel->name, sizeof(tunnel->name), "%d:%d:%d", + tunnel_group_drv_id(drv_group_id) & ((1 << DP_GROUP_ID_BITS) - 1), + tunnel_group_id(drv_group_id) & ((1 << DP_USB4_DRIVER_ID_BITS) - 1), + tunnel->adapter_id & ((1 << DP_IN_ADAPTER_NUMBER_BITS) - 1)); + + tunnel->bw_granularity = tunnel_reg_bw_granularity(regs); + tunnel->allocated_bw = tunnel_reg(regs, DP_ALLOCATED_BW) * + tunnel->bw_granularity; + /* + * An initial allocated BW of 0 indicates an undefined state: the + * actual allocation is determined by the TBT CM, usually following a + * legacy allocation policy (based on the max DPRX caps). From the + * driver's POV the state becomes defined only after the first + * allocation request. + */ + if (!tunnel->allocated_bw) + tunnel->allocated_bw = -1; + + tunnel->bw_alloc_supported = tunnel_reg_bw_alloc_supported(regs); + tunnel->bw_alloc_enabled = tunnel_reg_bw_alloc_enabled(regs); + + if (!add_tunnel_to_group(mgr, drv_group_id, tunnel)) { + kfree(tunnel); + + return NULL; + } + + track_tunnel_ref(tunnel, &tunnel->tracker); + + return tunnel; +} + +static void destroy_tunnel(struct drm_dp_tunnel *tunnel) +{ + untrack_tunnel_ref(tunnel, &tunnel->tracker); + tunnel_put(tunnel); +} + +/** + * drm_dp_tunnel_set_io_error - Set the IO error flag for a DP tunnel + * @tunnel: Tunnel object + * + * Set the IO error flag for @tunnel. Drivers can call this function upon + * detecting a failure that affects the tunnel functionality, for instance + * after a DP AUX transfer failure on the port @tunnel is connected to. + * + * This disables further management of @tunnel, including any related + * AUX accesses for tunneling DPCD registers, returning error to the + * initiators of these. The driver is supposed to drop this tunnel and - + * optionally - recreate it. + */ +void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel) +{ + tunnel->has_io_error = true; +} +EXPORT_SYMBOL(drm_dp_tunnel_set_io_error); + +#define SKIP_DPRX_CAPS_CHECK BIT(0) +#define ALLOW_ALLOCATED_BW_CHANGE BIT(1) +static bool tunnel_regs_are_valid(struct drm_dp_tunnel_mgr *mgr, + const struct drm_dp_tunnel_regs *regs, + unsigned int flags) +{ + u8 drv_group_id = tunnel_reg_drv_group_id(regs); + bool check_dprx = !(flags & SKIP_DPRX_CAPS_CHECK); + bool ret = true; + + if (!tunnel_reg_bw_alloc_supported(regs)) { + if (tunnel_group_id(drv_group_id)) { + drm_dbg_kms(mgr->dev, + "DPTUN: A non-zero group ID is only allowed with BWA support\n"); + ret = false; + } + + if (tunnel_reg(regs, DP_ALLOCATED_BW)) { + drm_dbg_kms(mgr->dev, + "DPTUN: BW is allocated without BWA support\n"); + ret = false; + } + + return ret; + } + + if (!tunnel_group_id(drv_group_id)) { + drm_dbg_kms(mgr->dev, + "DPTUN: BWA support requires a non-zero group ID\n"); + ret = false; + } + + if (check_dprx && hweight8(tunnel_reg_max_dprx_lane_count(regs)) != 1) { + drm_dbg_kms(mgr->dev, + "DPTUN: Invalid DPRX lane count: %d\n", + tunnel_reg_max_dprx_lane_count(regs)); + + ret = false; + } + + if (check_dprx && !tunnel_reg_max_dprx_rate(regs)) { + drm_dbg_kms(mgr->dev, + "DPTUN: DPRX rate is 0\n"); + + ret = false; + } + + if (tunnel_reg_bw_granularity(regs) < 0) { + drm_dbg_kms(mgr->dev, + "DPTUN: Invalid BW granularity\n"); + + ret = false; + } + + if (tunnel_reg(regs, DP_ALLOCATED_BW) > tunnel_reg(regs, DP_ESTIMATED_BW)) { + drm_dbg_kms(mgr->dev, + "DPTUN: Allocated BW %d > estimated BW %d Mb/s\n", + DPTUN_BW_ARG(tunnel_reg(regs, DP_ALLOCATED_BW) * + tunnel_reg_bw_granularity(regs)), + DPTUN_BW_ARG(tunnel_reg(regs, DP_ESTIMATED_BW) * + tunnel_reg_bw_granularity(regs))); + + ret = false; + } + + return ret; +} + +static int tunnel_allocated_bw(const struct drm_dp_tunnel *tunnel) +{ + return max(tunnel->allocated_bw, 0); +} + +static bool tunnel_info_changes_are_valid(struct drm_dp_tunnel *tunnel, + const struct drm_dp_tunnel_regs *regs, + unsigned int flags) +{ + u8 new_drv_group_id = tunnel_reg_drv_group_id(regs); + bool ret = true; + + if (tunnel->bw_alloc_supported != tunnel_reg_bw_alloc_supported(regs)) { + tun_dbg(tunnel, + "BW alloc support has changed %s -> %s\n", + str_yes_no(tunnel->bw_alloc_supported), + str_yes_no(tunnel_reg_bw_alloc_supported(regs))); + + ret = false; + } + + if (tunnel->group->drv_group_id != new_drv_group_id) { + tun_dbg(tunnel, + "Driver/group ID has changed %d:%d:* -> %d:%d:*\n", + tunnel_group_drv_id(tunnel->group->drv_group_id), + tunnel_group_id(tunnel->group->drv_group_id), + tunnel_group_drv_id(new_drv_group_id), + tunnel_group_id(new_drv_group_id)); + + ret = false; + } + + if (!tunnel->bw_alloc_supported) + return ret; + + if (tunnel->bw_granularity != tunnel_reg_bw_granularity(regs)) { + tun_dbg(tunnel, + "BW granularity has changed: %d -> %d Mb/s\n", + DPTUN_BW_ARG(tunnel->bw_granularity), + DPTUN_BW_ARG(tunnel_reg_bw_granularity(regs))); + + ret = false; + } + + /* + * On some devices at least the BW alloc mode enabled status is always + * reported as 0, so skip checking that here. + */ + + if (!(flags & ALLOW_ALLOCATED_BW_CHANGE) && + tunnel_allocated_bw(tunnel) != + tunnel_reg(regs, DP_ALLOCATED_BW) * tunnel->bw_granularity) { + tun_dbg(tunnel, + "Allocated BW has changed: %d -> %d Mb/s\n", + DPTUN_BW_ARG(tunnel->allocated_bw), + DPTUN_BW_ARG(tunnel_reg(regs, DP_ALLOCATED_BW) * tunnel->bw_granularity)); + + ret = false; + } + + return ret; +} + +static int +read_and_verify_tunnel_regs(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_regs *regs, + unsigned int flags) +{ + int err; + + err = read_tunnel_regs(tunnel->aux, regs); + if (err < 0) { + drm_dp_tunnel_set_io_error(tunnel); + + return err; + } + + if (!tunnel_regs_are_valid(tunnel->group->mgr, regs, flags)) + return -EINVAL; + + if (!tunnel_info_changes_are_valid(tunnel, regs, flags)) + return -EINVAL; + + return 0; +} + +static bool update_dprx_caps(struct drm_dp_tunnel *tunnel, const struct drm_dp_tunnel_regs *regs) +{ + bool changed = false; + + if (tunnel_reg_max_dprx_rate(regs) != tunnel->max_dprx_rate) { + tunnel->max_dprx_rate = tunnel_reg_max_dprx_rate(regs); + changed = true; + } + + if (tunnel_reg_max_dprx_lane_count(regs) != tunnel->max_dprx_lane_count) { + tunnel->max_dprx_lane_count = tunnel_reg_max_dprx_lane_count(regs); + changed = true; + } + + return changed; +} + +static int dev_id_len(const u8 *dev_id, int max_len) +{ + while (max_len && dev_id[max_len - 1] == '\0') + max_len--; + + return max_len; +} + +static int get_max_dprx_bw(const struct drm_dp_tunnel *tunnel) +{ + int max_dprx_bw = drm_dp_max_dprx_data_rate(tunnel->max_dprx_rate, + tunnel->max_dprx_lane_count); + + /* + * A BW request of roundup(max_dprx_bw, tunnel->bw_granularity) results in + * an allocation of max_dprx_bw. A BW request above this rounded-up + * value will fail. + */ + return min(roundup(max_dprx_bw, tunnel->bw_granularity), + MAX_DP_REQUEST_BW * tunnel->bw_granularity); +} + +static int get_max_tunnel_bw(const struct drm_dp_tunnel *tunnel) +{ + return min(get_max_dprx_bw(tunnel), tunnel->group->available_bw); +} + +/** + * drm_dp_tunnel_detect - Detect DP tunnel on the link + * @mgr: Tunnel manager + * @aux: DP AUX on which the tunnel will be detected + * + * Detect if there is any DP tunnel on the link and add it to the tunnel + * group's tunnel list. + * + * Returns a pointer to a tunnel on success, or an ERR_PTR() error on + * failure. + */ +struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + struct drm_dp_tunnel_regs regs; + struct drm_dp_tunnel *tunnel; + int err; + + err = read_tunnel_regs(aux, ®s); + if (err) + return ERR_PTR(err); + + if (!(tunnel_reg(®s, DP_TUNNELING_CAPABILITIES) & + DP_TUNNELING_SUPPORT)) + return ERR_PTR(-ENODEV); + + /* The DPRX caps are valid only after enabling BW alloc mode. */ + if (!tunnel_regs_are_valid(mgr, ®s, SKIP_DPRX_CAPS_CHECK)) + return ERR_PTR(-EINVAL); + + tunnel = create_tunnel(mgr, aux, ®s); + if (!tunnel) + return ERR_PTR(-ENOMEM); + + tun_dbg(tunnel, + "OUI:%*phD DevID:%*pE Rev-HW:%d.%d SW:%d.%d PR-Sup:%s BWA-Sup:%s BWA-En:%s\n", + DP_TUNNELING_OUI_BYTES, + tunnel_reg_ptr(®s, DP_TUNNELING_OUI), + dev_id_len(tunnel_reg_ptr(®s, DP_TUNNELING_DEV_ID), DP_TUNNELING_DEV_ID_BYTES), + tunnel_reg_ptr(®s, DP_TUNNELING_DEV_ID), + (tunnel_reg(®s, DP_TUNNELING_HW_REV) & DP_TUNNELING_HW_REV_MAJOR_MASK) >> + DP_TUNNELING_HW_REV_MAJOR_SHIFT, + (tunnel_reg(®s, DP_TUNNELING_HW_REV) & DP_TUNNELING_HW_REV_MINOR_MASK) >> + DP_TUNNELING_HW_REV_MINOR_SHIFT, + tunnel_reg(®s, DP_TUNNELING_SW_REV_MAJOR), + tunnel_reg(®s, DP_TUNNELING_SW_REV_MINOR), + str_yes_no(tunnel_reg(®s, DP_TUNNELING_CAPABILITIES) & + DP_PANEL_REPLAY_OPTIMIZATION_SUPPORT), + str_yes_no(tunnel->bw_alloc_supported), + str_yes_no(tunnel->bw_alloc_enabled)); + + return tunnel; +} +EXPORT_SYMBOL(drm_dp_tunnel_detect); + +/** + * drm_dp_tunnel_destroy - Destroy tunnel object + * @tunnel: Tunnel object + * + * Remove the tunnel from the tunnel topology and destroy it. + * + * Returns 0 on success, -ENODEV if the tunnel has been destroyed already. + */ +int drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel) +{ + if (!tunnel) + return 0; + + if (drm_WARN_ON(tunnel->group->mgr->dev, tunnel->destroyed)) + return -ENODEV; + + tun_dbg(tunnel, "destroying\n"); + + tunnel->destroyed = true; + destroy_tunnel(tunnel); + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_destroy); + +static int check_tunnel(const struct drm_dp_tunnel *tunnel) +{ + if (tunnel->destroyed) + return -ENODEV; + + if (tunnel->has_io_error) + return -EIO; + + return 0; +} + +static int group_allocated_bw(struct drm_dp_tunnel_group *group) +{ + struct drm_dp_tunnel *tunnel; + int group_allocated_bw = 0; + + for_each_tunnel_in_group(group, tunnel) { + if (check_tunnel(tunnel) == 0 && + tunnel->bw_alloc_enabled) + group_allocated_bw += tunnel_allocated_bw(tunnel); + } + + return group_allocated_bw; +} + +/* + * The estimated BW reported by the TBT Connection Manager for each tunnel in + * a group includes the BW already allocated for the given tunnel and the + * unallocated BW which is free to be used by any tunnel in the group. + */ +static int group_free_bw(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->estimated_bw - tunnel_allocated_bw(tunnel); +} + +static int calc_group_available_bw(const struct drm_dp_tunnel *tunnel) +{ + return group_allocated_bw(tunnel->group) + + group_free_bw(tunnel); +} + +static int update_group_available_bw(struct drm_dp_tunnel *tunnel, + const struct drm_dp_tunnel_regs *regs) +{ + struct drm_dp_tunnel *tunnel_iter; + int group_available_bw; + bool changed; + + tunnel->estimated_bw = tunnel_reg(regs, DP_ESTIMATED_BW) * tunnel->bw_granularity; + + if (calc_group_available_bw(tunnel) == tunnel->group->available_bw) + return 0; + + for_each_tunnel_in_group(tunnel->group, tunnel_iter) { + int err; + + if (tunnel_iter == tunnel) + continue; + + if (check_tunnel(tunnel_iter) != 0 || + !tunnel_iter->bw_alloc_enabled) + continue; + + err = drm_dp_dpcd_probe(tunnel_iter->aux, DP_DPCD_REV); + if (err) { + tun_dbg(tunnel_iter, + "Probe failed, assume disconnected (err %pe)\n", + ERR_PTR(err)); + drm_dp_tunnel_set_io_error(tunnel_iter); + } + } + + group_available_bw = calc_group_available_bw(tunnel); + + tun_dbg(tunnel, "Updated group available BW: %d->%d\n", + DPTUN_BW_ARG(tunnel->group->available_bw), + DPTUN_BW_ARG(group_available_bw)); + + changed = tunnel->group->available_bw != group_available_bw; + + tunnel->group->available_bw = group_available_bw; + + return changed ? 1 : 0; +} + +static int set_bw_alloc_mode(struct drm_dp_tunnel *tunnel, bool enable) +{ + u8 mask = DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE | DP_UNMASK_BW_ALLOCATION_IRQ; + u8 val; + + if (drm_dp_dpcd_readb(tunnel->aux, DP_DPTX_BW_ALLOCATION_MODE_CONTROL, &val) < 0) + goto out_err; + + if (enable) + val |= mask; + else + val &= ~mask; + + if (drm_dp_dpcd_writeb(tunnel->aux, DP_DPTX_BW_ALLOCATION_MODE_CONTROL, val) < 0) + goto out_err; + + tunnel->bw_alloc_enabled = enable; + + return 0; + +out_err: + drm_dp_tunnel_set_io_error(tunnel); + + return -EIO; +} + +/** + * drm_dp_tunnel_enable_bw_alloc - Enable DP tunnel BW allocation mode + * @tunnel: Tunnel object + * + * Enable the DP tunnel BW allocation mode on @tunnel if it supports it. + * + * Returns 0 in case of success, negative error code otherwise. + */ +int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_regs regs; + int err; + + err = check_tunnel(tunnel); + if (err) + return err; + + if (!tunnel->bw_alloc_supported) + return -EOPNOTSUPP; + + if (!tunnel_group_id(tunnel->group->drv_group_id)) + return -EINVAL; + + err = set_bw_alloc_mode(tunnel, true); + if (err) + goto out; + + /* + * After a BWA disable/re-enable sequence the allocated BW can either + * stay at its last requested value or, for instance after system + * suspend/resume, TBT CM can reset back the allocation to the amount + * allocated in the legacy/non-BWA mode. Accordingly allow for the + * allocation to change wrt. the last SW state. + */ + err = read_and_verify_tunnel_regs(tunnel, ®s, + ALLOW_ALLOCATED_BW_CHANGE); + if (err) { + set_bw_alloc_mode(tunnel, false); + + goto out; + } + + if (!tunnel->max_dprx_rate) + update_dprx_caps(tunnel, ®s); + + if (tunnel->group->available_bw == -1) { + err = update_group_available_bw(tunnel, ®s); + if (err > 0) + err = 0; + } +out: + tun_dbg_stat(tunnel, err, + "Enabling BW alloc mode: DPRX:%dx%d Group alloc:%d/%d Mb/s", + tunnel->max_dprx_rate / 100, tunnel->max_dprx_lane_count, + DPTUN_BW_ARG(group_allocated_bw(tunnel->group)), + DPTUN_BW_ARG(tunnel->group->available_bw)); + + return err; +} +EXPORT_SYMBOL(drm_dp_tunnel_enable_bw_alloc); + +/** + * drm_dp_tunnel_disable_bw_alloc - Disable DP tunnel BW allocation mode + * @tunnel: Tunnel object + * + * Disable the DP tunnel BW allocation mode on @tunnel. + * + * Returns 0 in case of success, negative error code otherwise. + */ +int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + int err; + + err = check_tunnel(tunnel); + if (err) + return err; + + tunnel->allocated_bw = -1; + + err = set_bw_alloc_mode(tunnel, false); + + tun_dbg_stat(tunnel, err, "Disabling BW alloc mode"); + + return err; +} +EXPORT_SYMBOL(drm_dp_tunnel_disable_bw_alloc); + +/** + * drm_dp_tunnel_bw_alloc_is_enabled - Query the BW allocation mode enabled state + * @tunnel: Tunnel object + * + * Query if the BW allocation mode is enabled for @tunnel. + * + * Returns %true if the BW allocation mode is enabled for @tunnel. + */ +bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel) +{ + return tunnel && tunnel->bw_alloc_enabled; +} +EXPORT_SYMBOL(drm_dp_tunnel_bw_alloc_is_enabled); + +static int clear_bw_req_state(struct drm_dp_aux *aux) +{ + u8 bw_req_mask = DP_BW_REQUEST_SUCCEEDED | DP_BW_REQUEST_FAILED; + + if (drm_dp_dpcd_writeb(aux, DP_TUNNELING_STATUS, bw_req_mask) < 0) + return -EIO; + + return 0; +} + +static int bw_req_complete(struct drm_dp_aux *aux, bool *status_changed) +{ + u8 bw_req_mask = DP_BW_REQUEST_SUCCEEDED | DP_BW_REQUEST_FAILED; + u8 status_change_mask = DP_BW_ALLOCATION_CAPABILITY_CHANGED | DP_ESTIMATED_BW_CHANGED; + u8 val; + int err; + + if (drm_dp_dpcd_readb(aux, DP_TUNNELING_STATUS, &val) < 0) + return -EIO; + + *status_changed = val & status_change_mask; + + val &= bw_req_mask; + + if (!val) + return -EAGAIN; + + err = clear_bw_req_state(aux); + if (err < 0) + return err; + + return val == DP_BW_REQUEST_SUCCEEDED ? 0 : -ENOSPC; +} + +static int allocate_tunnel_bw(struct drm_dp_tunnel *tunnel, int bw) +{ + struct drm_dp_tunnel_mgr *mgr = tunnel->group->mgr; + int request_bw = DIV_ROUND_UP(bw, tunnel->bw_granularity); + DEFINE_WAIT_FUNC(wait, woken_wake_function); + long timeout; + int err; + + if (bw < 0) { + err = -EINVAL; + goto out; + } + + if (request_bw * tunnel->bw_granularity == tunnel->allocated_bw) + return 0; + + /* Atomic check should prevent the following. */ + if (drm_WARN_ON(mgr->dev, request_bw > MAX_DP_REQUEST_BW)) { + err = -EINVAL; + goto out; + } + + err = clear_bw_req_state(tunnel->aux); + if (err) + goto out; + + if (drm_dp_dpcd_writeb(tunnel->aux, DP_REQUEST_BW, request_bw) < 0) { + err = -EIO; + goto out; + } + + timeout = msecs_to_jiffies(3000); + add_wait_queue(&mgr->bw_req_queue, &wait); + + for (;;) { + bool status_changed; + + err = bw_req_complete(tunnel->aux, &status_changed); + if (err != -EAGAIN) + break; + + if (status_changed) { + struct drm_dp_tunnel_regs regs; + + err = read_and_verify_tunnel_regs(tunnel, ®s, + ALLOW_ALLOCATED_BW_CHANGE); + if (err) + break; + } + + if (!timeout) { + err = -ETIMEDOUT; + break; + } + + timeout = wait_woken(&wait, TASK_UNINTERRUPTIBLE, timeout); + }; + + remove_wait_queue(&mgr->bw_req_queue, &wait); + + if (err) + goto out; + + tunnel->allocated_bw = request_bw * tunnel->bw_granularity; + +out: + tun_dbg_stat(tunnel, err, "Allocating %d/%d Mb/s for tunnel: Group alloc:%d/%d Mb/s", + DPTUN_BW_ARG(request_bw * tunnel->bw_granularity), + DPTUN_BW_ARG(get_max_tunnel_bw(tunnel)), + DPTUN_BW_ARG(group_allocated_bw(tunnel->group)), + DPTUN_BW_ARG(tunnel->group->available_bw)); + + if (err == -EIO) + drm_dp_tunnel_set_io_error(tunnel); + + return err; +} + +/** + * drm_dp_tunnel_alloc_bw - Allocate BW for a DP tunnel + * @tunnel: Tunnel object + * @bw: BW in kB/s units + * + * Allocate @bw kB/s for @tunnel. The allocated BW must be freed after use by + * calling this function for the same tunnel setting @bw to 0. + * + * Returns 0 in case of success, a negative error code otherwise. + */ +int drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw) +{ + int err; + + err = check_tunnel(tunnel); + if (err) + return err; + + return allocate_tunnel_bw(tunnel, bw); +} +EXPORT_SYMBOL(drm_dp_tunnel_alloc_bw); + +/** + * drm_dp_tunnel_atomic_get_allocated_bw - Get the BW allocated for a DP tunnel + * @tunnel: Tunnel object + * + * Get the current BW allocated for @tunnel. After the tunnel is created / + * resumed and the BW allocation mode is enabled for it, the allocation + * becomes determined only after the first allocation request by the driver + * calling drm_dp_tunnel_alloc_bw(). + * + * Return the BW allocated for the tunnel, or -1 if the allocation is + * undetermined. + */ +int drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel) +{ + return tunnel->allocated_bw; +} +EXPORT_SYMBOL(drm_dp_tunnel_get_allocated_bw); + +/* + * Return 0 if the status hasn't changed, 1 if the status has changed, a + * negative error code in case of an I/O failure. + */ +static int check_and_clear_status_change(struct drm_dp_tunnel *tunnel) +{ + u8 mask = DP_BW_ALLOCATION_CAPABILITY_CHANGED | DP_ESTIMATED_BW_CHANGED; + u8 val; + + if (drm_dp_dpcd_readb(tunnel->aux, DP_TUNNELING_STATUS, &val) < 0) + goto out_err; + + val &= mask; + + if (val) { + if (drm_dp_dpcd_writeb(tunnel->aux, DP_TUNNELING_STATUS, val) < 0) + goto out_err; + + return 1; + } + + if (!drm_dp_tunnel_bw_alloc_is_enabled(tunnel)) + return 0; + + /* + * Check for estimated BW changes explicitly to account for lost + * BW change notifications. + */ + if (drm_dp_dpcd_readb(tunnel->aux, DP_ESTIMATED_BW, &val) < 0) + goto out_err; + + if (val * tunnel->bw_granularity != tunnel->estimated_bw) + return 1; + + return 0; + +out_err: + drm_dp_tunnel_set_io_error(tunnel); + + return -EIO; +} + +/** + * drm_dp_tunnel_update_state - Update DP tunnel SW state with the HW state + * @tunnel: Tunnel object + * + * Update the SW state of @tunnel with the HW state. + * + * Returns 0 if the state has not changed, 1 if it has changed and got updated + * successfully and a negative error code otherwise. + */ +int drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_regs regs; + bool changed = false; + int ret; + + ret = check_tunnel(tunnel); + if (ret < 0) + return ret; + + ret = check_and_clear_status_change(tunnel); + if (ret < 0) + goto out; + + if (!ret) + return 0; + + ret = read_and_verify_tunnel_regs(tunnel, ®s, 0); + if (ret) + goto out; + + if (update_dprx_caps(tunnel, ®s)) + changed = true; + + ret = update_group_available_bw(tunnel, ®s); + if (ret == 1) + changed = true; + +out: + tun_dbg_stat(tunnel, ret < 0 ? ret : 0, + "State update: Changed:%s DPRX:%dx%d Tunnel alloc:%d/%d Group alloc:%d/%d Mb/s", + str_yes_no(changed), + tunnel->max_dprx_rate / 100, tunnel->max_dprx_lane_count, + DPTUN_BW_ARG(tunnel->allocated_bw), + DPTUN_BW_ARG(get_max_tunnel_bw(tunnel)), + DPTUN_BW_ARG(group_allocated_bw(tunnel->group)), + DPTUN_BW_ARG(tunnel->group->available_bw)); + + if (ret < 0) + return ret; + + if (changed) + return 1; + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_update_state); + +/* + * drm_dp_tunnel_handle_irq - Handle DP tunnel IRQs + * + * Handle any pending DP tunnel IRQs, waking up waiters for a completion + * event. + * + * Returns 1 if the state of the tunnel has changed which requires calling + * drm_dp_tunnel_update_state(), a negative error code in case of a failure, + * 0 otherwise. + */ +int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, struct drm_dp_aux *aux) +{ + u8 val; + + if (drm_dp_dpcd_readb(aux, DP_TUNNELING_STATUS, &val) < 0) + return -EIO; + + if (val & (DP_BW_REQUEST_SUCCEEDED | DP_BW_REQUEST_FAILED)) + wake_up_all(&mgr->bw_req_queue); + + if (val & (DP_BW_ALLOCATION_CAPABILITY_CHANGED | DP_ESTIMATED_BW_CHANGED)) + return 1; + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_handle_irq); + +/** + * drm_dp_tunnel_max_dprx_rate - Query the maximum rate of the tunnel's DPRX + * @tunnel: Tunnel object + * + * The function is used to query the maximum link rate of the DPRX connected + * to @tunnel. Note that this rate will not be limited by the BW limit of the + * tunnel, as opposed to the standard and extended DP_MAX_LINK_RATE DPCD + * registers. + * + * Returns the maximum link rate in 10 kbit/s units. + */ +int drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->max_dprx_rate; +} +EXPORT_SYMBOL(drm_dp_tunnel_max_dprx_rate); + +/** + * drm_dp_tunnel_max_dprx_lane_count - Query the maximum lane count of the tunnel's DPRX + * @tunnel: Tunnel object + * + * The function is used to query the maximum lane count of the DPRX connected + * to @tunnel. Note that this lane count will not be limited by the BW limit of + * the tunnel, as opposed to the standard and extended DP_MAX_LANE_COUNT DPCD + * registers. + * + * Returns the maximum lane count. + */ +int drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->max_dprx_lane_count; +} +EXPORT_SYMBOL(drm_dp_tunnel_max_dprx_lane_count); + +/** + * drm_dp_tunnel_available_bw - Query the estimated total available BW of the tunnel + * @tunnel: Tunnel object + * + * This function is used to query the estimated total available BW of the + * tunnel. This includes the currently allocated and free BW for all the + * tunnels in @tunnel's group. The available BW is valid only after the BW + * allocation mode has been enabled for the tunnel and its state got updated + * calling drm_dp_tunnel_update_state(). + * + * Returns the @tunnel group's estimated total available bandwidth in kB/s + * units, or -1 if the available BW isn't valid (the BW allocation mode is + * not enabled or the tunnel's state hasn't been updated). + */ +int drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel) +{ + return tunnel->group->available_bw; +} +EXPORT_SYMBOL(drm_dp_tunnel_available_bw); + +static struct drm_dp_tunnel_group_state * +drm_dp_tunnel_atomic_get_group_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + return (struct drm_dp_tunnel_group_state *) + drm_atomic_get_private_obj_state(state, + &tunnel->group->base); +} + +static struct drm_dp_tunnel_state * +add_tunnel_state(struct drm_dp_tunnel_group_state *group_state, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_state *tunnel_state; + + tun_dbg_atomic(tunnel, + "Adding state for tunnel %p to group state %p\n", + tunnel, group_state); + + tunnel_state = kzalloc(sizeof(*tunnel_state), GFP_KERNEL); + if (!tunnel_state) + return NULL; + + tunnel_state->group_state = group_state; + + drm_dp_tunnel_ref_get(tunnel, &tunnel_state->tunnel_ref); + + INIT_LIST_HEAD(&tunnel_state->node); + list_add(&tunnel_state->node, &group_state->tunnel_states); + + return tunnel_state; +} + +static void free_tunnel_state(struct drm_dp_tunnel_state *tunnel_state) +{ + tun_dbg_atomic(tunnel_state->tunnel_ref.tunnel, + "Freeing state for tunnel %p\n", + tunnel_state->tunnel_ref.tunnel); + + list_del(&tunnel_state->node); + + kfree(tunnel_state->stream_bw); + drm_dp_tunnel_ref_put(&tunnel_state->tunnel_ref); + + kfree(tunnel_state); +} + +static void free_group_state(struct drm_dp_tunnel_group_state *group_state) +{ + struct drm_dp_tunnel_state *tunnel_state; + struct drm_dp_tunnel_state *tunnel_state_tmp; + + for_each_tunnel_state_safe(group_state, tunnel_state, tunnel_state_tmp) + free_tunnel_state(tunnel_state); + + kfree(group_state); +} + +static struct drm_dp_tunnel_state * +get_tunnel_state(struct drm_dp_tunnel_group_state *group_state, + const struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_state *tunnel_state; + + for_each_tunnel_state(group_state, tunnel_state) + if (tunnel_state->tunnel_ref.tunnel == tunnel) + return tunnel_state; + + return NULL; +} + +static struct drm_dp_tunnel_state * +get_or_add_tunnel_state(struct drm_dp_tunnel_group_state *group_state, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_state *tunnel_state; + + tunnel_state = get_tunnel_state(group_state, tunnel); + if (tunnel_state) + return tunnel_state; + + return add_tunnel_state(group_state, tunnel); +} + +static struct drm_private_state * +tunnel_group_duplicate_state(struct drm_private_obj *obj) +{ + struct drm_dp_tunnel_group_state *group_state; + struct drm_dp_tunnel_state *tunnel_state; + + group_state = kzalloc(sizeof(*group_state), GFP_KERNEL); + if (!group_state) + return NULL; + + INIT_LIST_HEAD(&group_state->tunnel_states); + + __drm_atomic_helper_private_obj_duplicate_state(obj, &group_state->base); + + for_each_tunnel_state(to_group_state(obj->state), tunnel_state) { + struct drm_dp_tunnel_state *new_tunnel_state; + + new_tunnel_state = get_or_add_tunnel_state(group_state, + tunnel_state->tunnel_ref.tunnel); + if (!new_tunnel_state) + goto out_free_state; + + new_tunnel_state->stream_mask = tunnel_state->stream_mask; + new_tunnel_state->stream_bw = kmemdup(tunnel_state->stream_bw, + sizeof(*tunnel_state->stream_bw) * + hweight32(tunnel_state->stream_mask), + GFP_KERNEL); + + if (!new_tunnel_state->stream_bw) + goto out_free_state; + } + + return &group_state->base; + +out_free_state: + free_group_state(group_state); + + return NULL; +} + +static void tunnel_group_destroy_state(struct drm_private_obj *obj, struct drm_private_state *state) +{ + free_group_state(to_group_state(state)); +} + +static const struct drm_private_state_funcs tunnel_group_funcs = { + .atomic_duplicate_state = tunnel_group_duplicate_state, + .atomic_destroy_state = tunnel_group_destroy_state, +}; + +/** + * drm_dp_tunnel_atomic_get_state - get/allocate the new atomic state for a tunnel + * @state: Atomic state + * @tunnel: Tunnel to get the state for + * + * Get the new atomic state for @tunnel, duplicating it from the old tunnel + * state if not yet allocated. + * + * Return the state or an ERR_PTR() error on failure. + */ +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group_state *group_state; + struct drm_dp_tunnel_state *tunnel_state; + + group_state = drm_dp_tunnel_atomic_get_group_state(state, tunnel); + if (IS_ERR(group_state)) + return ERR_CAST(group_state); + + tunnel_state = get_or_add_tunnel_state(group_state, tunnel); + if (!tunnel_state) + return ERR_PTR(-ENOMEM); + + return tunnel_state; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_state); + +/** + * drm_dp_tunnel_atomic_get_old_state - get the old atomic state for a tunnel + * @state: Atomic state + * @tunnel: Tunnel to get the state for + * + * Get the old atomic state for @tunnel. + * + * Return the old state or NULL if the tunnel's atomic state is not in @state. + */ +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_old_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group_state *old_group_state; + int i; + + for_each_old_group_in_state(state, old_group_state, i) + if (to_group(old_group_state->base.obj) == tunnel->group) + return get_tunnel_state(old_group_state, tunnel); + + return NULL; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_old_state); + +/** + * drm_dp_tunnel_atomic_get_new_state - get the new atomic state for a tunnel + * @state: Atomic state + * @tunnel: Tunnel to get the state for + * + * Get the new atomic state for @tunnel. + * + * Return the new state or NULL if the tunnel's atomic state is not in @state. + */ +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + struct drm_dp_tunnel_group_state *new_group_state; + int i; + + for_each_new_group_in_state(state, new_group_state, i) + if (to_group(new_group_state->base.obj) == tunnel->group) + return get_tunnel_state(new_group_state, tunnel); + + return NULL; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_new_state); + +static bool init_group(struct drm_dp_tunnel_mgr *mgr, struct drm_dp_tunnel_group *group) +{ + struct drm_dp_tunnel_group_state *group_state; + + group_state = kzalloc(sizeof(*group_state), GFP_KERNEL); + if (!group_state) + return false; + + INIT_LIST_HEAD(&group_state->tunnel_states); + + group->mgr = mgr; + group->available_bw = -1; + INIT_LIST_HEAD(&group->tunnels); + + drm_atomic_private_obj_init(mgr->dev, &group->base, &group_state->base, + &tunnel_group_funcs); + + return true; +} + +static void cleanup_group(struct drm_dp_tunnel_group *group) +{ + drm_atomic_private_obj_fini(&group->base); +} + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE +static void check_unique_stream_ids(const struct drm_dp_tunnel_group_state *group_state) +{ + const struct drm_dp_tunnel_state *tunnel_state; + u32 stream_mask = 0; + + for_each_tunnel_state(group_state, tunnel_state) { + drm_WARN(to_group(group_state->base.obj)->mgr->dev, + tunnel_state->stream_mask & stream_mask, + "[DPTUN %s]: conflicting stream IDs %x (IDs in other tunnels %x)\n", + tunnel_state->tunnel_ref.tunnel->name, + tunnel_state->stream_mask, + stream_mask); + + stream_mask |= tunnel_state->stream_mask; + } +} +#else +static void check_unique_stream_ids(const struct drm_dp_tunnel_group_state *group_state) +{ +} +#endif + +static int stream_id_to_idx(u32 stream_mask, u8 stream_id) +{ + return hweight32(stream_mask & (BIT(stream_id) - 1)); +} + +static int resize_bw_array(struct drm_dp_tunnel_state *tunnel_state, + unsigned long old_mask, unsigned long new_mask) +{ + unsigned long move_mask = old_mask & new_mask; + int *new_bws = NULL; + int id; + + WARN_ON(!new_mask); + + if (old_mask == new_mask) + return 0; + + new_bws = kcalloc(hweight32(new_mask), sizeof(*new_bws), GFP_KERNEL); + if (!new_bws) + return -ENOMEM; + + for_each_set_bit(id, &move_mask, BITS_PER_TYPE(move_mask)) + new_bws[stream_id_to_idx(new_mask, id)] = + tunnel_state->stream_bw[stream_id_to_idx(old_mask, id)]; + + kfree(tunnel_state->stream_bw); + tunnel_state->stream_bw = new_bws; + tunnel_state->stream_mask = new_mask; + + return 0; +} + +static int set_stream_bw(struct drm_dp_tunnel_state *tunnel_state, + u8 stream_id, int bw) +{ + int err; + + err = resize_bw_array(tunnel_state, + tunnel_state->stream_mask, + tunnel_state->stream_mask | BIT(stream_id)); + if (err) + return err; + + tunnel_state->stream_bw[stream_id_to_idx(tunnel_state->stream_mask, stream_id)] = bw; + + return 0; +} + +static int clear_stream_bw(struct drm_dp_tunnel_state *tunnel_state, + u8 stream_id) +{ + if (!(tunnel_state->stream_mask & ~BIT(stream_id))) { + free_tunnel_state(tunnel_state); + return 0; + } + + return resize_bw_array(tunnel_state, + tunnel_state->stream_mask, + tunnel_state->stream_mask & ~BIT(stream_id)); +} + +/** + * drm_dp_tunnel_atomic_set_stream_bw - Set the BW for a DP tunnel stream + * @state: Atomic state + * @tunnel: DP tunnel containing the stream + * @stream_id: Stream ID + * @bw: BW of the stream + * + * Set a DP tunnel stream's required BW in the atomic state. + * + * Returns 0 in case of success, a negative error code otherwise. + */ +int drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw) +{ + struct drm_dp_tunnel_group_state *new_group_state; + struct drm_dp_tunnel_state *tunnel_state; + int err; + + if (drm_WARN_ON(tunnel->group->mgr->dev, + stream_id > BITS_PER_TYPE(tunnel_state->stream_mask))) + return -EINVAL; + + tun_dbg(tunnel, + "Setting %d Mb/s for stream %d\n", + DPTUN_BW_ARG(bw), stream_id); + + new_group_state = drm_dp_tunnel_atomic_get_group_state(state, tunnel); + if (IS_ERR(new_group_state)) + return PTR_ERR(new_group_state); + + if (bw == 0) { + tunnel_state = get_tunnel_state(new_group_state, tunnel); + if (!tunnel_state) + return 0; + + return clear_stream_bw(tunnel_state, stream_id); + } + + tunnel_state = get_or_add_tunnel_state(new_group_state, tunnel); + if (drm_WARN_ON(state->dev, !tunnel_state)) + return -EINVAL; + + err = set_stream_bw(tunnel_state, stream_id, bw); + if (err) + return err; + + check_unique_stream_ids(new_group_state); + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_set_stream_bw); + +/** + * drm_dp_tunnel_atomic_get_required_bw - Get the BW required by a DP tunnel + * @tunnel_state: Atomic state of the queried tunnel + * + * Calculate the BW required by a tunnel adding up the required BW of all + * the streams in the tunnel. + * + * Return the total BW required by the tunnel. + */ +int drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state) +{ + int tunnel_bw = 0; + int i; + + if (!tunnel_state || !tunnel_state->stream_mask) + return 0; + + for (i = 0; i < hweight32(tunnel_state->stream_mask); i++) + tunnel_bw += tunnel_state->stream_bw[i]; + + return tunnel_bw; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_required_bw); + +/** + * drm_dp_tunnel_atomic_get_group_streams_in_state - Get mask of stream IDs in a group + * @state: Atomic state + * @tunnel: Tunnel object + * @stream_mask: Mask of streams in @tunnel's group + * + * Get the mask of all the stream IDs in the tunnel group of @tunnel. + * + * Return 0 in case of success - with the stream IDs in @stream_mask - or a + * negative error code in case of failure. + */ +int drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask) +{ + struct drm_dp_tunnel_group_state *group_state; + struct drm_dp_tunnel_state *tunnel_state; + + group_state = drm_dp_tunnel_atomic_get_group_state(state, tunnel); + if (IS_ERR(group_state)) + return PTR_ERR(group_state); + + *stream_mask = 0; + for_each_tunnel_state(group_state, tunnel_state) + *stream_mask |= tunnel_state->stream_mask; + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_get_group_streams_in_state); + +static int +drm_dp_tunnel_atomic_check_group_bw(struct drm_dp_tunnel_group_state *new_group_state, + u32 *failed_stream_mask) +{ + struct drm_dp_tunnel_group *group = to_group(new_group_state->base.obj); + struct drm_dp_tunnel_state *new_tunnel_state; + u32 group_stream_mask = 0; + int group_bw = 0; + + for_each_tunnel_state(new_group_state, new_tunnel_state) { + struct drm_dp_tunnel *tunnel = new_tunnel_state->tunnel_ref.tunnel; + int max_dprx_bw = get_max_dprx_bw(tunnel); + int tunnel_bw = drm_dp_tunnel_atomic_get_required_bw(new_tunnel_state); + + tun_dbg(tunnel, + "%sRequired %d/%d Mb/s total for tunnel.\n", + tunnel_bw > max_dprx_bw ? "Not enough BW: " : "", + DPTUN_BW_ARG(tunnel_bw), + DPTUN_BW_ARG(max_dprx_bw)); + + if (tunnel_bw > max_dprx_bw) { + *failed_stream_mask = new_tunnel_state->stream_mask; + return -ENOSPC; + } + + group_bw += min(roundup(tunnel_bw, tunnel->bw_granularity), + max_dprx_bw); + group_stream_mask |= new_tunnel_state->stream_mask; + } + + tun_grp_dbg(group, + "%sRequired %d/%d Mb/s total for tunnel group.\n", + group_bw > group->available_bw ? "Not enough BW: " : "", + DPTUN_BW_ARG(group_bw), + DPTUN_BW_ARG(group->available_bw)); + + if (group_bw > group->available_bw) { + *failed_stream_mask = group_stream_mask; + return -ENOSPC; + } + + return 0; +} + +/** + * drm_dp_tunnel_atomic_check_stream_bws - Check BW limit for all streams in state + * @state: Atomic state + * @failed_stream_mask: Mask of stream IDs with a BW limit failure + * + * Check the required BW of each DP tunnel in @state against both the DPRX BW + * limit of the tunnel and the BW limit of the tunnel group. Return a mask of + * stream IDs in @failed_stream_mask once a check fails. The mask will contain + * either all the streams in a tunnel (in case a DPRX BW limit check failed) or + * all the streams in a tunnel group (in case a group BW limit check failed). + * + * Return 0 if all the BW limit checks passed, -ENOSPC in case a BW limit + * check failed - with @failed_stream_mask containing the streams failing the + * check - or a negative error code otherwise. + */ +int drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask) +{ + struct drm_dp_tunnel_group_state *new_group_state; + int i; + + for_each_new_group_in_state(state, new_group_state, i) { + int ret; + + ret = drm_dp_tunnel_atomic_check_group_bw(new_group_state, + failed_stream_mask); + if (ret) + return ret; + } + + return 0; +} +EXPORT_SYMBOL(drm_dp_tunnel_atomic_check_stream_bws); + +static void destroy_mgr(struct drm_dp_tunnel_mgr *mgr) +{ + int i; + + for (i = 0; i < mgr->group_count; i++) { + cleanup_group(&mgr->groups[i]); + drm_WARN_ON(mgr->dev, !list_empty(&mgr->groups[i].tunnels)); + } + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + ref_tracker_dir_exit(&mgr->ref_tracker); +#endif + + kfree(mgr->groups); + kfree(mgr); +} + +/** + * drm_dp_tunnel_mgr_create - Create a DP tunnel manager + * @dev: DRM device object + * + * Creates a DP tunnel manager for @dev. + * + * Returns a pointer to the tunnel manager if created successfully or NULL in + * case of an error. + */ +struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count) +{ + struct drm_dp_tunnel_mgr *mgr; + int i; + + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); + if (!mgr) + return NULL; + + mgr->dev = dev; + init_waitqueue_head(&mgr->bw_req_queue); + + mgr->groups = kcalloc(max_group_count, sizeof(*mgr->groups), GFP_KERNEL); + if (!mgr->groups) { + kfree(mgr); + + return NULL; + } + +#ifdef CONFIG_DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE + ref_tracker_dir_init(&mgr->ref_tracker, 16, "dptun"); +#endif + + for (i = 0; i < max_group_count; i++) { + if (!init_group(mgr, &mgr->groups[i])) { + destroy_mgr(mgr); + + return NULL; + } + + mgr->group_count++; + } + + return mgr; +} +EXPORT_SYMBOL(drm_dp_tunnel_mgr_create); + +/** + * drm_dp_tunnel_mgr_destroy - Destroy DP tunnel manager + * @mgr: Tunnel manager object + * + * Destroy the tunnel manager. + */ +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr) +{ + destroy_mgr(mgr); +} +EXPORT_SYMBOL(drm_dp_tunnel_mgr_destroy); diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 281afff6ee4e..8bfd5d007be8 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -1382,6 +1382,66 @@ #define DP_HDCP_2_2_REG_STREAM_TYPE_OFFSET 0x69494 #define DP_HDCP_2_2_REG_DBG_OFFSET 0x69518 +/* DP-tunneling */ +#define DP_TUNNELING_OUI 0xe0000 +#define DP_TUNNELING_OUI_BYTES 3 + +#define DP_TUNNELING_DEV_ID 0xe0003 +#define DP_TUNNELING_DEV_ID_BYTES 6 + +#define DP_TUNNELING_HW_REV 0xe0009 +#define DP_TUNNELING_HW_REV_MAJOR_SHIFT 4 +#define DP_TUNNELING_HW_REV_MAJOR_MASK (0xf << DP_TUNNELING_HW_REV_MAJOR_SHIFT) +#define DP_TUNNELING_HW_REV_MINOR_SHIFT 0 +#define DP_TUNNELING_HW_REV_MINOR_MASK (0xf << DP_TUNNELING_HW_REV_MINOR_SHIFT) + +#define DP_TUNNELING_SW_REV_MAJOR 0xe000a +#define DP_TUNNELING_SW_REV_MINOR 0xe000b + +#define DP_TUNNELING_CAPABILITIES 0xe000d +#define DP_IN_BW_ALLOCATION_MODE_SUPPORT (1 << 7) +#define DP_PANEL_REPLAY_OPTIMIZATION_SUPPORT (1 << 6) +#define DP_TUNNELING_SUPPORT (1 << 0) + +#define DP_IN_ADAPTER_INFO 0xe000e +#define DP_IN_ADAPTER_NUMBER_BITS 7 +#define DP_IN_ADAPTER_NUMBER_MASK ((1 << DP_IN_ADAPTER_NUMBER_BITS) - 1) + +#define DP_USB4_DRIVER_ID 0xe000f +#define DP_USB4_DRIVER_ID_BITS 4 +#define DP_USB4_DRIVER_ID_MASK ((1 << DP_USB4_DRIVER_ID_BITS) - 1) + +#define DP_USB4_DRIVER_BW_CAPABILITY 0xe0020 +#define DP_USB4_DRIVER_BW_ALLOCATION_MODE_SUPPORT (1 << 7) + +#define DP_IN_ADAPTER_TUNNEL_INFORMATION 0xe0021 +#define DP_GROUP_ID_BITS 3 +#define DP_GROUP_ID_MASK ((1 << DP_GROUP_ID_BITS) - 1) + +#define DP_BW_GRANULARITY 0xe0022 +#define DP_BW_GRANULARITY_MASK 0x3 + +#define DP_ESTIMATED_BW 0xe0023 +#define DP_ALLOCATED_BW 0xe0024 + +#define DP_TUNNELING_STATUS 0xe0025 +#define DP_BW_ALLOCATION_CAPABILITY_CHANGED (1 << 3) +#define DP_ESTIMATED_BW_CHANGED (1 << 2) +#define DP_BW_REQUEST_SUCCEEDED (1 << 1) +#define DP_BW_REQUEST_FAILED (1 << 0) + +#define DP_TUNNELING_MAX_LINK_RATE 0xe0028 + +#define DP_TUNNELING_MAX_LANE_COUNT 0xe0029 +#define DP_TUNNELING_MAX_LANE_COUNT_MASK 0x1f + +#define DP_DPTX_BW_ALLOCATION_MODE_CONTROL 0xe0030 +#define DP_DISPLAY_DRIVER_BW_ALLOCATION_MODE_ENABLE (1 << 7) +#define DP_UNMASK_BW_ALLOCATION_IRQ (1 << 6) + +#define DP_REQUEST_BW 0xe0031 +#define MAX_DP_REQUEST_BW 255 + /* LTTPR: Link Training (LT)-tunable PHY Repeaters */ #define DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV 0xf0000 /* 1.3 */ #define DP_MAX_LINK_RATE_PHY_REPEATER 0xf0001 /* 1.4a */ diff --git a/include/drm/display/drm_dp_tunnel.h b/include/drm/display/drm_dp_tunnel.h new file mode 100644 index 000000000000..87212c847915 --- /dev/null +++ b/include/drm/display/drm_dp_tunnel.h @@ -0,0 +1,248 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __DRM_DP_TUNNEL_H__ +#define __DRM_DP_TUNNEL_H__ + +#include +#include +#include + +struct drm_dp_aux; + +struct drm_device; + +struct drm_atomic_state; +struct drm_dp_tunnel_mgr; +struct drm_dp_tunnel_state; + +struct ref_tracker; + +struct drm_dp_tunnel_ref { + struct drm_dp_tunnel *tunnel; + struct ref_tracker *tracker; +}; + +#ifdef CONFIG_DRM_DISPLAY_DP_TUNNEL + +struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker); + +void +drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker); + +static inline void drm_dp_tunnel_ref_get(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_ref *tunnel_ref) +{ + tunnel_ref->tunnel = drm_dp_tunnel_get(tunnel, &tunnel_ref->tracker); +} + +static inline void drm_dp_tunnel_ref_put(struct drm_dp_tunnel_ref *tunnel_ref) +{ + drm_dp_tunnel_put(tunnel_ref->tunnel, &tunnel_ref->tracker); + tunnel_ref->tunnel = NULL; +} + +struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux); +int drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel); +bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw); +int drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel); + +void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux); + +int drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel); +int drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel); + +const char *drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_old_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel); + +struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel); + +int drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw); +int drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask); + +int drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask); + +int drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state); + +struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count); +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr); + +#else + +static inline struct drm_dp_tunnel * +drm_dp_tunnel_get(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker) +{ + return NULL; +} + +static inline void +drm_dp_tunnel_put(struct drm_dp_tunnel *tunnel, struct ref_tracker **tracker) {} + +static inline void drm_dp_tunnel_ref_get(struct drm_dp_tunnel *tunnel, + struct drm_dp_tunnel_ref *tunnel_ref) {} + +static inline void drm_dp_tunnel_ref_put(struct drm_dp_tunnel_ref *tunnel_ref) {} + +static inline struct drm_dp_tunnel * +drm_dp_tunnel_detect(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int +drm_dp_tunnel_destroy(struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int drm_dp_tunnel_enable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline int drm_dp_tunnel_disable_bw_alloc(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline bool drm_dp_tunnel_bw_alloc_is_enabled(const struct drm_dp_tunnel *tunnel) +{ + return false; +} + +static inline int +drm_dp_tunnel_alloc_bw(struct drm_dp_tunnel *tunnel, int bw) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_get_allocated_bw(struct drm_dp_tunnel *tunnel) +{ + return -1; +} + +static inline int +drm_dp_tunnel_update_state(struct drm_dp_tunnel *tunnel) +{ + return -EOPNOTSUPP; +} + +static inline void drm_dp_tunnel_set_io_error(struct drm_dp_tunnel *tunnel) {} + +static inline int +drm_dp_tunnel_handle_irq(struct drm_dp_tunnel_mgr *mgr, + struct drm_dp_aux *aux) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_max_dprx_rate(const struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int +drm_dp_tunnel_max_dprx_lane_count(const struct drm_dp_tunnel *tunnel) +{ + return 0; +} + +static inline int +drm_dp_tunnel_available_bw(const struct drm_dp_tunnel *tunnel) +{ + return -1; +} + +static inline const char * +drm_dp_tunnel_name(const struct drm_dp_tunnel *tunnel) +{ + return NULL; +} + +static inline struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_state(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline struct drm_dp_tunnel_state * +drm_dp_tunnel_atomic_get_new_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline int +drm_dp_tunnel_atomic_set_stream_bw(struct drm_atomic_state *state, + struct drm_dp_tunnel *tunnel, + u8 stream_id, int bw) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_get_group_streams_in_state(struct drm_atomic_state *state, + const struct drm_dp_tunnel *tunnel, + u32 *stream_mask) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_check_stream_bws(struct drm_atomic_state *state, + u32 *failed_stream_mask) +{ + return -EOPNOTSUPP; +} + +static inline int +drm_dp_tunnel_atomic_get_required_bw(const struct drm_dp_tunnel_state *tunnel_state) +{ + return 0; +} + +static inline struct drm_dp_tunnel_mgr * +drm_dp_tunnel_mgr_create(struct drm_device *dev, int max_group_count) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +static inline +void drm_dp_tunnel_mgr_destroy(struct drm_dp_tunnel_mgr *mgr) {} + +#endif /* CONFIG_DRM_DISPLAY_DP_TUNNEL */ + +#endif /* __DRM_DP_TUNNEL_H__ */ From 1dd9d86ab60b9810cc99640fa7ebe42e1b9c8b4f Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:23 +0200 Subject: [PATCH 72/90] drm/i915: Fix display bpp limit computation during system resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The system resume display mode restoration should happen with an output configuration matching that of the suspend time saved mode. Since the restored mode configuration is subject to the bpp fallback logic, starting out with an unlimited bpp and reducing the bpp as required by any (MST) link BW limit, the resulting bpp will match the one during suspend only if the BW limit checks during suspend and resume are applied in an identical way. The latter is not guaranteed at the moment, since the pre-suspend MST topology may not be in place during resume (for instance if the MST sink was disconnected while being suspended), which makes the MST link BW check accept the unlimited bpp mode configuration unconditionally without ensuring that the required BW fits into the available MST link BW. To fix the above, initialize the bpp fallback logic with the max link bpp / force-FEC limits left behind by the suspend time mode save. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-4-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 3 +-- drivers/gpu/drm/i915/display/intel_link_bw.c | 22 ++++++++++++++++---- drivers/gpu/drm/i915/display/intel_link_bw.h | 2 +- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 607117ac2168..862ca6f9f381 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -6253,12 +6253,11 @@ static int intel_atomic_check_config(struct intel_atomic_state *state, static int intel_atomic_check_config_and_link(struct intel_atomic_state *state) { - struct drm_i915_private *i915 = to_i915(state->base.dev); struct intel_link_bw_limits new_limits; struct intel_link_bw_limits old_limits; int ret; - intel_link_bw_init_limits(i915, &new_limits); + intel_link_bw_init_limits(state, &new_limits); old_limits = new_limits; while (true) { diff --git a/drivers/gpu/drm/i915/display/intel_link_bw.c b/drivers/gpu/drm/i915/display/intel_link_bw.c index 9c6d35a405a1..27ea858897c9 100644 --- a/drivers/gpu/drm/i915/display/intel_link_bw.c +++ b/drivers/gpu/drm/i915/display/intel_link_bw.c @@ -6,6 +6,7 @@ #include "i915_drv.h" #include "intel_atomic.h" +#include "intel_crtc.h" #include "intel_display_types.h" #include "intel_dp_mst.h" #include "intel_fdi.h" @@ -13,19 +14,32 @@ /** * intel_link_bw_init_limits - initialize BW limits - * @i915: device instance + * @state: Atomic state * @limits: link BW limits * * Initialize @limits. */ -void intel_link_bw_init_limits(struct drm_i915_private *i915, struct intel_link_bw_limits *limits) +void intel_link_bw_init_limits(struct intel_atomic_state *state, + struct intel_link_bw_limits *limits) { + struct drm_i915_private *i915 = to_i915(state->base.dev); enum pipe pipe; limits->force_fec_pipes = 0; limits->bpp_limit_reached_pipes = 0; - for_each_pipe(i915, pipe) - limits->max_bpp_x16[pipe] = INT_MAX; + for_each_pipe(i915, pipe) { + const struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, + intel_crtc_for_pipe(i915, pipe)); + + if (state->base.duplicated && crtc_state) { + limits->max_bpp_x16[pipe] = crtc_state->max_link_bpp_x16; + if (crtc_state->fec_enable) + limits->force_fec_pipes |= BIT(pipe); + } else { + limits->max_bpp_x16[pipe] = INT_MAX; + } + } } /** diff --git a/drivers/gpu/drm/i915/display/intel_link_bw.h b/drivers/gpu/drm/i915/display/intel_link_bw.h index 2cf57307cc24..6b0ccfff59da 100644 --- a/drivers/gpu/drm/i915/display/intel_link_bw.h +++ b/drivers/gpu/drm/i915/display/intel_link_bw.h @@ -22,7 +22,7 @@ struct intel_link_bw_limits { int max_bpp_x16[I915_MAX_PIPES]; }; -void intel_link_bw_init_limits(struct drm_i915_private *i915, +void intel_link_bw_init_limits(struct intel_atomic_state *state, struct intel_link_bw_limits *limits); int intel_link_bw_reduce_bpp(struct intel_atomic_state *state, struct intel_link_bw_limits *limits, From d1e217d44b406e005a9dbca78c74a35b3da1a300 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:24 +0200 Subject: [PATCH 73/90] drm/i915/dp: Add support to notify MST connectors to retry modesets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On shared (Thunderbolt) links with DP tunnels, the modeset may need to be retried on all connectors on the link due to a link BW limitation arising only after the atomic check phase. To support this add a helper function queuing a work to retry the modeset on a given port's connector and at the same time any MST connector with streams through the same port. A follow-up change enabling the DP tunnel Bandwidth Allocation Mode will take this into use. v2: - Send the uevent only to enabled MST connectors. (Jouni) Cc: Jouni Högander Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-5-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 5 ++- drivers/gpu/drm/i915/display/intel_dp.c | 45 ++++++++++++++++++- drivers/gpu/drm/i915/display/intel_dp.h | 6 +++ .../drm/i915/display/intel_dp_link_training.c | 3 +- drivers/gpu/drm/i915/display/intel_dp_mst.c | 2 + 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 862ca6f9f381..a10a0ad2466a 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -8086,8 +8086,9 @@ void intel_hpd_poll_fini(struct drm_i915_private *i915) /* Kill all the work that may have been queued by hpd. */ drm_connector_list_iter_begin(&i915->drm, &conn_iter); for_each_intel_connector_iter(connector, &conn_iter) { - if (connector->modeset_retry_work.func) - cancel_work_sync(&connector->modeset_retry_work); + if (connector->modeset_retry_work.func && + cancel_work_sync(&connector->modeset_retry_work)) + drm_connector_put(&connector->base); if (connector->hdcp.shim) { cancel_delayed_work_sync(&connector->hdcp.check_work); cancel_work_sync(&connector->hdcp.prop_work); diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 217196196e50..88606e336a92 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2842,6 +2842,40 @@ intel_dp_audio_compute_config(struct intel_encoder *encoder, intel_dp_is_uhbr(pipe_config); } +void intel_dp_queue_modeset_retry_work(struct intel_connector *connector) +{ + struct drm_i915_private *i915 = to_i915(connector->base.dev); + + drm_connector_get(&connector->base); + if (!queue_work(i915->unordered_wq, &connector->modeset_retry_work)) + drm_connector_put(&connector->base); +} + +void +intel_dp_queue_modeset_retry_for_link(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct intel_connector *connector; + struct intel_digital_connector_state *conn_state; + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + int i; + + if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) { + intel_dp_queue_modeset_retry_work(intel_dp->attached_connector); + + return; + } + + for_each_new_intel_connector_in_state(state, connector, conn_state, i) { + if (!conn_state->base.crtc) + continue; + + if (connector->mst_port == intel_dp) + intel_dp_queue_modeset_retry_work(connector); + } +} + int intel_dp_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, @@ -6441,6 +6475,14 @@ static void intel_dp_modeset_retry_work_fn(struct work_struct *work) mutex_unlock(&connector->dev->mode_config.mutex); /* Send Hotplug uevent so userspace can reprobe */ drm_kms_helper_connector_hotplug_event(connector); + + drm_connector_put(connector); +} + +void intel_dp_init_modeset_retry_work(struct intel_connector *connector) +{ + INIT_WORK(&connector->modeset_retry_work, + intel_dp_modeset_retry_work_fn); } bool @@ -6457,8 +6499,7 @@ intel_dp_init_connector(struct intel_digital_port *dig_port, int type; /* Initialize the work for modeset in case of link train failure */ - INIT_WORK(&intel_connector->modeset_retry_work, - intel_dp_modeset_retry_work_fn); + intel_dp_init_modeset_retry_work(intel_connector); if (drm_WARN(dev, dig_port->max_lanes < 1, "Not enough lanes (%d) for DP on [ENCODER:%d:%s]\n", diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 530cc97bc42f..e2875e03afba 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -43,6 +43,12 @@ void intel_dp_adjust_compliance_config(struct intel_dp *intel_dp, bool intel_dp_limited_color_range(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); int intel_dp_min_bpp(enum intel_output_format output_format); +void intel_dp_init_modeset_retry_work(struct intel_connector *connector); +void intel_dp_queue_modeset_retry_work(struct intel_connector *connector); +void +intel_dp_queue_modeset_retry_for_link(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state); bool intel_dp_init_connector(struct intel_digital_port *dig_port, struct intel_connector *intel_connector); void intel_dp_set_link_params(struct intel_dp *intel_dp, diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 1abfafbbfa75..7b140cbf8dd3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -1075,7 +1075,6 @@ static void intel_dp_schedule_fallback_link_training(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { struct intel_connector *intel_connector = intel_dp->attached_connector; - struct drm_i915_private *i915 = dp_to_i915(intel_dp); if (!intel_digital_port_connected(&dp_to_dig_port(intel_dp)->base)) { lt_dbg(intel_dp, DP_PHY_DPRX, "Link Training failed on disconnected sink.\n"); @@ -1093,7 +1092,7 @@ static void intel_dp_schedule_fallback_link_training(struct intel_dp *intel_dp, } /* Schedule a Hotplug Uevent to userspace to start modeset */ - queue_work(i915->unordered_wq, &intel_connector->modeset_retry_work); + intel_dp_queue_modeset_retry_work(intel_connector); } /* Perform the link training on all LTTPRs and the DPRX on a link. */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 5307ddd4edcf..c685b64bb781 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -1546,6 +1546,8 @@ static struct drm_connector *intel_dp_add_mst_connector(struct drm_dp_mst_topolo intel_connector->port = port; drm_dp_mst_get_port_malloc(port); + intel_dp_init_modeset_retry_work(intel_connector); + intel_connector->dp.dsc_decompression_aux = drm_dp_mst_dsc_aux_for_port(port); intel_dp_mst_read_decompression_port_dsc_caps(intel_dp, intel_connector); intel_connector->dp.dsc_hblank_expansion_quirk = From 204863d668ff06b8b33e626766e6e75e48ed9639 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:25 +0200 Subject: [PATCH 74/90] drm/i915/dp: Use drm_dp_max_dprx_data_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of intel_dp_max_data_rate() use the equivalent drm_dp_max_dprx_data_rate() which was copied from the former one in a previous patch. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-6-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_display.c | 2 +- drivers/gpu/drm/i915/display/intel_dp.c | 62 +++----------------- drivers/gpu/drm/i915/display/intel_dp.h | 1 - drivers/gpu/drm/i915/display/intel_dp_mst.c | 2 +- 4 files changed, 10 insertions(+), 57 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index a10a0ad2466a..444d09e6a30d 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -2478,7 +2478,7 @@ intel_link_compute_m_n(u16 bits_per_pixel_x16, int nlanes, u32 link_symbol_clock = intel_dp_link_symbol_clock(link_clock); u32 data_m = intel_dp_effective_data_rate(pixel_clock, bits_per_pixel_x16, bw_overhead); - u32 data_n = intel_dp_max_data_rate(link_clock, nlanes); + u32 data_n = drm_dp_max_dprx_data_rate(link_clock, nlanes); /* * Windows/BIOS uses fixed M/N values always. Follow suit. diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 88606e336a92..0a2ee4339214 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -383,52 +383,6 @@ int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16, 1000000 * 16 * 8); } -/* - * Given a link rate and lanes, get the data bandwidth. - * - * Data bandwidth is the actual payload rate, which depends on the data - * bandwidth efficiency and the link rate. - * - * For 8b/10b channel encoding, SST and non-FEC, the data bandwidth efficiency - * is 80%. For example, for a 1.62 Gbps link, 1.62*10^9 bps * 0.80 * (1/8) = - * 162000 kBps. With 8-bit symbols, we have 162000 kHz symbol clock. Just by - * coincidence, the port clock in kHz matches the data bandwidth in kBps, and - * they equal the link bit rate in Gbps multiplied by 100000. (Note that this no - * longer holds for data bandwidth as soon as FEC or MST is taken into account!) - * - * For 128b/132b channel encoding, the data bandwidth efficiency is 96.71%. For - * example, for a 10 Gbps link, 10*10^9 bps * 0.9671 * (1/8) = 1208875 - * kBps. With 32-bit symbols, we have 312500 kHz symbol clock. The value 1000000 - * does not match the symbol clock, the port clock (not even if you think in - * terms of a byte clock), nor the data bandwidth. It only matches the link bit - * rate in units of 10000 bps. - */ -int -intel_dp_max_data_rate(int max_link_rate, int max_lanes) -{ - int ch_coding_efficiency = - drm_dp_bw_channel_coding_efficiency(drm_dp_is_uhbr_rate(max_link_rate)); - int max_link_rate_kbps = max_link_rate * 10; - - /* - * UHBR rates always use 128b/132b channel encoding, and have - * 97.71% data bandwidth efficiency. Consider max_link_rate the - * link bit rate in units of 10000 bps. - */ - /* - * Lower than UHBR rates always use 8b/10b channel encoding, and have - * 80% data bandwidth efficiency for SST non-FEC. However, this turns - * out to be a nop by coincidence: - * - * int max_link_rate_kbps = max_link_rate * 10; - * max_link_rate_kbps = DIV_ROUND_DOWN_ULL(max_link_rate_kbps * 8, 10); - * max_link_rate = max_link_rate_kbps / 8; - */ - return DIV_ROUND_DOWN_ULL(mul_u32_u32(max_link_rate_kbps * max_lanes, - ch_coding_efficiency), - 1000000 * 8); -} - bool intel_dp_can_bigjoiner(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -658,7 +612,7 @@ static bool intel_dp_can_link_train_fallback_for_edp(struct intel_dp *intel_dp, int mode_rate, max_rate; mode_rate = intel_dp_link_required(fixed_mode->clock, 18); - max_rate = intel_dp_max_data_rate(link_rate, lane_count); + max_rate = drm_dp_max_dprx_data_rate(link_rate, lane_count); if (mode_rate > max_rate) return false; @@ -1262,7 +1216,7 @@ intel_dp_mode_valid(struct drm_connector *_connector, max_link_clock = intel_dp_max_link_rate(intel_dp); max_lanes = intel_dp_max_lane_count(intel_dp); - max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); + max_rate = drm_dp_max_dprx_data_rate(max_link_clock, max_lanes); mode_rate = intel_dp_link_required(target_clock, intel_dp_mode_min_output_bpp(connector, mode)); @@ -1612,8 +1566,8 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, for (lane_count = limits->min_lane_count; lane_count <= limits->max_lane_count; lane_count <<= 1) { - link_avail = intel_dp_max_data_rate(link_rate, - lane_count); + link_avail = drm_dp_max_dprx_data_rate(link_rate, + lane_count); if (mode_rate <= link_avail) { pipe_config->lane_count = lane_count; @@ -2467,8 +2421,8 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, "DP link rate required %i available %i\n", intel_dp_link_required(adjusted_mode->crtc_clock, to_bpp_int_roundup(pipe_config->dsc.compressed_bpp_x16)), - intel_dp_max_data_rate(pipe_config->port_clock, - pipe_config->lane_count)); + drm_dp_max_dprx_data_rate(pipe_config->port_clock, + pipe_config->lane_count)); } else { drm_dbg_kms(&i915->drm, "DP lane count %d clock %d bpp %d\n", pipe_config->lane_count, pipe_config->port_clock, @@ -2478,8 +2432,8 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, "DP link rate required %i available %i\n", intel_dp_link_required(adjusted_mode->crtc_clock, pipe_config->pipe_bpp), - intel_dp_max_data_rate(pipe_config->port_clock, - pipe_config->lane_count)); + drm_dp_max_dprx_data_rate(pipe_config->port_clock, + pipe_config->lane_count)); } return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index e2875e03afba..20252984b3b0 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -111,7 +111,6 @@ bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp); int intel_dp_link_required(int pixel_clock, int bpp); int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16, int bw_overhead); -int intel_dp_max_data_rate(int max_link_rate, int max_lanes); bool intel_dp_can_bigjoiner(struct intel_dp *intel_dp); bool intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index c685b64bb781..b4d4bb90126e 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -1299,7 +1299,7 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector, max_link_clock = intel_dp_max_link_rate(intel_dp); max_lanes = intel_dp_max_lane_count(intel_dp); - max_rate = intel_dp_max_data_rate(max_link_clock, max_lanes); + max_rate = drm_dp_max_dprx_data_rate(max_link_clock, max_lanes); mode_rate = intel_dp_link_required(mode->clock, min_bpp); ret = drm_modeset_lock(&mgr->base.lock, ctx); From e35cce9371fe1dd88a40cce676b12a98d36fcd77 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:26 +0200 Subject: [PATCH 75/90] drm/i915/dp: Factor out intel_dp_config_required_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out intel_dp_config_required_rate() used by a follow-up patch enabling the DP tunnel BW allocation mode. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-7-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 43 +++++++++++-------------- drivers/gpu/drm/i915/display/intel_dp.h | 1 + 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 0a2ee4339214..c7e3e4efc324 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2343,6 +2343,17 @@ intel_dp_compute_config_limits(struct intel_dp *intel_dp, limits); } +int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state) +{ + const struct drm_display_mode *adjusted_mode = + &crtc_state->hw.adjusted_mode; + int bpp = crtc_state->dsc.compression_enable ? + to_bpp_int_roundup(crtc_state->dsc.compressed_bpp_x16) : + crtc_state->pipe_bpp; + + return intel_dp_link_required(adjusted_mode->crtc_clock, bpp); +} + static int intel_dp_compute_link_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, @@ -2410,31 +2421,15 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, return ret; } - if (pipe_config->dsc.compression_enable) { - drm_dbg_kms(&i915->drm, - "DP lane count %d clock %d Input bpp %d Compressed bpp " BPP_X16_FMT "\n", - pipe_config->lane_count, pipe_config->port_clock, - pipe_config->pipe_bpp, - BPP_X16_ARGS(pipe_config->dsc.compressed_bpp_x16)); + drm_dbg_kms(&i915->drm, + "DP lane count %d clock %d bpp input %d compressed " BPP_X16_FMT " link rate required %d available %d\n", + pipe_config->lane_count, pipe_config->port_clock, + pipe_config->pipe_bpp, + BPP_X16_ARGS(pipe_config->dsc.compressed_bpp_x16), + intel_dp_config_required_rate(pipe_config), + drm_dp_max_dprx_data_rate(pipe_config->port_clock, + pipe_config->lane_count)); - drm_dbg_kms(&i915->drm, - "DP link rate required %i available %i\n", - intel_dp_link_required(adjusted_mode->crtc_clock, - to_bpp_int_roundup(pipe_config->dsc.compressed_bpp_x16)), - drm_dp_max_dprx_data_rate(pipe_config->port_clock, - pipe_config->lane_count)); - } else { - drm_dbg_kms(&i915->drm, "DP lane count %d clock %d bpp %d\n", - pipe_config->lane_count, pipe_config->port_clock, - pipe_config->pipe_bpp); - - drm_dbg_kms(&i915->drm, - "DP link rate required %i available %i\n", - intel_dp_link_required(adjusted_mode->crtc_clock, - pipe_config->pipe_bpp), - drm_dp_max_dprx_data_rate(pipe_config->port_clock, - pipe_config->lane_count)); - } return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 20252984b3b0..4667870fed31 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -100,6 +100,7 @@ void intel_dp_mst_suspend(struct drm_i915_private *dev_priv); void intel_dp_mst_resume(struct drm_i915_private *dev_priv); int intel_dp_max_link_rate(struct intel_dp *intel_dp); int intel_dp_max_lane_count(struct intel_dp *intel_dp); +int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, From 4ba732a64cdb66daf1eac0306a98c1bbfbe3aac1 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:27 +0200 Subject: [PATCH 76/90] drm/i915/dp: Export intel_dp_max_common_rate/lane_count() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export intel_dp_max_common_rate() and intel_dp_max_lane_count() used by a follow-up patch enabling the DP tunnel BW allocation mode. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-8-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 4 ++-- drivers/gpu/drm/i915/display/intel_dp.h | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c7e3e4efc324..20e04cbdffca 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -309,7 +309,7 @@ static int intel_dp_common_rate(struct intel_dp *intel_dp, int index) } /* Theoretical max between source and sink */ -static int intel_dp_max_common_rate(struct intel_dp *intel_dp) +int intel_dp_max_common_rate(struct intel_dp *intel_dp) { return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); } @@ -326,7 +326,7 @@ static int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) } /* Theoretical max between source and sink */ -static int intel_dp_max_common_lane_count(struct intel_dp *intel_dp) +int intel_dp_max_common_lane_count(struct intel_dp *intel_dp) { struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); int source_max = intel_dp_max_source_lane_count(dig_port); diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 4667870fed31..ee0600b4425e 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -102,6 +102,8 @@ int intel_dp_max_link_rate(struct intel_dp *intel_dp); int intel_dp_max_lane_count(struct intel_dp *intel_dp); int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); +int intel_dp_max_common_rate(struct intel_dp *intel_dp); +int intel_dp_max_common_lane_count(struct intel_dp *intel_dp); void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, u8 *link_bw, u8 *rate_select); From 85e5be68217be3d131a8a6ea1aba7db942dd50ea Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:28 +0200 Subject: [PATCH 77/90] drm/i915/dp: Factor out intel_dp_update_sink_caps() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out a function updating the sink's link rate and lane count capabilities, used by a follow-up patch enabling the DP tunnel BW allocation mode. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-9-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 11 ++++++++--- drivers/gpu/drm/i915/display/intel_dp.h | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 20e04cbdffca..520c04245c2d 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -3944,6 +3944,13 @@ intel_dp_has_sink_count(struct intel_dp *intel_dp) &intel_dp->desc); } +void intel_dp_update_sink_caps(struct intel_dp *intel_dp) +{ + intel_dp_set_sink_rates(intel_dp); + intel_dp_set_max_sink_lane_count(intel_dp); + intel_dp_set_common_rates(intel_dp); +} + static bool intel_dp_get_dpcd(struct intel_dp *intel_dp) { @@ -3960,9 +3967,7 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) drm_dp_read_desc(&intel_dp->aux, &intel_dp->desc, drm_dp_is_branch(intel_dp->dpcd)); - intel_dp_set_sink_rates(intel_dp); - intel_dp_set_max_sink_lane_count(intel_dp); - intel_dp_set_common_rates(intel_dp); + intel_dp_update_sink_caps(intel_dp); } if (intel_dp_has_sink_count(intel_dp)) { diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index ee0600b4425e..10e859b62bbe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -104,6 +104,7 @@ int intel_dp_config_required_rate(const struct intel_crtc_state *crtc_state); int intel_dp_rate_select(struct intel_dp *intel_dp, int rate); int intel_dp_max_common_rate(struct intel_dp *intel_dp); int intel_dp_max_common_lane_count(struct intel_dp *intel_dp); +void intel_dp_update_sink_caps(struct intel_dp *intel_dp); void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, u8 *link_bw, u8 *rate_select); From 363c31787344f240b7cef68ec2734665ec292f7e Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:29 +0200 Subject: [PATCH 78/90] drm/i915/dp: Factor out intel_dp_read_dprx_caps() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out a function to read the sink's DPRX capabilities used by a follow-up patch enabling the DP tunnel BW allocation mode. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-10-imre.deak@intel.com --- .../drm/i915/display/intel_dp_link_training.c | 30 +++++++++++++++---- .../drm/i915/display/intel_dp_link_training.h | 1 + 2 files changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.c b/drivers/gpu/drm/i915/display/intel_dp_link_training.c index 7b140cbf8dd3..fb84ca98bb7a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -162,6 +162,28 @@ static int intel_dp_init_lttpr(struct intel_dp *intel_dp, const u8 dpcd[DP_RECEI return lttpr_count; } +int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_SIZE]) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + + if (intel_dp_is_edp(intel_dp)) + return 0; + + /* + * Detecting LTTPRs must be avoided on platforms with an AUX timeout + * period < 3.2ms. (see DP Standard v2.0, 2.11.2, 3.6.6.1). + */ + if (DISPLAY_VER(i915) >= 10 && !IS_GEMINILAKE(i915)) + if (drm_dp_dpcd_probe(&intel_dp->aux, + DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV)) + return -EIO; + + if (drm_dp_read_dpcd_caps(&intel_dp->aux, dpcd)) + return -EIO; + + return 0; +} + /** * intel_dp_init_lttpr_and_dprx_caps - detect LTTPR and DPRX caps, init the LTTPR link training mode * @intel_dp: Intel DP struct @@ -192,12 +214,10 @@ int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp) if (!intel_dp_is_edp(intel_dp) && (DISPLAY_VER(i915) >= 10 && !IS_GEMINILAKE(i915))) { u8 dpcd[DP_RECEIVER_CAP_SIZE]; + int err = intel_dp_read_dprx_caps(intel_dp, dpcd); - if (drm_dp_dpcd_probe(&intel_dp->aux, DP_LT_TUNABLE_PHY_REPEATER_FIELD_DATA_STRUCTURE_REV)) - return -EIO; - - if (drm_dp_read_dpcd_caps(&intel_dp->aux, dpcd)) - return -EIO; + if (err != 0) + return err; lttpr_count = intel_dp_init_lttpr(intel_dp, dpcd); } diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_training.h b/drivers/gpu/drm/i915/display/intel_dp_link_training.h index 2c8f2775891b..19836a8a4f90 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -11,6 +11,7 @@ struct intel_crtc_state; struct intel_dp; +int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_SIZE]); int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp); void intel_dp_get_adjust_train(struct intel_dp *intel_dp, From a4ea61b7482f56cc99391ccf65f13dec2ec51d1d Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:30 +0200 Subject: [PATCH 79/90] drm/i915/dp: Add intel_dp_max_link_data_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add intel_dp_max_link_data_rate() to get the link BW vs. the sink DPRX BW used by a follow-up patch enabling the DP tunnel BW allocation mode. The link BW can be below the DPRX BW due to a BW limitation on a link shared by multiple sinks. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-11-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 32 +++++++++++++++++---- drivers/gpu/drm/i915/display/intel_dp.h | 2 ++ drivers/gpu/drm/i915/display/intel_dp_mst.c | 3 +- 3 files changed, 30 insertions(+), 7 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 520c04245c2d..d9e75922ff8f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -383,6 +383,22 @@ int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16, 1000000 * 16 * 8); } +/** + * intel_dp_max_link_data_rate: Calculate the maximum rate for the given link params + * @intel_dp: Intel DP object + * @max_dprx_rate: Maximum data rate of the DPRX + * @max_dprx_lanes: Maximum lane count of the DPRX + * + * Calculate the maximum data rate for the provided link parameters. + * + * Returns the maximum data rate in kBps units. + */ +int intel_dp_max_link_data_rate(struct intel_dp *intel_dp, + int max_dprx_rate, int max_dprx_lanes) +{ + return drm_dp_max_dprx_data_rate(max_dprx_rate, max_dprx_lanes); +} + bool intel_dp_can_bigjoiner(struct intel_dp *intel_dp) { struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); @@ -612,7 +628,7 @@ static bool intel_dp_can_link_train_fallback_for_edp(struct intel_dp *intel_dp, int mode_rate, max_rate; mode_rate = intel_dp_link_required(fixed_mode->clock, 18); - max_rate = drm_dp_max_dprx_data_rate(link_rate, lane_count); + max_rate = intel_dp_max_link_data_rate(intel_dp, link_rate, lane_count); if (mode_rate > max_rate) return false; @@ -1216,7 +1232,8 @@ intel_dp_mode_valid(struct drm_connector *_connector, max_link_clock = intel_dp_max_link_rate(intel_dp); max_lanes = intel_dp_max_lane_count(intel_dp); - max_rate = drm_dp_max_dprx_data_rate(max_link_clock, max_lanes); + max_rate = intel_dp_max_link_data_rate(intel_dp, max_link_clock, max_lanes); + mode_rate = intel_dp_link_required(target_clock, intel_dp_mode_min_output_bpp(connector, mode)); @@ -1566,8 +1583,10 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, for (lane_count = limits->min_lane_count; lane_count <= limits->max_lane_count; lane_count <<= 1) { - link_avail = drm_dp_max_dprx_data_rate(link_rate, - lane_count); + link_avail = intel_dp_max_link_data_rate(intel_dp, + link_rate, + lane_count); + if (mode_rate <= link_avail) { pipe_config->lane_count = lane_count; @@ -2427,8 +2446,9 @@ intel_dp_compute_link_config(struct intel_encoder *encoder, pipe_config->pipe_bpp, BPP_X16_ARGS(pipe_config->dsc.compressed_bpp_x16), intel_dp_config_required_rate(pipe_config), - drm_dp_max_dprx_data_rate(pipe_config->port_clock, - pipe_config->lane_count)); + intel_dp_max_link_data_rate(intel_dp, + pipe_config->port_clock, + pipe_config->lane_count)); return 0; } diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 10e859b62bbe..564a587e2d01 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -115,6 +115,8 @@ bool intel_dp_get_colorimetry_status(struct intel_dp *intel_dp); int intel_dp_link_required(int pixel_clock, int bpp); int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16, int bw_overhead); +int intel_dp_max_link_data_rate(struct intel_dp *intel_dp, + int max_dprx_rate, int max_dprx_lanes); bool intel_dp_can_bigjoiner(struct intel_dp *intel_dp); bool intel_dp_needs_vsc_sdp(const struct intel_crtc_state *crtc_state, const struct drm_connector_state *conn_state); diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index b4d4bb90126e..4b6c8ff97429 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -1299,7 +1299,8 @@ intel_dp_mst_mode_valid_ctx(struct drm_connector *connector, max_link_clock = intel_dp_max_link_rate(intel_dp); max_lanes = intel_dp_max_lane_count(intel_dp); - max_rate = drm_dp_max_dprx_data_rate(max_link_clock, max_lanes); + max_rate = intel_dp_max_link_data_rate(intel_dp, + max_link_clock, max_lanes); mode_rate = intel_dp_link_required(mode->clock, min_bpp); ret = drm_modeset_lock(&mgr->base.lock, ctx); From 199c7d75b2676b64b0b3cda075450d59debe612b Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 26 Feb 2024 20:52:45 +0200 Subject: [PATCH 80/90] drm/i915/dp: Sync instead of try-sync commits when getting active pipes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sync instead of only try-sync non-blocking commits when getting the active pipes through a given DP port. Atm intel_dp_get_active_pipes() will only try to sync a given pipe and if that would block ignore the pipe. This was supposed to avoid link retraining in case a pending modeset would do that anyway, however that could incorrectly ignore fastset pipes as well for instance (which don't retraing the link). The TC port reset path needs to handle all pipes, even if a waiting for a pending commit would block. To account for the above cases sync all the pipes unconditionally. This also prepares for a follow-up change enabling the DP tunnel BW allocation mode which needs to ensure that all active pipes are synced and returned from intel_dp_get_active_pipes(). v2: - Add a separate function to try-sync the pipes. (Ville) v3: - Just sync the pipes unconditionally in intel_dp_get_active_pipes(). (Ville) Cc: Ville Syrjälä Reviewed-by: Uma Shankar (v2) Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240226185246.1276018-3-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index d9e75922ff8f..8ae06aa42ddc 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5048,9 +5048,10 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp, if (!crtc_state->hw.active) continue; - if (conn_state->commit && - !try_wait_for_completion(&conn_state->commit->hw_done)) - continue; + if (conn_state->commit) + drm_WARN_ON(&i915->drm, + !wait_for_completion_timeout(&conn_state->commit->hw_done, + msecs_to_jiffies(5000))); *pipe_mask |= BIT(crtc->pipe); } From 91888b5b1ad2fda3f4c6b8de5dd42dbe8b90ac2c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Mon, 26 Feb 2024 20:52:46 +0200 Subject: [PATCH 81/90] drm/i915/dp: Add support for DP tunnel BW allocation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support to enable the DP tunnel BW allocation mode. Follow-up patches will call the required helpers added here to prepare for a modeset on a link with DP tunnels, the last change in the patchset actually enabling BWA. With BWA enabled, the driver will expose the full mode list a display supports, regardless of any BW limitation on a shared (Thunderbolt) link. Such BW limits will be checked against only during a modeset, when the driver has the full knowledge of each display's BW requirement. If the link BW changes in a way that a connector's modelist may also change, userspace will get a hotplug notification for all the connectors sharing the same link (so it can adjust the mode used for a display). The BW limitation can change at any point, asynchronously to modesets on a given connector, so a modeset can fail even though the atomic check for it passed. In such scenarios userspace will get a bad link notification and in response is supposed to retry the modeset. v2: - Fix old vs. new connector state in intel_dp_tunnel_atomic_check_state(). (Ville) - Fix propagating the error from intel_dp_tunnel_atomic_compute_stream_bw(). (Ville) - Move tunnel==NULL checks from driver to DRM core helpers. (Ville) - Simplify return flow from intel_dp_tunnel_detect(). (Ville) - s/dp_tunnel_state/inherited_dp_tunnels (Ville) - Simplify struct intel_dp_tunnel_inherited_state. (Ville) - Unconstify object pointers (vs. states) where possible. (Ville) - Init crtc_state while declaring it in check_group_state(). (Ville) - Join obj->base.id, obj->name arg lines in debug prints to reduce LOC. (Ville) - Add/rework intel_dp_tunnel_atomic_alloc_bw() to prepare for moving the BW allocation from encoder hooks up to intel_atomic_commit_tail() later in the patchset. - Disable BW alloc mode during system suspend. - Allocate the required BW for all tunnels during system resume. - Add intel_dp_tunnel_atomic_clear_stream_bw() instead of the open-coded sequence in a follow-up patch. - Add function documentation to all exported functions. - Add CONFIG_USB4 dependency to CONFIG_DRM_I915_DP_TUNNEL. v3: - Rebase on intel_dp_get_active_pipes() change in previous patch. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Reviewed-by: Uma Shankar Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240226185246.1276018-4-imre.deak@intel.com --- drivers/gpu/drm/i915/Kconfig | 14 + drivers/gpu/drm/i915/Kconfig.debug | 1 + drivers/gpu/drm/i915/Makefile | 3 + drivers/gpu/drm/i915/display/intel_atomic.c | 2 + .../gpu/drm/i915/display/intel_display_core.h | 1 + .../drm/i915/display/intel_display_types.h | 9 + .../gpu/drm/i915/display/intel_dp_tunnel.c | 811 ++++++++++++++++++ .../gpu/drm/i915/display/intel_dp_tunnel.h | 133 +++ 8 files changed, 974 insertions(+) create mode 100644 drivers/gpu/drm/i915/display/intel_dp_tunnel.c create mode 100644 drivers/gpu/drm/i915/display/intel_dp_tunnel.h diff --git a/drivers/gpu/drm/i915/Kconfig b/drivers/gpu/drm/i915/Kconfig index b5d6e3352071..fcf91afd2fb2 100644 --- a/drivers/gpu/drm/i915/Kconfig +++ b/drivers/gpu/drm/i915/Kconfig @@ -155,6 +155,20 @@ config DRM_I915_PXP protected session and manage the status of the alive software session, as well as its life cycle. +config DRM_I915_DP_TUNNEL + bool "Enable DP tunnel support" + depends on DRM_I915 + depends on USB4 + select DRM_DISPLAY_DP_TUNNEL + default y + help + Choose this option to detect DP tunnels and enable the Bandwidth + Allocation mode for such tunnels. This allows using the maximum + resolution allowed by the link BW on all displays sharing the + link BW, for instance on a Thunderbolt link. + + If in doubt, say "Y". + menu "drm/i915 Debugging" depends on DRM_I915 depends on EXPERT diff --git a/drivers/gpu/drm/i915/Kconfig.debug b/drivers/gpu/drm/i915/Kconfig.debug index 5b7162076850..bc18e2d9ea05 100644 --- a/drivers/gpu/drm/i915/Kconfig.debug +++ b/drivers/gpu/drm/i915/Kconfig.debug @@ -28,6 +28,7 @@ config DRM_I915_DEBUG select STACKDEPOT select STACKTRACE select DRM_DP_AUX_CHARDEV + select DRM_DISPLAY_DEBUG_DP_TUNNEL_STATE if DRM_I915_DP_TUNNEL select X86_MSR # used by igt/pm_rpm select DRM_VGEM # used by igt/prime_vgem (dmabuf interop checks) select DRM_DEBUG_MM if DRM=y diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index c13f14edb508..3ef6ed41e62b 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -369,6 +369,9 @@ i915-y += \ display/vlv_dsi.o \ display/vlv_dsi_pll.o +i915-$(CONFIG_DRM_I915_DP_TUNNEL) += \ + display/intel_dp_tunnel.o + i915-y += \ i915_perf.o diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index ec0d5168b503..96ab37e15899 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -29,6 +29,7 @@ * See intel_atomic_plane.c for the plane-specific atomic functionality. */ +#include #include #include #include @@ -38,6 +39,7 @@ #include "intel_atomic.h" #include "intel_cdclk.h" #include "intel_display_types.h" +#include "intel_dp_tunnel.h" #include "intel_global_state.h" #include "intel_hdcp.h" #include "intel_psr.h" diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index fdeaac994e17..2167dbee5eea 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -524,6 +524,7 @@ struct intel_display { } wq; /* Grouping using named structs. Keep sorted. */ + struct drm_dp_tunnel_mgr *dp_tunnel_mgr; struct intel_audio audio; struct intel_dpll dpll; struct intel_fbc *fbc[I915_MAX_FBCS]; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 8ce986fadd9a..860e867586f4 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -682,6 +683,8 @@ struct intel_atomic_state { struct intel_shared_dpll_state shared_dpll[I915_NUM_PLLS]; + struct intel_dp_tunnel_inherited_state *inherited_dp_tunnels; + /* * Current watermarks can't be trusted during hardware readout, so * don't bother calculating intermediate watermarks. @@ -1379,6 +1382,9 @@ struct intel_crtc_state { struct drm_dsc_config config; } dsc; + /* DP tunnel used for BW allocation. */ + struct drm_dp_tunnel_ref dp_tunnel_ref; + /* HSW+ linetime watermarks */ u16 linetime; u16 ips_linetime; @@ -1789,6 +1795,9 @@ struct intel_dp { /* connector directly attached - won't be use for modeset in mst world */ struct intel_connector *attached_connector; + struct drm_dp_tunnel *tunnel; + bool tunnel_suspended:1; + /* mst connector list */ struct intel_dp_mst_encoder *mst_encoders[I915_MAX_PIPES]; struct drm_dp_mst_topology_mgr mst_mgr; diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c new file mode 100644 index 000000000000..75d76f91ecbd --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -0,0 +1,811 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include "i915_drv.h" + +#include + +#include "intel_atomic.h" +#include "intel_display_limits.h" +#include "intel_display_types.h" +#include "intel_dp.h" +#include "intel_dp_link_training.h" +#include "intel_dp_mst.h" +#include "intel_dp_tunnel.h" +#include "intel_link_bw.h" + +struct intel_dp_tunnel_inherited_state { + struct drm_dp_tunnel_ref ref[I915_MAX_PIPES]; +}; + +/** + * intel_dp_tunnel_disconnect - Disconnect a DP tunnel from a port + * @intel_dp: DP port object the tunnel is connected to + * + * Disconnect a DP tunnel from @intel_dp, destroying any related state. This + * should be called after detecting a sink-disconnect event from the port. + */ +void intel_dp_tunnel_disconnect(struct intel_dp *intel_dp) +{ + drm_dp_tunnel_destroy(intel_dp->tunnel); + intel_dp->tunnel = NULL; +} + +/** + * intel_dp_tunnel_destroy - Destroy a DP tunnel + * @intel_dp: DP port object the tunnel is connected to + * + * Destroy a DP tunnel connected to @intel_dp, after disabling the BW + * allocation mode on the tunnel. This should be called while destroying the + * port. + */ +void intel_dp_tunnel_destroy(struct intel_dp *intel_dp) +{ + if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + drm_dp_tunnel_disable_bw_alloc(intel_dp->tunnel); + + intel_dp_tunnel_disconnect(intel_dp); +} + +static int kbytes_to_mbits(int kbytes) +{ + return DIV_ROUND_UP(kbytes * 8, 1000); +} + +static int get_current_link_bw(struct intel_dp *intel_dp, + bool *below_dprx_bw) +{ + int rate = intel_dp_max_common_rate(intel_dp); + int lane_count = intel_dp_max_common_lane_count(intel_dp); + int bw; + + bw = intel_dp_max_link_data_rate(intel_dp, rate, lane_count); + *below_dprx_bw = bw < drm_dp_max_dprx_data_rate(rate, lane_count); + + return bw; +} + +static int update_tunnel_state(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + bool old_bw_below_dprx; + bool new_bw_below_dprx; + int old_bw; + int new_bw; + int ret; + + old_bw = get_current_link_bw(intel_dp, &old_bw_below_dprx); + + ret = drm_dp_tunnel_update_state(intel_dp->tunnel); + if (ret < 0) { + drm_dbg_kms(&i915->drm, + "[DPTUN %s][ENCODER:%d:%s] State update failed (err %pe)\n", + drm_dp_tunnel_name(intel_dp->tunnel), + encoder->base.base.id, encoder->base.name, + ERR_PTR(ret)); + + return ret; + } + + if (ret == 0 || + !drm_dp_tunnel_bw_alloc_is_enabled(intel_dp->tunnel)) + return 0; + + intel_dp_update_sink_caps(intel_dp); + + new_bw = get_current_link_bw(intel_dp, &new_bw_below_dprx); + + /* Suppress the notification if the mode list can't change due to bw. */ + if (old_bw_below_dprx == new_bw_below_dprx && + !new_bw_below_dprx) + return 0; + + drm_dbg_kms(&i915->drm, + "[DPTUN %s][ENCODER:%d:%s] Notify users about BW change: %d -> %d\n", + drm_dp_tunnel_name(intel_dp->tunnel), + encoder->base.base.id, encoder->base.name, + kbytes_to_mbits(old_bw), kbytes_to_mbits(new_bw)); + + return 1; +} + +/* + * Allocate the BW for a tunnel on a DP connector/port if the connector/port + * was already active when detecting the tunnel. The allocated BW must be + * freed by the next atomic modeset, storing the BW in the + * intel_atomic_state::inherited_dp_tunnels, and calling + * intel_dp_tunnel_atomic_free_bw(). + */ +static int allocate_initial_tunnel_bw_for_pipes(struct intel_dp *intel_dp, u8 pipe_mask) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_crtc *crtc; + int tunnel_bw = 0; + int err; + + for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, pipe_mask) { + const struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); + int stream_bw = intel_dp_config_required_rate(crtc_state); + + tunnel_bw += stream_bw; + + drm_dbg_kms(&i915->drm, + "[DPTUN %s][ENCODER:%d:%s][CRTC:%d:%s] Initial BW for stream %d: %d/%d Mb/s\n", + drm_dp_tunnel_name(intel_dp->tunnel), + encoder->base.base.id, encoder->base.name, + crtc->base.base.id, crtc->base.name, + crtc->pipe, + kbytes_to_mbits(stream_bw), kbytes_to_mbits(tunnel_bw)); + } + + err = drm_dp_tunnel_alloc_bw(intel_dp->tunnel, tunnel_bw); + if (err) { + drm_dbg_kms(&i915->drm, + "[DPTUN %s][ENCODER:%d:%s] Initial BW allocation failed (err %pe)\n", + drm_dp_tunnel_name(intel_dp->tunnel), + encoder->base.base.id, encoder->base.name, + ERR_PTR(err)); + + return err; + } + + return update_tunnel_state(intel_dp); +} + +static int allocate_initial_tunnel_bw(struct intel_dp *intel_dp, + struct drm_modeset_acquire_ctx *ctx) +{ + u8 pipe_mask; + int err; + + err = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); + if (err) + return err; + + return allocate_initial_tunnel_bw_for_pipes(intel_dp, pipe_mask); +} + +static int detect_new_tunnel(struct intel_dp *intel_dp, struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct drm_dp_tunnel *tunnel; + int ret; + + tunnel = drm_dp_tunnel_detect(i915->display.dp_tunnel_mgr, + &intel_dp->aux); + if (IS_ERR(tunnel)) + return PTR_ERR(tunnel); + + intel_dp->tunnel = tunnel; + + ret = drm_dp_tunnel_enable_bw_alloc(intel_dp->tunnel); + if (ret) { + if (ret == -EOPNOTSUPP) + return 0; + + drm_dbg_kms(&i915->drm, + "[DPTUN %s][ENCODER:%d:%s] Failed to enable BW allocation mode (ret %pe)\n", + drm_dp_tunnel_name(intel_dp->tunnel), + encoder->base.base.id, encoder->base.name, + ERR_PTR(ret)); + + /* Keep the tunnel with BWA disabled */ + return 0; + } + + ret = allocate_initial_tunnel_bw(intel_dp, ctx); + if (ret < 0) + intel_dp_tunnel_destroy(intel_dp); + + return ret; +} + +/** + * intel_dp_tunnel_detect - Detect a DP tunnel on a port + * @intel_dp: DP port object + * @ctx: lock context acquired by the connector detection handler + * + * Detect a DP tunnel on the @intel_dp port, enabling the BW allocation mode + * on it if supported and allocating the BW required on an already active port. + * The BW allocated this way must be freed by the next atomic modeset calling + * intel_dp_tunnel_atomic_free_bw(). + * + * If @intel_dp has already a tunnel detected on it, update the tunnel's state + * wrt. its support for BW allocation mode and the available BW via the + * tunnel. If the tunnel's state change requires this - for instance the + * tunnel's group ID has changed - the tunnel will be dropped and recreated. + * + * Return 0 in case of success - after any tunnel detected and added to + * @intel_dp - 1 in case the BW on an already existing tunnel has changed in a + * way that requires notifying user space. + */ +int intel_dp_tunnel_detect(struct intel_dp *intel_dp, struct drm_modeset_acquire_ctx *ctx) +{ + int ret; + + if (intel_dp_is_edp(intel_dp)) + return 0; + + if (intel_dp->tunnel) { + ret = update_tunnel_state(intel_dp); + if (ret >= 0) + return ret; + + /* Try to recreate the tunnel after an update error. */ + intel_dp_tunnel_destroy(intel_dp); + } + + return detect_new_tunnel(intel_dp, ctx); +} + +/** + * intel_dp_tunnel_bw_alloc_is_enabled - Query the BW allocation support on a tunnel + * @intel_dp: DP port object + * + * Query whether a DP tunnel is connected on @intel_dp and the tunnel supports + * the BW allocation mode. + * + * Returns %true if the BW allocation mode is supported on @intel_dp. + */ +bool intel_dp_tunnel_bw_alloc_is_enabled(struct intel_dp *intel_dp) +{ + return drm_dp_tunnel_bw_alloc_is_enabled(intel_dp->tunnel); +} + +/** + * intel_dp_tunnel_suspend - Suspend a DP tunnel connected on a port + * @intel_dp: DP port object + * + * Suspend a DP tunnel on @intel_dp with BW allocation mode enabled on it. + */ +void intel_dp_tunnel_suspend(struct intel_dp *intel_dp) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_connector *connector = intel_dp->attached_connector; + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + + if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + return; + + drm_dbg_kms(&i915->drm, "[DPTUN %s][CONNECTOR:%d:%s][ENCODER:%d:%s] Suspend\n", + drm_dp_tunnel_name(intel_dp->tunnel), + connector->base.base.id, connector->base.name, + encoder->base.base.id, encoder->base.name); + + drm_dp_tunnel_disable_bw_alloc(intel_dp->tunnel); + + intel_dp->tunnel_suspended = true; +} + +/** + * intel_dp_tunnel_resume - Resume a DP tunnel connected on a port + * @intel_dp: DP port object + * @crtc_state: CRTC state + * @dpcd_updated: the DPCD DPRX capabilities got updated during resume + * + * Resume a DP tunnel on @intel_dp with BW allocation mode enabled on it. + */ +void intel_dp_tunnel_resume(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state, + bool dpcd_updated) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_connector *connector = intel_dp->attached_connector; + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; + u8 pipe_mask; + int err = 0; + + if (!intel_dp->tunnel_suspended) + return; + + intel_dp->tunnel_suspended = false; + + drm_dbg_kms(&i915->drm, "[DPTUN %s][CONNECTOR:%d:%s][ENCODER:%d:%s] Resume\n", + drm_dp_tunnel_name(intel_dp->tunnel), + connector->base.base.id, connector->base.name, + encoder->base.base.id, encoder->base.name); + + /* + * The TBT Connection Manager requires the GFX driver to read out + * the sink's DPRX caps to be able to service any BW requests later. + * During resume overriding the caps in @intel_dp cached before + * suspend must be avoided, so do here only a dummy read, unless the + * capabilities were updated already during resume. + */ + if (!dpcd_updated) { + err = intel_dp_read_dprx_caps(intel_dp, dpcd); + + if (err) { + drm_dp_tunnel_set_io_error(intel_dp->tunnel); + goto out_err; + } + } + + err = drm_dp_tunnel_enable_bw_alloc(intel_dp->tunnel); + if (err) + goto out_err; + + pipe_mask = 0; + if (crtc_state) { + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + + /* TODO: Add support for MST */ + pipe_mask |= BIT(crtc->pipe); + } + + err = allocate_initial_tunnel_bw_for_pipes(intel_dp, pipe_mask); + if (err < 0) + goto out_err; + + return; + +out_err: + drm_dbg_kms(&i915->drm, + "[DPTUN %s][CONNECTOR:%d:%s][ENCODER:%d:%s] Tunnel can't be resumed, will drop and redect it (err %pe)\n", + drm_dp_tunnel_name(intel_dp->tunnel), + connector->base.base.id, connector->base.name, + encoder->base.base.id, encoder->base.name, + ERR_PTR(err)); +} + +static struct drm_dp_tunnel * +get_inherited_tunnel(struct intel_atomic_state *state, struct intel_crtc *crtc) +{ + if (!state->inherited_dp_tunnels) + return NULL; + + return state->inherited_dp_tunnels->ref[crtc->pipe].tunnel; +} + +static int +add_inherited_tunnel(struct intel_atomic_state *state, + struct drm_dp_tunnel *tunnel, + struct intel_crtc *crtc) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct drm_dp_tunnel *old_tunnel; + + old_tunnel = get_inherited_tunnel(state, crtc); + if (old_tunnel) { + drm_WARN_ON(&i915->drm, old_tunnel != tunnel); + return 0; + } + + if (!state->inherited_dp_tunnels) { + state->inherited_dp_tunnels = kzalloc(sizeof(*state->inherited_dp_tunnels), + GFP_KERNEL); + if (!state->inherited_dp_tunnels) + return -ENOMEM; + } + + drm_dp_tunnel_ref_get(tunnel, &state->inherited_dp_tunnels->ref[crtc->pipe]); + + return 0; +} + +static int check_inherited_tunnel_state(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + const struct intel_digital_connector_state *old_conn_state) +{ + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_connector *connector = + to_intel_connector(old_conn_state->base.connector); + struct intel_crtc *old_crtc; + const struct intel_crtc_state *old_crtc_state; + + /* + * If a BWA tunnel gets detected only after the corresponding + * connector got enabled already without a BWA tunnel, or a different + * BWA tunnel (which was removed meanwhile) the old CRTC state won't + * contain the state of the current tunnel. This tunnel still has a + * reserved BW, which needs to be released, add the state for such + * inherited tunnels separately only to this atomic state. + */ + if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + return 0; + + if (!old_conn_state->base.crtc) + return 0; + + old_crtc = to_intel_crtc(old_conn_state->base.crtc); + old_crtc_state = intel_atomic_get_old_crtc_state(state, old_crtc); + + if (!old_crtc_state->hw.active || + old_crtc_state->dp_tunnel_ref.tunnel == intel_dp->tunnel) + return 0; + + drm_dbg_kms(&i915->drm, + "[DPTUN %s][CONNECTOR:%d:%s][ENCODER:%d:%s][CRTC:%d:%s] Adding state for inherited tunnel %p\n", + drm_dp_tunnel_name(intel_dp->tunnel), + connector->base.base.id, connector->base.name, + encoder->base.base.id, encoder->base.name, + old_crtc->base.base.id, old_crtc->base.name, + intel_dp->tunnel); + + return add_inherited_tunnel(state, intel_dp->tunnel, old_crtc); +} + +/** + * intel_dp_tunnel_atomic_cleanup_inherited_state - Free any inherited DP tunnel state + * @state: Atomic state + * + * Free the inherited DP tunnel state in @state. + */ +void intel_dp_tunnel_atomic_cleanup_inherited_state(struct intel_atomic_state *state) +{ + enum pipe pipe; + + if (!state->inherited_dp_tunnels) + return; + + for_each_pipe(to_i915(state->base.dev), pipe) + if (state->inherited_dp_tunnels->ref[pipe].tunnel) + drm_dp_tunnel_ref_put(&state->inherited_dp_tunnels->ref[pipe]); + + kfree(state->inherited_dp_tunnels); + state->inherited_dp_tunnels = NULL; +} + +static int intel_dp_tunnel_atomic_add_group_state(struct intel_atomic_state *state, + struct drm_dp_tunnel *tunnel) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + u32 pipe_mask; + int err; + + err = drm_dp_tunnel_atomic_get_group_streams_in_state(&state->base, + tunnel, &pipe_mask); + if (err) + return err; + + drm_WARN_ON(&i915->drm, pipe_mask & ~((1 << I915_MAX_PIPES) - 1)); + + return intel_modeset_pipes_in_mask_early(state, "DPTUN", pipe_mask); +} + +/** + * intel_dp_tunnel_atomic_add_state_for_crtc - Add CRTC specific DP tunnel state + * @state: Atomic state + * @crtc: CRTC to add the tunnel state for + * + * Add the DP tunnel state for @crtc if the CRTC (aka DP tunnel stream) is enabled + * via a DP tunnel. + * + * Return 0 in case of success, a negative error code otherwise. + */ +int intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + const struct intel_crtc_state *new_crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + const struct drm_dp_tunnel_state *tunnel_state; + struct drm_dp_tunnel *tunnel = new_crtc_state->dp_tunnel_ref.tunnel; + + if (!tunnel) + return 0; + + tunnel_state = drm_dp_tunnel_atomic_get_state(&state->base, tunnel); + if (IS_ERR(tunnel_state)) + return PTR_ERR(tunnel_state); + + return 0; +} + +static int check_group_state(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + struct intel_connector *connector, + struct intel_crtc *crtc) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + const struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + + if (!crtc_state->dp_tunnel_ref.tunnel) + return 0; + + drm_dbg_kms(&i915->drm, + "[DPTUN %s][CONNECTOR:%d:%s][ENCODER:%d:%s][CRTC:%d:%s] Adding group state for tunnel %p\n", + drm_dp_tunnel_name(intel_dp->tunnel), + connector->base.base.id, connector->base.name, + encoder->base.base.id, encoder->base.name, + crtc->base.base.id, crtc->base.name, + crtc_state->dp_tunnel_ref.tunnel); + + return intel_dp_tunnel_atomic_add_group_state(state, crtc_state->dp_tunnel_ref.tunnel); +} + +/** + * intel_dp_tunnel_atomic_check_state - Check a connector's DP tunnel specific state + * @state: Atomic state + * @intel_dp: DP port object + * @connector: connector using @intel_dp + * + * Check and add the DP tunnel atomic state for @intel_dp/@connector to + * @state, if there is a DP tunnel detected on @intel_dp with BW allocation + * mode enabled on it, or if @intel_dp/@connector was previously enabled via a + * DP tunnel. + * + * Returns 0 in case of success, or a negative error code otherwise. + */ +int intel_dp_tunnel_atomic_check_state(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + struct intel_connector *connector) +{ + const struct intel_digital_connector_state *old_conn_state = + intel_atomic_get_old_connector_state(state, connector); + const struct intel_digital_connector_state *new_conn_state = + intel_atomic_get_new_connector_state(state, connector); + int err; + + if (old_conn_state->base.crtc) { + err = check_group_state(state, intel_dp, connector, + to_intel_crtc(old_conn_state->base.crtc)); + if (err) + return err; + } + + if (new_conn_state->base.crtc && + new_conn_state->base.crtc != old_conn_state->base.crtc) { + err = check_group_state(state, intel_dp, connector, + to_intel_crtc(new_conn_state->base.crtc)); + if (err) + return err; + } + + return check_inherited_tunnel_state(state, intel_dp, old_conn_state); +} + +/** + * intel_dp_tunnel_atomic_compute_stream_bw - Compute the BW required by a DP tunnel stream + * @state: Atomic state + * @intel_dp: DP object + * @connector: connector using @intel_dp + * @crtc_state: state of CRTC of the given DP tunnel stream + * + * Compute the required BW of CRTC (aka DP tunnel stream), storing this BW to + * the DP tunnel state containing the stream in @state. Before re-calculating a + * BW requirement in the crtc_state state the old BW requirement computed by this + * function must be cleared by calling intel_dp_tunnel_atomic_clear_stream_bw(). + * + * Returns 0 in case of success, a negative error code otherwise. + */ +int intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + const struct intel_connector *connector, + struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + int required_rate = intel_dp_config_required_rate(crtc_state); + int ret; + + if (!intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + return 0; + + drm_dbg_kms(&i915->drm, + "[DPTUN %s][CONNECTOR:%d:%s][ENCODER:%d:%s][CRTC:%d:%s] Stream %d required BW %d Mb/s\n", + drm_dp_tunnel_name(intel_dp->tunnel), + connector->base.base.id, connector->base.name, + encoder->base.base.id, encoder->base.name, + crtc->base.base.id, crtc->base.name, + crtc->pipe, + kbytes_to_mbits(required_rate)); + + ret = drm_dp_tunnel_atomic_set_stream_bw(&state->base, intel_dp->tunnel, + crtc->pipe, required_rate); + if (ret < 0) + return ret; + + drm_dp_tunnel_ref_get(intel_dp->tunnel, + &crtc_state->dp_tunnel_ref); + + return 0; +} + +/** + * intel_dp_tunnel_atomic_clear_stream_bw - Clear any DP tunnel stream BW requirement + * @state: Atomic state + * @crtc_state: state of CRTC of the given DP tunnel stream + * + * Clear any DP tunnel stream BW requirement set by + * intel_dp_tunnel_atomic_compute_stream_bw(). + */ +void intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state) +{ + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + + if (!crtc_state->dp_tunnel_ref.tunnel) + return; + + drm_dp_tunnel_atomic_set_stream_bw(&state->base, + crtc_state->dp_tunnel_ref.tunnel, + crtc->pipe, 0); + drm_dp_tunnel_ref_put(&crtc_state->dp_tunnel_ref); +} + +/** + * intel_dp_tunnel_atomic_check_link - Check the DP tunnel atomic state + * @state: intel atomic state + * @limits: link BW limits + * + * Check the link configuration for all DP tunnels in @state. If the + * configuration is invalid @limits will be updated if possible to + * reduce the total BW, after which the configuration for all CRTCs in + * @state must be recomputed with the updated @limits. + * + * Returns: + * - 0 if the confugration is valid + * - %-EAGAIN, if the configuration is invalid and @limits got updated + * with fallback values with which the configuration of all CRTCs in + * @state must be recomputed + * - Other negative error, if the configuration is invalid without a + * fallback possibility, or the check failed for another reason + */ +int intel_dp_tunnel_atomic_check_link(struct intel_atomic_state *state, + struct intel_link_bw_limits *limits) +{ + u32 failed_stream_mask; + int err; + + err = drm_dp_tunnel_atomic_check_stream_bws(&state->base, + &failed_stream_mask); + if (err != -ENOSPC) + return err; + + err = intel_link_bw_reduce_bpp(state, limits, + failed_stream_mask, "DP tunnel link BW"); + + return err ? : -EAGAIN; +} + +static void atomic_decrease_bw(struct intel_atomic_state *state) +{ + struct intel_crtc *crtc; + const struct intel_crtc_state *old_crtc_state; + const struct intel_crtc_state *new_crtc_state; + int i; + + for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + const struct drm_dp_tunnel_state *new_tunnel_state; + struct drm_dp_tunnel *tunnel; + int old_bw; + int new_bw; + + if (!intel_crtc_needs_modeset(new_crtc_state)) + continue; + + tunnel = get_inherited_tunnel(state, crtc); + if (!tunnel) + tunnel = old_crtc_state->dp_tunnel_ref.tunnel; + + if (!tunnel) + continue; + + old_bw = drm_dp_tunnel_get_allocated_bw(tunnel); + + new_tunnel_state = drm_dp_tunnel_atomic_get_new_state(&state->base, tunnel); + new_bw = drm_dp_tunnel_atomic_get_required_bw(new_tunnel_state); + + if (new_bw >= old_bw) + continue; + + drm_dp_tunnel_alloc_bw(tunnel, new_bw); + } +} + +static void queue_retry_work(struct intel_atomic_state *state, + struct drm_dp_tunnel *tunnel, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_encoder *encoder; + + encoder = intel_get_crtc_new_encoder(state, crtc_state); + + if (!intel_digital_port_connected(encoder)) + return; + + drm_dbg_kms(&i915->drm, + "[DPTUN %s][ENCODER:%d:%s] BW allocation failed on a connected sink\n", + drm_dp_tunnel_name(tunnel), + encoder->base.base.id, + encoder->base.name); + + intel_dp_queue_modeset_retry_for_link(state, encoder, crtc_state); +} + +static void atomic_increase_bw(struct intel_atomic_state *state) +{ + struct intel_crtc *crtc; + const struct intel_crtc_state *crtc_state; + int i; + + for_each_new_intel_crtc_in_state(state, crtc, crtc_state, i) { + struct drm_dp_tunnel_state *tunnel_state; + struct drm_dp_tunnel *tunnel = crtc_state->dp_tunnel_ref.tunnel; + int bw; + + if (!intel_crtc_needs_modeset(crtc_state)) + continue; + + if (!tunnel) + continue; + + tunnel_state = drm_dp_tunnel_atomic_get_new_state(&state->base, tunnel); + + bw = drm_dp_tunnel_atomic_get_required_bw(tunnel_state); + + if (drm_dp_tunnel_alloc_bw(tunnel, bw) != 0) + queue_retry_work(state, tunnel, crtc_state); + } +} + +/** + * intel_dp_tunnel_atomic_alloc_bw - Allocate the BW for all modeset tunnels + * @state: Atomic state + * + * Allocate the required BW for all tunnels in @state. + */ +void intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state) +{ + atomic_decrease_bw(state); + atomic_increase_bw(state); +} + +/** + * intel_dp_tunnel_mgr_init - Initialize the DP tunnel manager + * @i915: i915 device object + * + * Initialize the DP tunnel manager. The tunnel manager will support the + * detection/management of DP tunnels on all DP connectors, so the function + * must be called after all these connectors have been registered already. + * + * Return 0 in case of success, a negative error code otherwise. + */ +int intel_dp_tunnel_mgr_init(struct drm_i915_private *i915) +{ + struct drm_dp_tunnel_mgr *tunnel_mgr; + struct drm_connector_list_iter connector_list_iter; + struct intel_connector *connector; + int dp_connectors = 0; + + drm_connector_list_iter_begin(&i915->drm, &connector_list_iter); + for_each_intel_connector_iter(connector, &connector_list_iter) { + if (connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort) + continue; + + dp_connectors++; + } + drm_connector_list_iter_end(&connector_list_iter); + + tunnel_mgr = drm_dp_tunnel_mgr_create(&i915->drm, dp_connectors); + if (IS_ERR(tunnel_mgr)) + return PTR_ERR(tunnel_mgr); + + i915->display.dp_tunnel_mgr = tunnel_mgr; + + return 0; +} + +/** + * intel_dp_tunnel_mgr_cleanup - Clean up the DP tunnel manager state + * @i915: i915 device object + * + * Clean up the DP tunnel manager state. + */ +void intel_dp_tunnel_mgr_cleanup(struct drm_i915_private *i915) +{ + drm_dp_tunnel_mgr_destroy(i915->display.dp_tunnel_mgr); + i915->display.dp_tunnel_mgr = NULL; +} diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.h b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h new file mode 100644 index 000000000000..08b2cba84af2 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.h @@ -0,0 +1,133 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __INTEL_DP_TUNNEL_H__ +#define __INTEL_DP_TUNNEL_H__ + +#include +#include + +struct drm_i915_private; +struct drm_connector_state; +struct drm_modeset_acquire_ctx; + +struct intel_atomic_state; +struct intel_connector; +struct intel_crtc; +struct intel_crtc_state; +struct intel_dp; +struct intel_encoder; +struct intel_link_bw_limits; + +#if defined(CONFIG_DRM_I915_DP_TUNNEL) && defined(I915) + +int intel_dp_tunnel_detect(struct intel_dp *intel_dp, struct drm_modeset_acquire_ctx *ctx); +void intel_dp_tunnel_disconnect(struct intel_dp *intel_dp); +void intel_dp_tunnel_destroy(struct intel_dp *intel_dp); +void intel_dp_tunnel_resume(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state, + bool dpcd_updated); +void intel_dp_tunnel_suspend(struct intel_dp *intel_dp); + +bool intel_dp_tunnel_bw_alloc_is_enabled(struct intel_dp *intel_dp); + +void +intel_dp_tunnel_atomic_cleanup_inherited_state(struct intel_atomic_state *state); + +int intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + const struct intel_connector *connector, + struct intel_crtc_state *crtc_state); +void intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state); + +int intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state, + struct intel_crtc *crtc); +int intel_dp_tunnel_atomic_check_link(struct intel_atomic_state *state, + struct intel_link_bw_limits *limits); +int intel_dp_tunnel_atomic_check_state(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + struct intel_connector *connector); + +void intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state); + +int intel_dp_tunnel_mgr_init(struct drm_i915_private *i915); +void intel_dp_tunnel_mgr_cleanup(struct drm_i915_private *i915); + +#else + +static inline int +intel_dp_tunnel_detect(struct intel_dp *intel_dp, struct drm_modeset_acquire_ctx *ctx) +{ + return -EOPNOTSUPP; +} + +static inline void intel_dp_tunnel_disconnect(struct intel_dp *intel_dp) {} +static inline void intel_dp_tunnel_destroy(struct intel_dp *intel_dp) {} +static inline void intel_dp_tunnel_resume(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state, + bool dpcd_updated) {} +static inline void intel_dp_tunnel_suspend(struct intel_dp *intel_dp) {} + +static inline bool intel_dp_tunnel_bw_alloc_is_enabled(struct intel_dp *intel_dp) +{ + return false; +} + +static inline void +intel_dp_tunnel_atomic_cleanup_inherited_state(struct intel_atomic_state *state) {} + +static inline int +intel_dp_tunnel_atomic_compute_stream_bw(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + const struct intel_connector *connector, + struct intel_crtc_state *crtc_state) +{ + return 0; +} + +static inline void +intel_dp_tunnel_atomic_clear_stream_bw(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state) {} + +static inline int +intel_dp_tunnel_atomic_add_state_for_crtc(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + return 0; +} + +static inline int +intel_dp_tunnel_atomic_check_link(struct intel_atomic_state *state, + struct intel_link_bw_limits *limits) +{ + return 0; +} + +static inline int +intel_dp_tunnel_atomic_check_state(struct intel_atomic_state *state, + struct intel_dp *intel_dp, + struct intel_connector *connector) +{ + return 0; +} + +static inline int +intel_dp_tunnel_atomic_alloc_bw(struct intel_atomic_state *state) +{ + return 0; +} + +static inline int +intel_dp_tunnel_mgr_init(struct drm_i915_private *i915) +{ + return 0; +} + +static inline void intel_dp_tunnel_mgr_cleanup(struct drm_i915_private *i915) {} + +#endif /* CONFIG_DRM_I915_DP_TUNNEL */ + +#endif /* __INTEL_DP_TUNNEL_H__ */ From 39818c06c829c7f8bcf21993d2e085c156c2f4f8 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:33 +0200 Subject: [PATCH 82/90] drm/i915/dp: Add DP tunnel atomic state and check BW limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the atomic state during a modeset required to enable the DP tunnel BW allocation mode on links where such a tunnel was detected. This state applies to an already enabled output, the state added for a newly enabled output will be computed and added/cleared to/from the atomic state in a follow-up patch. v2: - s/old_crtc_state/crtc_state in intel_crtc_duplicate_state(). - Move intel_dp_tunnel_atomic_cleanup_inherited_state() to a follow-up patch adding the corresponding state. (Ville) - Move intel_dp_tunnel_atomic_clear_stream_bw() to a follow-up patch adding the corresponding state. Cc: Ville Syrjälä Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-14-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_atomic.c | 6 ++++++ drivers/gpu/drm/i915/display/intel_display.c | 12 ++++++++++++ drivers/gpu/drm/i915/display/intel_link_bw.c | 5 +++++ 3 files changed, 23 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 96ab37e15899..798cb90361a8 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -260,6 +260,10 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc) if (crtc_state->post_csc_lut) drm_property_blob_get(crtc_state->post_csc_lut); + if (crtc_state->dp_tunnel_ref.tunnel) + drm_dp_tunnel_ref_get(crtc_state->dp_tunnel_ref.tunnel, + &crtc_state->dp_tunnel_ref); + crtc_state->update_pipe = false; crtc_state->update_m_n = false; crtc_state->update_lrr = false; @@ -311,6 +315,8 @@ intel_crtc_destroy_state(struct drm_crtc *crtc, __drm_atomic_helper_crtc_destroy_state(&crtc_state->uapi); intel_crtc_free_hw_state(crtc_state); + if (crtc_state->dp_tunnel_ref.tunnel) + drm_dp_tunnel_ref_put(&crtc_state->dp_tunnel_ref); kfree(crtc_state); } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 444d09e6a30d..1fc403661674 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -73,6 +74,7 @@ #include "intel_dp.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" +#include "intel_dp_tunnel.h" #include "intel_dpll.h" #include "intel_dpll_mgr.h" #include "intel_dpt.h" @@ -4490,6 +4492,8 @@ copy_bigjoiner_crtc_state_modeset(struct intel_atomic_state *state, saved_state->crc_enabled = slave_crtc_state->crc_enabled; intel_crtc_free_hw_state(slave_crtc_state); + if (slave_crtc_state->dp_tunnel_ref.tunnel) + drm_dp_tunnel_ref_put(&slave_crtc_state->dp_tunnel_ref); memcpy(slave_crtc_state, saved_state, sizeof(*slave_crtc_state)); kfree(saved_state); @@ -4505,6 +4509,10 @@ copy_bigjoiner_crtc_state_modeset(struct intel_atomic_state *state, &master_crtc_state->hw.adjusted_mode); slave_crtc_state->hw.scaling_filter = master_crtc_state->hw.scaling_filter; + if (master_crtc_state->dp_tunnel_ref.tunnel) + drm_dp_tunnel_ref_get(master_crtc_state->dp_tunnel_ref.tunnel, + &slave_crtc_state->dp_tunnel_ref); + copy_bigjoiner_crtc_state_nomodeset(state, slave_crtc); slave_crtc_state->uapi.mode_changed = master_crtc_state->uapi.mode_changed; @@ -5366,6 +5374,10 @@ static int intel_modeset_pipe(struct intel_atomic_state *state, if (ret) return ret; + ret = intel_dp_tunnel_atomic_add_state_for_crtc(state, crtc); + if (ret) + return ret; + ret = intel_dp_mst_add_topology_state_for_crtc(state, crtc); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_link_bw.c b/drivers/gpu/drm/i915/display/intel_link_bw.c index 27ea858897c9..dfd7d5e23f3f 100644 --- a/drivers/gpu/drm/i915/display/intel_link_bw.c +++ b/drivers/gpu/drm/i915/display/intel_link_bw.c @@ -9,6 +9,7 @@ #include "intel_crtc.h" #include "intel_display_types.h" #include "intel_dp_mst.h" +#include "intel_dp_tunnel.h" #include "intel_fdi.h" #include "intel_link_bw.h" @@ -163,6 +164,10 @@ static int check_all_link_config(struct intel_atomic_state *state, if (ret) return ret; + ret = intel_dp_tunnel_atomic_check_link(state, limits); + if (ret) + return ret; + ret = intel_fdi_atomic_check_link(state, limits); if (ret) return ret; From 259e2e0a043238111b85f4d45239c3538883e185 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:34 +0200 Subject: [PATCH 83/90] drm/i915/dp: Account for tunnel BW limit in intel_dp_max_link_data_rate() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Take any link BW limitation into account in intel_dp_max_link_data_rate(). Such a limitation can be due to multiple displays on (Thunderbolt) links with DP tunnels sharing the link BW. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-15-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 32 +++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 8ae06aa42ddc..32e7fdb2ab93 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -63,6 +63,7 @@ #include "intel_dp_hdcp.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" +#include "intel_dp_tunnel.h" #include "intel_dpio_phy.h" #include "intel_dpll.h" #include "intel_fifo_underrun.h" @@ -152,6 +153,22 @@ int intel_dp_link_symbol_clock(int rate) return DIV_ROUND_CLOSEST(rate * 10, intel_dp_link_symbol_size(rate)); } +static int max_dprx_rate(struct intel_dp *intel_dp) +{ + if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + return drm_dp_tunnel_max_dprx_rate(intel_dp->tunnel); + + return drm_dp_bw_code_to_link_rate(intel_dp->dpcd[DP_MAX_LINK_RATE]); +} + +static int max_dprx_lane_count(struct intel_dp *intel_dp) +{ + if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + return drm_dp_tunnel_max_dprx_lane_count(intel_dp->tunnel); + + return drm_dp_max_lane_count(intel_dp->dpcd); +} + static void intel_dp_set_default_sink_rates(struct intel_dp *intel_dp) { intel_dp->sink_rates[0] = 162000; @@ -180,7 +197,7 @@ static void intel_dp_set_dpcd_sink_rates(struct intel_dp *intel_dp) /* * Sink rates for 8b/10b. */ - max_rate = drm_dp_bw_code_to_link_rate(intel_dp->dpcd[DP_MAX_LINK_RATE]); + max_rate = max_dprx_rate(intel_dp); max_lttpr_rate = drm_dp_lttpr_max_link_rate(intel_dp->lttpr_common_caps); if (max_lttpr_rate) max_rate = min(max_rate, max_lttpr_rate); @@ -259,7 +276,7 @@ static void intel_dp_set_max_sink_lane_count(struct intel_dp *intel_dp) struct intel_digital_port *intel_dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *encoder = &intel_dig_port->base; - intel_dp->max_sink_lane_count = drm_dp_max_lane_count(intel_dp->dpcd); + intel_dp->max_sink_lane_count = max_dprx_lane_count(intel_dp); switch (intel_dp->max_sink_lane_count) { case 1: @@ -389,14 +406,21 @@ int intel_dp_effective_data_rate(int pixel_clock, int bpp_x16, * @max_dprx_rate: Maximum data rate of the DPRX * @max_dprx_lanes: Maximum lane count of the DPRX * - * Calculate the maximum data rate for the provided link parameters. + * Calculate the maximum data rate for the provided link parameters taking into + * account any BW limitations by a DP tunnel attached to @intel_dp. * * Returns the maximum data rate in kBps units. */ int intel_dp_max_link_data_rate(struct intel_dp *intel_dp, int max_dprx_rate, int max_dprx_lanes) { - return drm_dp_max_dprx_data_rate(max_dprx_rate, max_dprx_lanes); + int max_rate = drm_dp_max_dprx_data_rate(max_dprx_rate, max_dprx_lanes); + + if (intel_dp_tunnel_bw_alloc_is_enabled(intel_dp)) + max_rate = min(max_rate, + drm_dp_tunnel_available_bw(intel_dp->tunnel)); + + return max_rate; } bool intel_dp_can_bigjoiner(struct intel_dp *intel_dp) From a4efae87ecb21bfb7da96f15ee23815da802024a Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:35 +0200 Subject: [PATCH 84/90] drm/i915/dp: Compute DP tunnel BW during encoder state computation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compute the BW required through a DP tunnel on links with such tunnels detected and add the corresponding atomic state during a modeset. v2: - Fix error check of intel_dp_tunnel_compute_stream_bw(). (Ville) - Move intel_dp_tunnel_atomic_cleanup_inherited_state() to this patch. (Ville) - Move intel_dp_tunnel_atomic_clear_stream_bw() to this patch. Reviewed-by: Uma Shankar (v1) Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-16-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_atomic.c | 2 ++ drivers/gpu/drm/i915/display/intel_display.c | 2 ++ drivers/gpu/drm/i915/display/intel_dp.c | 16 ++++++++++++---- drivers/gpu/drm/i915/display/intel_dp_mst.c | 13 ++++++++++++- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index 798cb90361a8..2bb270f82932 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -352,6 +352,8 @@ void intel_atomic_state_clear(struct drm_atomic_state *s) /* state->internal not reset on purpose */ state->dpll_set = state->modeset = false; + + intel_dp_tunnel_atomic_cleanup_inherited_state(state); } struct intel_crtc_state * diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 1fc403661674..6a2dec97f79b 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4541,6 +4541,8 @@ intel_crtc_prepare_cleared_state(struct intel_atomic_state *state, /* free the old crtc_state->hw members */ intel_crtc_free_hw_state(crtc_state); + intel_dp_tunnel_atomic_clear_stream_bw(state, crtc_state); + /* FIXME: before the switch to atomic started, a new pipe_config was * kzalloc'd. Code that depends on any field being zero should be * fixed, so that the crtc_state can be safely duplicated. For now, diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 32e7fdb2ab93..3b7adaaa7c96 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -2875,6 +2875,7 @@ intel_dp_compute_config(struct intel_encoder *encoder, struct drm_connector_state *conn_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct intel_dp *intel_dp = enc_to_intel_dp(encoder); const struct drm_display_mode *fixed_mode; @@ -2975,7 +2976,8 @@ intel_dp_compute_config(struct intel_encoder *encoder, intel_dp_compute_vsc_sdp(intel_dp, pipe_config, conn_state); intel_dp_compute_hdr_metadata_infoframe_sdp(intel_dp, pipe_config, conn_state); - return 0; + return intel_dp_tunnel_atomic_compute_stream_bw(state, intel_dp, connector, + pipe_config); } void intel_dp_set_link_params(struct intel_dp *intel_dp, @@ -6066,6 +6068,15 @@ static int intel_dp_connector_atomic_check(struct drm_connector *conn, return ret; } + if (!intel_connector_needs_modeset(state, conn)) + return 0; + + ret = intel_dp_tunnel_atomic_check_state(state, + intel_dp, + intel_conn); + if (ret) + return ret; + /* * We don't enable port sync on BDW due to missing w/as and * due to not having adjusted the modeset sequence appropriately. @@ -6073,9 +6084,6 @@ static int intel_dp_connector_atomic_check(struct drm_connector *conn, if (DISPLAY_VER(dev_priv) < 9) return 0; - if (!intel_connector_needs_modeset(state, conn)) - return 0; - if (conn->has_tile) { ret = intel_modeset_tile_group(state, conn->tile_group->id); if (ret) diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 4b6c8ff97429..db1254b036f1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -42,6 +42,7 @@ #include "intel_dp.h" #include "intel_dp_hdcp.h" #include "intel_dp_mst.h" +#include "intel_dp_tunnel.h" #include "intel_dpio_phy.h" #include "intel_hdcp.h" #include "intel_hotplug.h" @@ -523,6 +524,7 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, struct drm_connector_state *conn_state) { 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_dp_mst_encoder *intel_mst = enc_to_mst(encoder); struct intel_dp *intel_dp = &intel_mst->primary->dp; const struct intel_connector *connector = @@ -619,7 +621,8 @@ static int intel_dp_mst_compute_config(struct intel_encoder *encoder, intel_psr_compute_config(intel_dp, pipe_config, conn_state); - return 0; + return intel_dp_tunnel_atomic_compute_stream_bw(state, intel_dp, connector, + pipe_config); } /* @@ -876,6 +879,14 @@ intel_dp_mst_atomic_check(struct drm_connector *connector, if (ret) return ret; + if (intel_connector_needs_modeset(state, connector)) { + ret = intel_dp_tunnel_atomic_check_state(state, + intel_connector->mst_port, + intel_connector); + if (ret) + return ret; + } + return drm_dp_atomic_release_time_slots(&state->base, &intel_connector->mst_port->mst_mgr, intel_connector->port); From e7e5048f4ad5e7a81f7b788b2e73c76cebd5b55b Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:36 +0200 Subject: [PATCH 85/90] drm/i915/dp: Allocate/free DP tunnel BW during modeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allocate and free the DP tunnel BW required by a stream while enabling/disabling the stream during a modeset. v2: - Move the allocation up from encoder hooks to intel_atomic_commit_tail(). v3: - Update the commit subject. (Ville) Reviewed-by: Uma Shankar (v1) Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-17-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 1 + drivers/gpu/drm/i915/display/intel_display.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index bea441590204..ed7301808604 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -54,6 +54,7 @@ #include "intel_dp_aux.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" +#include "intel_dp_tunnel.h" #include "intel_dpio_phy.h" #include "intel_dsi.h" #include "intel_fdi.h" diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 6a2dec97f79b..97655a3b6d68 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -7124,6 +7124,8 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) intel_commit_modeset_disables(state); + intel_dp_tunnel_atomic_alloc_bw(state); + /* FIXME: Eventually get rid of our crtc->config pointer */ for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state, i) crtc->config = new_crtc_state; From 6496dbecb9c242cb87c237dbf1a51a89588b20f7 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:37 +0200 Subject: [PATCH 86/90] drm/i915/dp: Handle DP tunnel IRQs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle DP tunnel IRQs a sink (or rather a BW management component like the Thunderbolt Connection Manager) raises to signal the completion of a BW request by the driver, or to signal any state change related to the link BW. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-18-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 37 +++++++++++++++++++------ include/drm/display/drm_dp.h | 1 + 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 3b7adaaa7c96..51b24cab51ff 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4904,13 +4904,15 @@ static bool intel_dp_mst_link_status(struct intel_dp *intel_dp) * - %true if pending interrupts were serviced (or no interrupts were * pending) w/o detecting an error condition. * - %false if an error condition - like AUX failure or a loss of link - is - * detected, which needs servicing from the hotplug work. + * detected, or another condition - like a DP tunnel BW state change - needs + * servicing from the hotplug work. */ static bool intel_dp_check_mst_status(struct intel_dp *intel_dp) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); bool link_ok = true; + bool reprobe_needed = false; drm_WARN_ON_ONCE(&i915->drm, intel_dp->active_mst_links < 0); @@ -4937,6 +4939,13 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) intel_dp_mst_hpd_irq(intel_dp, esi, ack); + if (esi[3] & DP_TUNNELING_IRQ) { + if (drm_dp_tunnel_handle_irq(i915->display.dp_tunnel_mgr, + &intel_dp->aux)) + reprobe_needed = true; + ack[3] |= DP_TUNNELING_IRQ; + } + if (!memchr_inv(ack, 0, sizeof(ack))) break; @@ -4947,7 +4956,7 @@ intel_dp_check_mst_status(struct intel_dp *intel_dp) drm_dp_mst_hpd_irq_send_new_request(&intel_dp->mst_mgr); } - return link_ok; + return link_ok && !reprobe_needed; } static void @@ -5307,23 +5316,32 @@ static void intel_dp_check_device_service_irq(struct intel_dp *intel_dp) drm_dbg_kms(&i915->drm, "Sink specific irq unhandled\n"); } -static void intel_dp_check_link_service_irq(struct intel_dp *intel_dp) +static bool intel_dp_check_link_service_irq(struct intel_dp *intel_dp) { + struct drm_i915_private *i915 = dp_to_i915(intel_dp); + bool reprobe_needed = false; u8 val; if (intel_dp->dpcd[DP_DPCD_REV] < 0x11) - return; + return false; if (drm_dp_dpcd_readb(&intel_dp->aux, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, &val) != 1 || !val) - return; + return false; + + if ((val & DP_TUNNELING_IRQ) && + drm_dp_tunnel_handle_irq(i915->display.dp_tunnel_mgr, + &intel_dp->aux)) + reprobe_needed = true; if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_LINK_SERVICE_IRQ_VECTOR_ESI0, val) != 1) - return; + return reprobe_needed; if (val & HDMI_LINK_STATUS_CHANGED) intel_dp_handle_hdmi_link_status_change(intel_dp); + + return reprobe_needed; } /* @@ -5344,6 +5362,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) { struct drm_i915_private *dev_priv = dp_to_i915(intel_dp); u8 old_sink_count = intel_dp->sink_count; + bool reprobe_needed = false; bool ret; /* @@ -5366,7 +5385,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) } intel_dp_check_device_service_irq(intel_dp); - intel_dp_check_link_service_irq(intel_dp); + reprobe_needed = intel_dp_check_link_service_irq(intel_dp); /* Handle CEC interrupts, if any */ drm_dp_cec_irq(&intel_dp->aux); @@ -5393,10 +5412,10 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) * FIXME get rid of the ad-hoc phy test modeset code * and properly incorporate it into the normal modeset. */ - return false; + reprobe_needed = true; } - return true; + return !reprobe_needed; } /* XXX this is probably wrong for multiple downstream ports */ diff --git a/include/drm/display/drm_dp.h b/include/drm/display/drm_dp.h index 8bfd5d007be8..4891bd916d26 100644 --- a/include/drm/display/drm_dp.h +++ b/include/drm/display/drm_dp.h @@ -1081,6 +1081,7 @@ # define STREAM_STATUS_CHANGED (1 << 2) # define HDMI_LINK_STATUS_CHANGED (1 << 3) # define CONNECTED_OFF_ENTRY_REQUESTED (1 << 4) +# define DP_TUNNELING_IRQ (1 << 5) #define DP_PSR_ERROR_STATUS 0x2006 /* XXX 1.2? */ # define DP_PSR_LINK_CRC_ERROR (1 << 0) From 7605d0e8c178b2408a9191df8f95b52df08cb63a Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:38 +0200 Subject: [PATCH 87/90] drm/i915/dp: Call intel_dp_sync_state() always for DDI DP encoders MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A follow-up change will need to resume DP tunnels during system resume, so call intel_dp_sync_state() always for DDI encoders, so this function can resume the tunnels for all DP connectors. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-19-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index ed7301808604..c587a8efeafc 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4151,7 +4151,7 @@ static void intel_ddi_sync_state(struct intel_encoder *encoder, intel_tc_port_sanitize_mode(enc_to_dig_port(encoder), crtc_state); - if (crtc_state && intel_crtc_has_dp_encoder(crtc_state)) + if (intel_encoder_is_dp(encoder)) intel_dp_sync_state(encoder, crtc_state); } From fb687904aae27c8bf5b5f653c0bf72171bd43b41 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:39 +0200 Subject: [PATCH 88/90] drm/i915/dp: Suspend/resume DP tunnels MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suspend and resume DP tunnels during system suspend/resume, disabling the BW allocation mode during suspend, re-enabling it after resume. This reflects the link's BW management component (Thunderbolt CM) disabling BWA during suspend. Before any BW requests the driver must read the sink's DPRX capabilities (since the BW manager requires this information, so snoops for it on AUX), so ensure this read takes place. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-20-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 51b24cab51ff..0b9c33a000f9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -3313,18 +3314,21 @@ void intel_dp_sync_state(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - - if (!crtc_state) - return; + bool dpcd_updated = false; /* * Don't clobber DPCD if it's been already read out during output * setup (eDP) or detect. */ - if (intel_dp->dpcd[DP_DPCD_REV] == 0) + if (crtc_state && intel_dp->dpcd[DP_DPCD_REV] == 0) { intel_dp_get_dpcd(intel_dp); + dpcd_updated = true; + } - intel_dp_reset_max_link_params(intel_dp); + intel_dp_tunnel_resume(intel_dp, crtc_state, dpcd_updated); + + if (crtc_state) + intel_dp_reset_max_link_params(intel_dp); } bool intel_dp_initial_fastset_check(struct intel_encoder *encoder, @@ -5950,6 +5954,8 @@ void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder) struct intel_dp *intel_dp = enc_to_intel_dp(intel_encoder); intel_pps_vdd_off_sync(intel_dp); + + intel_dp_tunnel_suspend(intel_dp); } void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder) From a32f3a95413881b69c8dd319334927eb9519013c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:40 +0200 Subject: [PATCH 89/90] drm/i915/dp: Read DPRX for all long HPD pulses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TBT DP tunnel BW request logic in the Thunderbolt Connection Manager depends on the GFX driver reading out the sink's DPRX capabilities in response to a long HPD pulse. Since in i915 this read-out can be blocked by another connector's/encoder's hotplug event handling (which is serialized by drm_mode_config::connection_mutex), do a dummy DPRX read-out in the encoder's HPD pulse handler (which is not blocked by other encoders). Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-21-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_dp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 0b9c33a000f9..a0e826a740b1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -6165,6 +6165,7 @@ intel_dp_hpd_pulse(struct intel_digital_port *dig_port, bool long_hpd) { struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_dp *intel_dp = &dig_port->dp; + u8 dpcd[DP_RECEIVER_CAP_SIZE]; if (dig_port->base.type == INTEL_OUTPUT_EDP && (long_hpd || !intel_pps_have_panel_power_or_vdd(intel_dp))) { @@ -6187,6 +6188,17 @@ intel_dp_hpd_pulse(struct intel_digital_port *dig_port, bool long_hpd) dig_port->base.base.name, long_hpd ? "long" : "short"); + /* + * TBT DP tunnels require the GFX driver to read out the DPRX caps in + * response to long HPD pulses. The DP hotplug handler does that, + * however the hotplug handler may be blocked by another + * connector's/encoder's hotplug handler. Since the TBT CM may not + * complete the DP tunnel BW request for the latter connector/encoder + * waiting for this encoder's DPRX read, perform a dummy read here. + */ + if (long_hpd) + intel_dp_read_dprx_caps(intel_dp, dpcd); + if (long_hpd) { intel_dp->reset_link_params = true; return IRQ_NONE; From e60cff453b82789a652239c6200bd90d5178d2a0 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Tue, 20 Feb 2024 23:18:41 +0200 Subject: [PATCH 90/90] drm/i915/dp: Enable DP tunnel BW allocation mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detect DP tunnels and enable the BW allocation mode on them. Send a hotplug notification to userspace in response to a BW change. Reviewed-by: Uma Shankar Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20240220211841.448846-22-imre.deak@intel.com --- .../drm/i915/display/intel_display_driver.c | 20 +++++++++++++++---- drivers/gpu/drm/i915/display/intel_dp.c | 14 +++++++++++-- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index 14a9a62cab8a..2391e33c8f87 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -35,6 +35,7 @@ #include "intel_dkl_phy.h" #include "intel_dmc.h" #include "intel_dp.h" +#include "intel_dp_tunnel.h" #include "intel_dpll.h" #include "intel_dpll_mgr.h" #include "intel_fb.h" @@ -434,10 +435,8 @@ int intel_display_driver_probe_nogem(struct drm_i915_private *i915) for_each_pipe(i915, pipe) { ret = intel_crtc_init(i915, pipe); - if (ret) { - intel_mode_config_cleanup(i915); - return ret; - } + if (ret) + goto err_mode_config; } intel_plane_possible_crtcs_init(i915); @@ -457,6 +456,10 @@ int intel_display_driver_probe_nogem(struct drm_i915_private *i915) intel_vga_disable(i915); intel_setup_outputs(i915); + ret = intel_dp_tunnel_mgr_init(i915); + if (ret) + goto err_hdcp; + intel_display_driver_disable_user_access(i915); drm_modeset_lock_all(dev); @@ -475,6 +478,13 @@ int intel_display_driver_probe_nogem(struct drm_i915_private *i915) ilk_wm_sanitize(i915); return 0; + +err_hdcp: + intel_hdcp_component_fini(i915); +err_mode_config: + intel_mode_config_cleanup(i915); + + return ret; } /* part #3: call after gem init */ @@ -598,6 +608,8 @@ void intel_display_driver_remove_noirq(struct drm_i915_private *i915) intel_mode_config_cleanup(i915); + intel_dp_tunnel_mgr_cleanup(i915); + intel_overlay_cleanup(i915); intel_gmbus_teardown(i915); diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index a0e826a740b1..523a6d68a52c 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5729,6 +5729,7 @@ intel_dp_detect(struct drm_connector *connector, struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *encoder = &dig_port->base; enum drm_connector_status status; + int ret; drm_dbg_kms(&dev_priv->drm, "[CONNECTOR:%d:%s]\n", connector->base.id, connector->name); @@ -5764,9 +5765,18 @@ intel_dp_detect(struct drm_connector *connector, intel_dp->is_mst); } + intel_dp_tunnel_disconnect(intel_dp); + goto out; } + ret = intel_dp_tunnel_detect(intel_dp, ctx); + if (ret == -EDEADLK) + return ret; + + if (ret == 1) + intel_connector->base.epoch_counter++; + intel_dp_detect_dsc_caps(intel_dp, intel_connector); intel_dp_configure_mst(intel_dp); @@ -5797,8 +5807,6 @@ intel_dp_detect(struct drm_connector *connector, * with an IRQ_HPD, so force a link status check. */ if (!intel_dp_is_edp(intel_dp)) { - int ret; - ret = intel_dp_retrain_link(encoder, ctx); if (ret) return ret; @@ -5938,6 +5946,8 @@ void intel_dp_encoder_flush_work(struct drm_encoder *encoder) intel_dp_mst_encoder_cleanup(dig_port); + intel_dp_tunnel_destroy(intel_dp); + intel_pps_vdd_off_sync(intel_dp); /*