diff options
| author | Mark Brown <broonie@kernel.org> | 2026-07-03 15:44:52 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-07-03 15:44:52 +0100 |
| commit | 10182b0e6181d81cbba867e2a098c42088285224 (patch) | |
| tree | 9c674f76798bbd5484d6d7fcfd239e9bc33a2817 | |
| parent | 4dac529c9a2c0ed1286801ec62d23538035a3a1d (diff) | |
| parent | 8e27f752037e72ccee9c4a7c4a6202ecf3daf603 (diff) | |
| download | linux-next-10182b0e6181d81cbba867e2a098c42088285224.tar.gz linux-next-10182b0e6181d81cbba867e2a098c42088285224.zip | |
Merge branch 'for-linux-next' of https://gitlab.freedesktop.org/drm/i915/kernel.git
91 files changed, 3663 insertions, 1962 deletions
diff --git a/Documentation/gpu/intel-display/dp-link-training.rst b/Documentation/gpu/intel-display/dp-link-training.rst new file mode 100644 index 000000000000..d0bde965021d --- /dev/null +++ b/Documentation/gpu/intel-display/dp-link-training.rst @@ -0,0 +1,8 @@ +.. SPDX-License-Identifier: MIT +.. Copyright © 2026 Intel Corporation + +DisplayPort Link Training +========================= + +.. kernel-doc:: drivers/gpu/drm/i915/display/intel_dp_link_training.c + :doc: DisplayPort link training diff --git a/Documentation/gpu/intel-display/index.rst b/Documentation/gpu/intel-display/index.rst index 01c3d1e576b7..6fa929d82c38 100644 --- a/Documentation/gpu/intel-display/index.rst +++ b/Documentation/gpu/intel-display/index.rst @@ -38,6 +38,7 @@ driver. The display driver isn't an independent driver in that sense. fifo-underrun frontbuffer hotplug + dp-link-training plane psr snps-phy diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c index 3f8e025fd6d9..e1eed13a8e94 100644 --- a/drivers/gpu/drm/drm_modes.c +++ b/drivers/gpu/drm/drm_modes.c @@ -1469,6 +1469,25 @@ struct drm_display_mode *drm_mode_duplicate(struct drm_device *dev, } EXPORT_SYMBOL(drm_mode_duplicate); +static bool drm_mode_match_timings_vrr(const struct drm_display_mode *mode1, + const struct drm_display_mode *mode2) +{ + int mode1_vsync_start_offset = mode1->vtotal - mode1->vsync_start; + int mode1_vsync_end_offset = mode1->vtotal - mode1->vsync_end; + int mode2_vsync_start_offset = mode2->vtotal - mode2->vsync_start; + int mode2_vsync_end_offset = mode2->vtotal - mode2->vsync_end; + + return mode1->hdisplay == mode2->hdisplay && + mode1->hsync_start == mode2->hsync_start && + mode1->hsync_end == mode2->hsync_end && + mode1->htotal == mode2->htotal && + mode1->hskew == mode2->hskew && + mode1->vdisplay == mode2->vdisplay && + mode1_vsync_start_offset == mode2_vsync_start_offset && + mode1_vsync_end_offset == mode2_vsync_end_offset && + mode1->vscan == mode2->vscan; +} + static bool drm_mode_match_timings(const struct drm_display_mode *mode1, const struct drm_display_mode *mode2) { @@ -1538,6 +1557,10 @@ bool drm_mode_match(const struct drm_display_mode *mode1, if (!mode1 || !mode2) return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS_VRR && + !drm_mode_match_timings_vrr(mode1, mode2)) + return false; + if (match_flags & DRM_MODE_MATCH_TIMINGS && !drm_mode_match_timings(mode1, mode2)) return false; diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 07802a7f4ce5..c4de717505d7 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -83,8 +83,7 @@ i915-y += \ i915_fb_pin.o \ i915_hdcp_gsc.o \ i915_initial_plane.o \ - i915_overlay.o \ - i915_panic.o + i915_overlay.o # "Graphics Technology" (aka we talk to the gpu) gt-y += \ @@ -167,6 +166,7 @@ gem-y += \ gem/i915_gem_object.o \ gem/i915_gem_object_frontbuffer.o \ gem/i915_gem_pages.o \ + gem/i915_gem_panic.o \ gem/i915_gem_phys.o \ gem/i915_gem_pm.o \ gem/i915_gem_region.o \ @@ -356,6 +356,7 @@ i915-y += \ display/intel_dp_aux.o \ display/intel_dp_aux_backlight.o \ display/intel_dp_hdcp.o \ + display/intel_dp_link_caps.o \ display/intel_dp_link_training.o \ display/intel_dp_mst.o \ display/intel_dp_test.o \ diff --git a/drivers/gpu/drm/i915/display/g4x_dp.c b/drivers/gpu/drm/i915/display/g4x_dp.c index 5ff1cdf4581a..b867443ff227 100644 --- a/drivers/gpu/drm/i915/display/g4x_dp.c +++ b/drivers/gpu/drm/i915/display/g4x_dp.c @@ -1222,7 +1222,8 @@ static bool ilk_digital_port_connected(struct intel_encoder *encoder) return intel_de_read(display, DEISR) & bit; } -static int g4x_dp_compute_config(struct intel_encoder *encoder, +static int g4x_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -1232,7 +1233,7 @@ static int g4x_dp_compute_config(struct intel_encoder *encoder, if (HAS_PCH_SPLIT(display) && encoder->port != PORT_A) crtc_state->has_pch_encoder = true; - ret = intel_dp_compute_config(encoder, crtc_state, conn_state); + ret = intel_dp_compute_config(state, encoder, crtc_state, conn_state); if (ret) return ret; @@ -1252,10 +1253,13 @@ static void g4x_dp_suspend_complete(struct intel_encoder *encoder) static void intel_dp_encoder_destroy(struct drm_encoder *encoder) { + struct intel_digital_port *dig_port = enc_to_dig_port(to_intel_encoder(encoder)); + intel_dp_encoder_flush_work(encoder); drm_encoder_cleanup(encoder); - kfree(enc_to_dig_port(to_intel_encoder(encoder))); + intel_dp_link_cleanup(&dig_port->dp); + kfree(dig_port); } static void intel_dp_encoder_reset(struct drm_encoder *encoder) @@ -1350,6 +1354,9 @@ bool g4x_dp_init(struct intel_display *display, intel_encoder->audio_enable = g4x_dp_audio_enable; intel_encoder->audio_disable = g4x_dp_audio_disable; + if (intel_dp_link_init(&dig_port->dp) != 0) + goto err_dp_init; + if ((display->platform.ivybridge && port == PORT_A) || (HAS_PCH_CPT(display) && port != PORT_A)) { dig_port->dp.set_link_train = cpt_set_link_train; @@ -1419,6 +1426,8 @@ bool g4x_dp_init(struct intel_display *display, return true; err_init_connector: + intel_dp_link_cleanup(&dig_port->dp); +err_dp_init: drm_encoder_cleanup(encoder); err_encoder_init: kfree(intel_connector); diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index acb36cab999c..4c33aa1d1d32 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -126,12 +126,12 @@ static bool g4x_compute_has_hdmi_sink(struct intel_atomic_state *state, return false; } -static int g4x_hdmi_compute_config(struct intel_encoder *encoder, +static int g4x_hdmi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(crtc_state->uapi.state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); if (HAS_PCH_SPLIT(display)) diff --git a/drivers/gpu/drm/i915/display/icl_dsi.c b/drivers/gpu/drm/i915/display/icl_dsi.c index a549f1fac810..ea0cdb7822f3 100644 --- a/drivers/gpu/drm/i915/display/icl_dsi.c +++ b/drivers/gpu/drm/i915/display/icl_dsi.c @@ -1657,7 +1657,8 @@ static int gen11_dsi_dsc_compute_config(struct intel_encoder *encoder, return 0; } -static int gen11_dsi_compute_config(struct intel_encoder *encoder, +static int gen11_dsi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -1671,7 +1672,7 @@ static int gen11_dsi_compute_config(struct intel_encoder *encoder, pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - ret = intel_panel_compute_config(intel_connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, intel_connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_alpm.c b/drivers/gpu/drm/i915/display/intel_alpm.c index c6963ea420cc..f1383764b702 100644 --- a/drivers/gpu/drm/i915/display/intel_alpm.c +++ b/drivers/gpu/drm/i915/display/intel_alpm.c @@ -291,7 +291,9 @@ void intel_alpm_lobf_compute_config_late(struct intel_dp *intel_dp, if (!crtc_state->has_lobf) return; - if (!intel_alpm_lobf_is_window1_sufficient(crtc_state)) { + if (crtc_state->has_psr || + !intel_vrr_is_fixed_rr(crtc_state) || + !intel_alpm_lobf_is_window1_sufficient(crtc_state)) { crtc_state->has_lobf = false; return; } @@ -343,11 +345,7 @@ void intel_alpm_lobf_compute_config(struct intel_dp *intel_dp, if (!intel_dp->as_sdp_supported) return; - if (crtc_state->has_psr) - return; - - if (!intel_vrr_always_use_vrr_tg(display) || - !intel_vrr_is_fixed_rr(crtc_state)) + if (!intel_vrr_always_use_vrr_tg(display)) return; if (!(intel_alpm_aux_wake_supported(intel_dp) || @@ -407,6 +405,11 @@ static void lnl_alpm_configure(struct intel_dp *intel_dp, if (crtc_state->disable_as_sdp_when_pr_active) pr_alpm_ctl |= PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE; + if (intel_display_power_dc3co_allowed(display)) + pr_alpm_ctl |= PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL; + else + pr_alpm_ctl &= ~PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL; + intel_de_write(display, PR_ALPM_CTL(display, cpu_transcoder), pr_alpm_ctl); } diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index ded2ee497bbf..97cbae2e547e 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -2217,52 +2217,52 @@ static u8 translate_iboost(struct intel_display *display, u8 val) static const u8 cnp_ddc_pin_map[] = { [0] = 0, /* N/A */ - [GMBUS_PIN_1_BXT] = DDC_BUS_DDI_B, - [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_C, - [GMBUS_PIN_4_CNP] = DDC_BUS_DDI_D, /* sic */ - [GMBUS_PIN_3_BXT] = DDC_BUS_DDI_F, /* sic */ + [GMBUS_PIN_1] = DDC_BUS_DDI_B, + [GMBUS_PIN_2] = DDC_BUS_DDI_C, + [GMBUS_PIN_4] = DDC_BUS_DDI_D, /* sic */ + [GMBUS_PIN_3] = DDC_BUS_DDI_F, /* sic */ }; static const u8 icp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_3_BXT] = TGL_DDC_BUS_DDI_C, - [GMBUS_PIN_9_TC1_ICP] = ICL_DDC_BUS_PORT_1, - [GMBUS_PIN_10_TC2_ICP] = ICL_DDC_BUS_PORT_2, - [GMBUS_PIN_11_TC3_ICP] = ICL_DDC_BUS_PORT_3, - [GMBUS_PIN_12_TC4_ICP] = ICL_DDC_BUS_PORT_4, - [GMBUS_PIN_13_TC5_TGP] = TGL_DDC_BUS_PORT_5, - [GMBUS_PIN_14_TC6_TGP] = TGL_DDC_BUS_PORT_6, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_3] = TGL_DDC_BUS_DDI_C, + [GMBUS_PIN_9_TC1] = ICL_DDC_BUS_PORT_1, + [GMBUS_PIN_10_TC2] = ICL_DDC_BUS_PORT_2, + [GMBUS_PIN_11_TC3] = ICL_DDC_BUS_PORT_3, + [GMBUS_PIN_12_TC4] = ICL_DDC_BUS_PORT_4, + [GMBUS_PIN_13_TC5] = TGL_DDC_BUS_PORT_5, + [GMBUS_PIN_14_TC6] = TGL_DDC_BUS_PORT_6, }; static const u8 rkl_pch_tgp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = RKL_DDC_BUS_DDI_D, - [GMBUS_PIN_10_TC2_ICP] = RKL_DDC_BUS_DDI_E, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = RKL_DDC_BUS_DDI_D, + [GMBUS_PIN_10_TC2] = RKL_DDC_BUS_DDI_E, }; static const u8 adls_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_9_TC1_ICP] = ADLS_DDC_BUS_PORT_TC1, - [GMBUS_PIN_10_TC2_ICP] = ADLS_DDC_BUS_PORT_TC2, - [GMBUS_PIN_11_TC3_ICP] = ADLS_DDC_BUS_PORT_TC3, - [GMBUS_PIN_12_TC4_ICP] = ADLS_DDC_BUS_PORT_TC4, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_9_TC1] = ADLS_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2] = ADLS_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3] = ADLS_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4] = ADLS_DDC_BUS_PORT_TC4, }; static const u8 gen9bc_tgp_ddc_pin_map[] = { - [GMBUS_PIN_2_BXT] = DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = DDC_BUS_DDI_C, - [GMBUS_PIN_10_TC2_ICP] = DDC_BUS_DDI_D, + [GMBUS_PIN_2] = DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = DDC_BUS_DDI_C, + [GMBUS_PIN_10_TC2] = DDC_BUS_DDI_D, }; static const u8 adlp_ddc_pin_map[] = { - [GMBUS_PIN_1_BXT] = ICL_DDC_BUS_DDI_A, - [GMBUS_PIN_2_BXT] = ICL_DDC_BUS_DDI_B, - [GMBUS_PIN_9_TC1_ICP] = ADLP_DDC_BUS_PORT_TC1, - [GMBUS_PIN_10_TC2_ICP] = ADLP_DDC_BUS_PORT_TC2, - [GMBUS_PIN_11_TC3_ICP] = ADLP_DDC_BUS_PORT_TC3, - [GMBUS_PIN_12_TC4_ICP] = ADLP_DDC_BUS_PORT_TC4, + [GMBUS_PIN_1] = ICL_DDC_BUS_DDI_A, + [GMBUS_PIN_2] = ICL_DDC_BUS_DDI_B, + [GMBUS_PIN_9_TC1] = ADLP_DDC_BUS_PORT_TC1, + [GMBUS_PIN_10_TC2] = ADLP_DDC_BUS_PORT_TC2, + [GMBUS_PIN_11_TC3] = ADLP_DDC_BUS_PORT_TC3, + [GMBUS_PIN_12_TC4] = ADLP_DDC_BUS_PORT_TC4, }; static u8 map_ddc_pin(struct intel_display *display, u8 vbt_pin) diff --git a/drivers/gpu/drm/i915/display/intel_bw.c b/drivers/gpu/drm/i915/display/intel_bw.c index dc5a5b639d87..41539fdfeac5 100644 --- a/drivers/gpu/drm/i915/display/intel_bw.c +++ b/drivers/gpu/drm/i915/display/intel_bw.c @@ -59,7 +59,7 @@ struct intel_psf_gv_point { struct intel_qgv_info { struct intel_qgv_point points[I915_NUM_QGV_POINTS]; struct intel_psf_gv_point psf_points[I915_NUM_PSF_GV_POINTS]; - u8 num_points; + u8 num_qgv_points; u8 num_psf_points; u8 t_bl; u8 max_numchannels; @@ -155,8 +155,8 @@ static int adls_pcode_read_psf_gv_point_info(struct intel_display *display, static u16 icl_qgv_points_mask(struct intel_display *display) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; u16 qgv_points = 0, psf_points = 0; /* @@ -252,7 +252,7 @@ static int icl_get_qgv_points(struct intel_display *display, { int i, ret; - qi->num_points = dram_info->num_qgv_points; + qi->num_qgv_points = dram_info->num_qgv_points; qi->num_psf_points = dram_info->num_psf_gv_points; if (DISPLAY_VER(display) >= 14) { @@ -324,10 +324,10 @@ static int icl_get_qgv_points(struct intel_display *display, } if (drm_WARN_ON(display->drm, - qi->num_points > ARRAY_SIZE(qi->points))) - qi->num_points = ARRAY_SIZE(qi->points); + qi->num_qgv_points > ARRAY_SIZE(qi->points))) + qi->num_qgv_points = ARRAY_SIZE(qi->points); - for (i = 0; i < qi->num_points; i++) { + for (i = 0; i < qi->num_qgv_points; i++) { struct intel_qgv_point *sp = &qi->points[i]; ret = intel_read_qgv_point_info(display, sp, i); @@ -373,7 +373,7 @@ static int icl_sagv_max_dclk(const struct intel_qgv_info *qi) u16 dclk = 0; int i; - for (i = 0; i < qi->num_points; i++) + for (i = 0; i < qi->num_qgv_points; i++) dclk = max(dclk, qi->points[i].dclk); return dclk; @@ -536,6 +536,9 @@ static int icl_get_bw_info(struct intel_display *display, ipqdepth = min(ipqdepthpch, display_bw_params->displayrtids / num_channels); qi.deinterleave = DIV_ROUND_UP(num_channels, is_y_tile(display) ? 4 : 2); + display->bw.num_qgv_points = qi.num_qgv_points; + display->bw.num_psf_gv_points = qi.num_psf_points; + for (i = 0; i < num_groups; i++) { struct intel_bw_info *bi = &display->bw.max[i]; int clpchgroup; @@ -544,10 +547,7 @@ static int icl_get_bw_info(struct intel_display *display, clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i; bi->num_planes = (ipqdepth - clpchgroup) / clpchgroup + 1; - bi->num_qgv_points = qi.num_points; - bi->num_psf_gv_points = qi.num_psf_points; - - for (j = 0; j < qi.num_points; j++) { + for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -574,7 +574,7 @@ static int icl_get_bw_info(struct intel_display *display, * SAGV point, but we can't send PCode commands to restrict it * as it will fail and pointless anyway. */ - if (qi.num_points == 1) + if (qi.num_qgv_points == 1) display->sagv.status = I915_SAGV_NOT_CONTROLLED; else display->sagv.status = I915_SAGV_ENABLED; @@ -629,16 +629,20 @@ static int tgl_get_bw_info(struct intel_display *display, */ clperchgroup = 4 * (8 / num_channels) * qi.deinterleave; + display->bw.num_qgv_points = qi.num_qgv_points; + display->bw.num_psf_gv_points = qi.num_psf_points; + + display->bw.max[0].num_planes = U8_MAX; + for (i = 0; i < num_groups; i++) { struct intel_bw_info *bi = &display->bw.max[i]; - struct intel_bw_info *bi_next; int clpchgroup; int j; clpchgroup = (display_bw_params->deburst * qi.deinterleave / num_channels) << i; if (i < num_groups - 1) { - bi_next = &display->bw.max[i + 1]; + struct intel_bw_info *bi_next = &display->bw.max[i + 1]; if (clpchgroup < clperchgroup) bi_next->num_planes = (ipqdepth - clpchgroup) / clpchgroup; @@ -646,10 +650,7 @@ static int tgl_get_bw_info(struct intel_display *display, bi_next->num_planes = 0; } - bi->num_qgv_points = qi.num_points; - bi->num_psf_gv_points = qi.num_psf_points; - - for (j = 0; j < qi.num_points; j++) { + for (j = 0; j < qi.num_qgv_points; j++) { const struct intel_qgv_point *sp = &qi.points[j]; int ct, bw; @@ -665,23 +666,27 @@ static int tgl_get_bw_info(struct intel_display *display, bi->deratedbw[j] = min(maxdebw, bw * (100 - soc_bw_params->derating) / 100); - bi->peakbw[j] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); drm_dbg_kms(display->drm, - "BW%d / QGV %d: num_planes=%d deratedbw=%u peakbw: %u\n", - i, j, bi->num_planes, bi->deratedbw[j], - bi->peakbw[j]); + "BW%d / QGV %d: num_planes=%d deratedbw=%u\n", + i, j, bi->num_planes, bi->deratedbw[j]); } + } - for (j = 0; j < qi.num_psf_points; j++) { - const struct intel_psf_gv_point *sp = &qi.psf_points[j]; + for (i = 0; i < qi.num_qgv_points; i++) { + const struct intel_qgv_point *sp = &qi.points[i]; - bi->psf_bw[j] = adl_calc_psf_bw(sp->clk); + display->bw.peakbw[i] = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); - drm_dbg_kms(display->drm, - "BW%d / PSF GV %d: num_planes=%d bw=%u\n", - i, j, bi->num_planes, bi->psf_bw[j]); - } + drm_dbg_kms(display->drm, "QGV %d: peakbw=%u\n", i, display->bw.peakbw[i]); + } + + for (i = 0; i < qi.num_psf_points; i++) { + const struct intel_psf_gv_point *sp = &qi.psf_points[i]; + + display->bw.psf_bw[i] = adl_calc_psf_bw(sp->clk); + + drm_dbg_kms(display->drm, "PSF GV %d: bw=%u\n", i, display->bw.psf_bw[i]); } /* @@ -689,7 +694,7 @@ static int tgl_get_bw_info(struct intel_display *display, * SAGV point, but we can't send PCode commands to restrict it * as it will fail and pointless anyway. */ - if (qi.num_points == 1) + if (qi.num_qgv_points == 1) display->sagv.status = I915_SAGV_NOT_CONTROLLED; else display->sagv.status = I915_SAGV_ENABLED; @@ -699,25 +704,20 @@ static int tgl_get_bw_info(struct intel_display *display, static void dg2_get_bw_info(struct intel_display *display) { - unsigned int deratedbw = display->platform.dg2_g11 ? 38000 : 50000; - int num_groups = ARRAY_SIZE(display->bw.max); int i; - /* - * DG2 doesn't have SAGV or QGV points, just a constant max bandwidth - * that doesn't depend on the number of planes enabled. So fill all the - * plane group with constant bw information for uniformity with other - * platforms. DG2-G10 platforms have a constant 50 GB/s bandwidth, - * whereas DG2-G11 platforms have 38 GB/s. - */ - for (i = 0; i < num_groups; i++) { - struct intel_bw_info *bi = &display->bw.max[i]; + display->bw.num_qgv_points = 1; - bi->num_planes = 1; - /* Need only one dummy QGV point per group */ - bi->num_qgv_points = 1; - bi->deratedbw[0] = deratedbw; - } + display->bw.max[0].num_planes = U8_MAX; + display->bw.max[0].deratedbw[0] = display->platform.dg2_g11 ? 38000 : 50000; + + drm_dbg_kms(display->drm, + "QGV 0: deratedbw=%u\n", + display->bw.max[0].deratedbw[0]); + + /* Bandwidth does not depend on # of planes; set all groups the same */ + for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) + display->bw.max[i] = display->bw.max[0]; display->sagv.status = I915_SAGV_NOT_CONTROLLED; } @@ -741,31 +741,32 @@ static int xe2_hpd_get_bw_info(struct intel_display *display, peakbw = tgl_peakbw(num_channels, qi.channel_width, icl_sagv_max_dclk(&qi)); maxdebw = min(soc_bw_params->deprogbwlimit * 1000, peakbw * DEPROGBWPCLIMIT / 100); - for (i = 0; i < qi.num_points; i++) { + display->bw.num_qgv_points = qi.num_qgv_points; + + display->bw.max[0].num_planes = U8_MAX; + + for (i = 0; i < qi.num_qgv_points; i++) { const struct intel_qgv_point *sp = &qi.points[i]; int bw = tgl_peakbw(num_channels, qi.channel_width, sp->dclk); display->bw.max[0].deratedbw[i] = min(maxdebw, (100 - soc_bw_params->derating) * bw / 100); - display->bw.max[0].peakbw[i] = bw; - drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw: %u\n", - i, display->bw.max[0].deratedbw[i], - display->bw.max[0].peakbw[i]); + display->bw.peakbw[i] = bw; + + drm_dbg_kms(display->drm, "QGV %d: deratedbw=%u peakbw=%u\n", + i, display->bw.max[0].deratedbw[i], display->bw.peakbw[i]); } /* Bandwidth does not depend on # of planes; set all groups the same */ - display->bw.max[0].num_planes = 1; - display->bw.max[0].num_qgv_points = qi.num_points; for (i = 1; i < ARRAY_SIZE(display->bw.max); i++) - memcpy(&display->bw.max[i], &display->bw.max[0], - sizeof(display->bw.max[0])); + display->bw.max[i] = display->bw.max[0]; /* * Xe2_HPD should always have exactly two QGV points representing * battery and plugged-in operation. */ - drm_WARN_ON(display->drm, qi.num_points != 2); + drm_WARN_ON(display->drm, qi.num_qgv_points != 2); display->sagv.status = I915_SAGV_ENABLED; return 0; @@ -776,6 +777,9 @@ static unsigned int icl_max_bw_index(struct intel_display *display, { int i; + if (qgv_point >= display->bw.num_qgv_points) + return UINT_MAX; + /* * Let's return max bw for 0 planes */ @@ -785,13 +789,6 @@ static unsigned int icl_max_bw_index(struct intel_display *display, const struct intel_bw_info *bi = &display->bw.max[i]; - /* - * Pcode will not expose all QGV points when - * SAGV is forced to off/min/med/max. - */ - if (qgv_point >= bi->num_qgv_points) - return UINT_MAX; - if (num_planes >= bi->num_planes) return i; } @@ -804,31 +801,24 @@ static unsigned int tgl_max_bw_index(struct intel_display *display, { int i; + if (qgv_point >= display->bw.num_qgv_points) + return UINT_MAX; + for (i = ARRAY_SIZE(display->bw.max) - 1; i >= 0; i--) { const struct intel_bw_info *bi = &display->bw.max[i]; - /* - * Pcode will not expose all QGV points when - * SAGV is forced to off/min/med/max. - */ - if (qgv_point >= bi->num_qgv_points) - return UINT_MAX; - if (num_planes <= bi->num_planes) return i; } - return 0; + return UINT_MAX; } static unsigned int adl_psf_bw(struct intel_display *display, int psf_gv_point) { - const struct intel_bw_info *bi = - &display->bw.max[0]; - - return bi->psf_bw[psf_gv_point]; + return display->bw.psf_bw[psf_gv_point]; } static unsigned int icl_qgv_bw(struct intel_display *display, @@ -950,7 +940,7 @@ intel_atomic_get_bw_state(struct intel_atomic_state *state) static unsigned int icl_max_bw_qgv_point_mask(struct intel_display *display, int num_active_planes) { - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; unsigned int max_bw_point = 0; unsigned int max_bw = 0; int i; @@ -986,7 +976,7 @@ static u16 icl_prepare_qgv_points_mask(struct intel_display *display, static unsigned int icl_max_bw_psf_gv_point_mask(struct intel_display *display) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; unsigned int max_bw_point_mask = 0; unsigned int max_bw = 0; int i; @@ -1091,7 +1081,7 @@ static int mtl_find_qgv_points(struct intel_display *display, struct intel_bw_state *new_bw_state) { unsigned int best_rate = UINT_MAX; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; unsigned int qgv_peak_bw = 0; int i; int ret; @@ -1116,21 +1106,15 @@ static int mtl_find_qgv_points(struct intel_display *display, * offered per plane group */ for (i = 0; i < num_qgv_points; i++) { - unsigned int bw_index = - tgl_max_bw_index(display, num_active_planes, i); - unsigned int max_data_rate; - - if (bw_index >= ARRAY_SIZE(display->bw.max)) - continue; - - max_data_rate = display->bw.max[bw_index].deratedbw[i]; + unsigned int max_data_rate = + icl_qgv_bw(display, num_active_planes, i); if (max_data_rate < data_rate) continue; - if (max_data_rate - data_rate < best_rate) { - best_rate = max_data_rate - data_rate; - qgv_peak_bw = display->bw.max[bw_index].peakbw[i]; + if (max_data_rate < best_rate) { + best_rate = max_data_rate; + qgv_peak_bw = display->bw.peakbw[i]; } drm_dbg_kms(display->drm, "QGV point %d: max bw %d required %d qgv_peak_bw: %d\n", @@ -1162,8 +1146,8 @@ static int icl_find_qgv_points(struct intel_display *display, const struct intel_bw_state *old_bw_state, struct intel_bw_state *new_bw_state) { - unsigned int num_psf_gv_points = display->bw.max[0].num_psf_gv_points; - unsigned int num_qgv_points = display->bw.max[0].num_qgv_points; + unsigned int num_psf_gv_points = display->bw.num_psf_gv_points; + unsigned int num_qgv_points = display->bw.num_qgv_points; u16 psf_points = 0; u16 qgv_points = 0; int i; diff --git a/drivers/gpu/drm/i915/display/intel_cdclk.c b/drivers/gpu/drm/i915/display/intel_cdclk.c index 7bc9b956554b..d3c5e3438d19 100644 --- a/drivers/gpu/drm/i915/display/intel_cdclk.c +++ b/drivers/gpu/drm/i915/display/intel_cdclk.c @@ -1229,23 +1229,28 @@ static void skl_set_cdclk(struct intel_display *display, static void skl_sanitize_cdclk(struct intel_display *display) { - u32 cdctl, expected; + u32 cdctl, expected, swf18; /* * check if the pre-os initialized the display * There is SWF18 scratchpad register defined which is set by the * pre-os which can be used by the OS drivers to check the status */ - if ((intel_de_read(display, SWF_ILK(0x18)) & 0x00FFFFFF) == 0) + swf18 = intel_de_read(display, SWF_ILK(0x18)); + if ((swf18 & 0x00FFFFFF) == 0) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to SWF18 0x%x\n", swf18); goto sanitize; + } intel_update_cdclk(display); intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK"); /* Is PLL enabled and locked ? */ if (display->cdclk.hw.vco == 0 || - display->cdclk.hw.cdclk == display->cdclk.hw.bypass) + display->cdclk.hw.cdclk == display->cdclk.hw.bypass) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n"); goto sanitize; + } /* DPLL okay; verify the cdclock * @@ -1261,8 +1266,11 @@ static void skl_sanitize_cdclk(struct intel_display *display) cdctl &= ~CDCLK_FREQ_DECIMAL_MASK; cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; - if (cdctl != expected) + if (cdctl != expected) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x)\n", + intel_de_read(display, CDCLK_CTL), expected); goto sanitize; + } drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", intel_de_read(display, CDCLK_CTL), expected); @@ -1274,8 +1282,6 @@ static void skl_sanitize_cdclk(struct intel_display *display) return; sanitize: - drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); - /* force cdclk programming */ display->cdclk.hw.cdclk = 0; /* force full PLL disable + enable */ @@ -1938,14 +1944,17 @@ static void adlp_cdclk_pll_crawl(struct intel_display *display, int vco) display->cdclk.hw.vco = vco; } +static u32 bxt_cdclk_cd2x_pipe_mask(struct intel_display *display) +{ + if (DISPLAY_VER(display) >= 11) + return ICL_CDCLK_CD2X_PIPE_MASK; + else + return BXT_CDCLK_CD2X_PIPE_MASK; +} + static u32 bxt_cdclk_cd2x_pipe(struct intel_display *display, enum pipe pipe) { - if (DISPLAY_VER(display) >= 12) { - if (pipe == INVALID_PIPE) - return TGL_CDCLK_CD2X_PIPE_NONE; - else - return TGL_CDCLK_CD2X_PIPE(pipe); - } else if (DISPLAY_VER(display) >= 11) { + if (DISPLAY_VER(display) >= 11) { if (pipe == INVALID_PIPE) return ICL_CDCLK_CD2X_PIPE_NONE; else @@ -2340,18 +2349,24 @@ static void bxt_sanitize_cdclk(struct intel_display *display) intel_cdclk_dump_config(display, &display->cdclk.hw, "Current CDCLK"); if (display->cdclk.hw.vco == 0 || - display->cdclk.hw.cdclk == display->cdclk.hw.bypass) + display->cdclk.hw.cdclk == display->cdclk.hw.bypass) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to PLL not enabled/locked\n"); goto sanitize; + } /* Make sure this is a legal cdclk value for the platform */ cdclk = bxt_calc_cdclk(display, display->cdclk.hw.cdclk); - if (cdclk != display->cdclk.hw.cdclk) + if (cdclk != display->cdclk.hw.cdclk) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad CDCLK frequency\n"); goto sanitize; + } /* Make sure the VCO is correct for the cdclk */ vco = bxt_calc_cdclk_pll_vco(display, cdclk); - if (vco != display->cdclk.hw.vco) + if (vco != display->cdclk.hw.vco) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to bad VCO frequency\n"); goto sanitize; + } /* * Some BIOS versions leave an incorrect decimal frequency value and @@ -2366,7 +2381,7 @@ static void bxt_sanitize_cdclk(struct intel_display *display) * dividers both syncing to an active pipe, or asynchronously * (PIPE_NONE). */ - cdctl &= ~bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); + cdctl &= ~bxt_cdclk_cd2x_pipe_mask(display); cdctl |= bxt_cdclk_cd2x_pipe(display, INVALID_PIPE); if (cdctl != expected) { @@ -2375,8 +2390,11 @@ static void bxt_sanitize_cdclk(struct intel_display *display) cdctl |= expected & CDCLK_FREQ_DECIMAL_MASK; } - if (cdctl != expected) + if (cdctl != expected) { + drm_dbg_kms(display->drm, "Sanitizing CDCLK due to CDCLK_CTL 0x%x, expected 0x%x\n", + intel_de_read(display, CDCLK_CTL), expected); goto sanitize; + } drm_dbg_kms(display->drm, "Sanitizing CDCLK decimal divider (CDCLK_CTL 0x%x, expected 0x%x)\n", intel_de_read(display, CDCLK_CTL), expected); @@ -2388,8 +2406,6 @@ static void bxt_sanitize_cdclk(struct intel_display *display) return; sanitize: - drm_dbg_kms(display->drm, "Sanitizing cdclk programmed by pre-os\n"); - /* force cdclk programming */ display->cdclk.hw.cdclk = 0; diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.c b/drivers/gpu/drm/i915/display/intel_cmtg.c index e1fdc6fe9762..c8e0f90af910 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.c +++ b/drivers/gpu/drm/i915/display/intel_cmtg.c @@ -4,7 +4,6 @@ */ #include <linux/string_choices.h> -#include <linux/types.h> #include <drm/drm_device.h> #include <drm/drm_print.h> @@ -13,9 +12,14 @@ #include "intel_cmtg_regs.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display.h" #include "intel_display_device.h" +#include "intel_display_irq.h" #include "intel_display_power.h" #include "intel_display_regs.h" +#include "intel_display_types.h" +#include "intel_vrr.h" +#include "intel_vrr_regs.h" /** * DOC: Common Primary Timing Generator (CMTG) @@ -81,6 +85,18 @@ static void intel_cmtg_dump_config(struct intel_display *display, str_yes_no(cmtg_config->trans_b_secondary)); } +static inline enum transcoder to_cmtg_transcoder(enum transcoder cpu_transcoder) +{ + switch (cpu_transcoder) { + case TRANSCODER_A: + return TRANSCODER_CMTG0; + case TRANSCODER_B: + return TRANSCODER_CMTG1; + default: + return INVALID_TRANSCODER; + } +} + static bool intel_cmtg_transcoder_is_secondary(struct intel_display *display, enum transcoder trans) { @@ -103,11 +119,11 @@ static void intel_cmtg_get_config(struct intel_display *display, { u32 val; - val = intel_de_read(display, TRANS_CMTG_CTL_A); + val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_A)); cmtg_config->cmtg_a_enable = val & CMTG_ENABLE; if (intel_cmtg_has_cmtg_b(display)) { - val = intel_de_read(display, TRANS_CMTG_CTL_B); + val = intel_de_read(display, TRANS_CMTG_CTL(TRANSCODER_B)); cmtg_config->cmtg_b_enable = val & CMTG_ENABLE; } @@ -124,8 +140,8 @@ static bool intel_cmtg_disable_requires_modeset(struct intel_display *display, return cmtg_config->trans_a_secondary || cmtg_config->trans_b_secondary; } -static void intel_cmtg_disable(struct intel_display *display, - struct intel_cmtg_config *cmtg_config) +static void intel_cmtg_disable_all(struct intel_display *display, + struct intel_cmtg_config *cmtg_config) { u32 clk_sel_clr = 0; u32 clk_sel_set = 0; @@ -140,14 +156,14 @@ static void intel_cmtg_disable(struct intel_display *display, if (cmtg_config->cmtg_a_enable) { drm_dbg_kms(display->drm, "Disabling CMTG A\n"); - intel_de_rmw(display, TRANS_CMTG_CTL_A, CMTG_ENABLE, 0); + intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_A), CMTG_ENABLE, 0); clk_sel_clr |= CMTG_CLK_SEL_A_MASK; clk_sel_set |= CMTG_CLK_SEL_A_DISABLED; } if (cmtg_config->cmtg_b_enable) { drm_dbg_kms(display->drm, "Disabling CMTG B\n"); - intel_de_rmw(display, TRANS_CMTG_CTL_B, CMTG_ENABLE, 0); + intel_de_rmw(display, TRANS_CMTG_CTL(TRANSCODER_B), CMTG_ENABLE, 0); clk_sel_clr |= CMTG_CLK_SEL_B_MASK; clk_sel_set |= CMTG_CLK_SEL_B_DISABLED; } @@ -156,6 +172,57 @@ static void intel_cmtg_disable(struct intel_display *display, intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); } +void intel_cmtg_disable(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + u32 clk_sel_clr = 0, interrupt_mask = 0; + + if (!crtc->cmtg.enabled) + return; + + if (drm_WARN_ON(display->drm, cmtg_transcoder == INVALID_TRANSCODER)) + return; + + crtc->cmtg.enabled = false; + intel_de_rmw(display, TRANS_VRR_CTL(display, cmtg_transcoder), + VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN, 0); + + /* + * Use cpu_transcoder for: + * 1. Exclusive CMTG registers that do not use the standard transcoder offset + * (e.g., TRANS_CMTG_CTL, CMTG_CLK_SEL). + * 2. Registers shared between the eDP and CMTG transcoders. + * (e.g., TRANS_DDI_FUNC_CTL2). + */ + + intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), + CMTG_SECONDARY_MODE, 0); + intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), CMTG_HW_GB_ENABLE, 0); + + intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_ENABLE, 0); + + if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), CMTG_STATE, 50)) { + drm_WARN(display->drm, 1, "CMTG: %s disable timeout\n", + transcoder_name(cpu_transcoder)); + return; + } + + clk_sel_clr = cpu_transcoder == TRANSCODER_A ? CMTG_CLK_SEL_A_MASK : CMTG_CLK_SEL_B_MASK; + intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, 0); + + drm_dbg_kms(display->drm, "CMTG: %s disabled\n", transcoder_name(cpu_transcoder)); + + if (cpu_transcoder == TRANSCODER_A) + interrupt_mask = CMTG_VBLANK_A; + else if (cpu_transcoder == TRANSCODER_B) + interrupt_mask = CMTG_VBLANK_B; + + intel_display_irq_port_interrupt_mask(display, interrupt_mask, true); +} + /* * Read out CMTG configuration and, on platforms that allow disabling it without * a modeset, do it. @@ -183,5 +250,202 @@ void intel_cmtg_sanitize(struct intel_display *display) if (intel_cmtg_disable_requires_modeset(display, &cmtg_config)) return; - intel_cmtg_disable(display, &cmtg_config); + intel_cmtg_disable_all(display, &cmtg_config); +} + +bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + + if ((cpu_transcoder == TRANSCODER_A || cpu_transcoder == TRANSCODER_B) && + DISPLAY_VER(display) == 35 && intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) + return true; + + return false; +} + +void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 clk_sel_clr = 0; + u32 clk_sel_set = 0; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + if (cpu_transcoder == TRANSCODER_A) { + clk_sel_clr = CMTG_CLK_SEL_A_MASK; + clk_sel_set = CMTG_CLK_SELECT_PHYA_ENABLE; + } else if (cpu_transcoder == TRANSCODER_B) { + clk_sel_clr = CMTG_CLK_SEL_B_MASK; + clk_sel_set = CMTG_CLK_SELECT_PHYB_ENABLE; + } + + if (clk_sel_set) + intel_de_rmw(display, CMTG_CLK_SEL, clk_sel_clr, clk_sel_set); +} + +void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type) +{ + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + + if (cmtg_transcoder == INVALID_TRANSCODER) + return; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + if (type == LRR) + intel_set_transcoder_timings_lrr(crtc_state, cmtg_transcoder); + else + intel_set_transcoder_timings(crtc_state, cmtg_transcoder); +} + +void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state) +{ + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + intel_vrr_set_fixed_rr_timings(crtc_state, cmtg_transcoder); +} + +void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + u32 vrr_ctl; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + vrr_ctl = VRR_CTL_VRR_ENABLE | VRR_CTL_FLIP_LINE_EN | + XELPD_VRR_CTL_VRR_GUARDBAND(crtc_state->vrr.guardband); + + /* TODO: The code below may need to be revisited once CMRR is enabled */ + if (crtc_state->cmrr.enable) + vrr_ctl |= VRR_CTL_CMRR_ENABLE; + + intel_de_write(display, TRANS_VRR_CTL(display, cmtg_transcoder), vrr_ctl); +} + +void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cmtg_transcoder = to_cmtg_transcoder(crtc_state->cpu_transcoder); + const struct intel_link_m_n *m_n = &crtc_state->dp_m_n; + + if (!intel_cmtg_is_allowed(crtc_state)) + return; + + intel_de_write(display, PIPE_LINK_M1(display, cmtg_transcoder), m_n->link_m); + intel_de_write(display, PIPE_LINK_N1(display, cmtg_transcoder), m_n->link_n); +} + +static void intel_cmtg_enable_sync(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 cmtg_ctl; + + cmtg_ctl = CMTG_SYNC_TO_PORT | CMTG_ENABLE; + + intel_de_rmw(display, TRANS_CMTG_CTL(cpu_transcoder), 0, cmtg_ctl); + if (intel_de_wait_for_clear_ms(display, TRANS_CMTG_CTL(cpu_transcoder), + CMTG_SYNC_TO_PORT, 50)) { + drm_WARN(display->drm, 1, "CMTG: %s enable timeout\n", + transcoder_name(cpu_transcoder)); + } +} + +static void intel_cmtg_enable_ddi(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 interrupt_mask = 0; + + intel_de_rmw(display, TRANS_DDI_FUNC_CTL2(display, cpu_transcoder), 0, CMTG_SECONDARY_MODE); + intel_de_rmw(display, CMTG_SCANLINE_GB1(cpu_transcoder), 0, CMTG_HW_GB_ENABLE); + crtc->cmtg.enabled = true; + drm_dbg_kms(display->drm, "CMTG: %s enabled\n", transcoder_name(cpu_transcoder)); + + /* + * TODO: Currently cmtg is enabled along with eDP transcoder so cmtg + * interrupt is not enabled through IER, need to do some fine + * tuning in future. + */ + + if (cpu_transcoder == TRANSCODER_A) + interrupt_mask = CMTG_VBLANK_A; + else if (cpu_transcoder == TRANSCODER_B) + interrupt_mask = CMTG_VBLANK_B; + + intel_display_irq_port_interrupt_mask(display, interrupt_mask, false); +} + +/* Bspec: 75253 */ +#define DC3CO_ENTRY_LATENCY_US 55 +#define DC3CO_EXIT_LATENCY_US 40 + +static void intel_cmtg_set_hwgb(const struct intel_crtc_state *crtc_state) +{ + struct intel_display *display = to_intel_display(crtc_state); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + u32 breakeven_gb; + u32 dc5_exit_latency; + u32 line_time_us = 75; /* Max default initialization value */ + u32 val; + + if (crtc_state->linetime) + line_time_us = DIV_ROUND_UP(crtc_state->linetime, 8); + + /* Break Even Guardband - DC3co Entry Latency / linetime */ + breakeven_gb = DIV_ROUND_UP(DC3CO_ENTRY_LATENCY_US, line_time_us); + + /* DC5 Exit Latency - DC3co Exit Latency / linetime */ + dc5_exit_latency = DIV_ROUND_UP(DC3CO_EXIT_LATENCY_US, line_time_us); + + val = REG_FIELD_PREP(CMTG_HW_GB_BREAKEVEN_MASK, breakeven_gb) | + REG_FIELD_PREP(CMTG_HW_GB_DC5_EXIT_LATENCY_MASK, dc5_exit_latency) | + REG_FIELD_PREP(CMTG_HW_GB_UP_LW_BG_DIFF_MASK, 1); + + intel_de_write(display, CMTG_HW_GB(cpu_transcoder), val); +} + +static void intel_cmtg_restore(const struct intel_crtc_state *crtc_state) +{ + intel_cmtg_set_clk_select(crtc_state); + intel_cmtg_set_timings(crtc_state, MODESET); + intel_cmtg_set_vrr_timings(crtc_state); + intel_cmtg_set_vrr_ctl(crtc_state); + intel_cmtg_set_m_n(crtc_state); +} + +void intel_cmtg_program(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + struct intel_crtc *crtc; + struct intel_crtc_state *new_crtc_state; + bool dc3co_to_dc6 = intel_display_power_get_and_reset_dc3co_to_dc6(display); + + for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) { + bool modeset = intel_crtc_needs_modeset(new_crtc_state); + + if (!intel_cmtg_is_allowed(new_crtc_state)) + continue; + + if ((modeset || dc3co_to_dc6) && + new_crtc_state->hw.active && !crtc->cmtg.enabled) { + if (dc3co_to_dc6) + intel_cmtg_restore(new_crtc_state); + + intel_cmtg_enable_sync(new_crtc_state); + intel_cmtg_set_hwgb(new_crtc_state); + intel_cmtg_enable_ddi(new_crtc_state); + } + } } diff --git a/drivers/gpu/drm/i915/display/intel_cmtg.h b/drivers/gpu/drm/i915/display/intel_cmtg.h index ba62199adaa2..a08cb2dcee67 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg.h @@ -6,8 +6,25 @@ #ifndef __INTEL_CMTG_H__ #define __INTEL_CMTG_H__ +#include <linux/types.h> + +struct intel_atomic_state; struct intel_display; +struct intel_crtc_state; + +enum set_timing_type { + MODESET = 0, + LRR +}; +void intel_cmtg_disable(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_m_n(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_vrr_timings(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_vrr_ctl(const struct intel_crtc_state *crtc_state); +void intel_cmtg_set_timings(const struct intel_crtc_state *crtc_state, enum set_timing_type type); +void intel_cmtg_set_clk_select(const struct intel_crtc_state *crtc_state); void intel_cmtg_sanitize(struct intel_display *display); +bool intel_cmtg_is_allowed(const struct intel_crtc_state *crtc_state); +void intel_cmtg_program(struct intel_atomic_state *state); #endif /* __INTEL_CMTG_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h index 945a35578284..18dcb665df04 100644 --- a/drivers/gpu/drm/i915/display/intel_cmtg_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cmtg_regs.h @@ -10,12 +10,32 @@ #define CMTG_CLK_SEL _MMIO(0x46160) #define CMTG_CLK_SEL_A_MASK REG_GENMASK(31, 29) +#define CMTG_CLK_SELECT_PHYA_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0x4) #define CMTG_CLK_SEL_A_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_A_MASK, 0) #define CMTG_CLK_SEL_B_MASK REG_GENMASK(15, 13) +#define CMTG_CLK_SELECT_PHYB_ENABLE REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0x6) #define CMTG_CLK_SEL_B_DISABLED REG_FIELD_PREP(CMTG_CLK_SEL_B_MASK, 0) -#define TRANS_CMTG_CTL_A _MMIO(0x6fa88) -#define TRANS_CMTG_CTL_B _MMIO(0x6fb88) +#define _TRANS_CMTG_CTL_A 0x6fa88 +#define _TRANS_CMTG_CTL_B 0x6fb88 +#define TRANS_CMTG_CTL(trans) _MMIO_TRANS((trans), \ + _TRANS_CMTG_CTL_A, _TRANS_CMTG_CTL_B) #define CMTG_ENABLE REG_BIT(31) +#define CMTG_SYNC_TO_PORT REG_BIT(29) +#define CMTG_STATE REG_BIT(23) + +#define _CMTG_HW_GB_A 0x6fa8c +#define _CMTG_HW_GB_B 0x6fb8c +#define CMTG_HW_GB(trans) _MMIO_TRANS((trans), \ + _CMTG_HW_GB_A, _CMTG_HW_GB_B) +#define CMTG_HW_GB_BREAKEVEN_MASK REG_GENMASK(11, 0) +#define CMTG_HW_GB_DC5_EXIT_LATENCY_MASK REG_GENMASK(27, 16) +#define CMTG_HW_GB_UP_LW_BG_DIFF_MASK REG_GENMASK(31, 28) + +#define _CMTG_SCANLINE_GB1_A 0x456A0 +#define _CMTG_SCANLINE_GB1_B 0x456C0 +#define CMTG_SCANLINE_GB1(trans) _MMIO_TRANS((trans), \ + _CMTG_SCANLINE_GB1_A, _CMTG_SCANLINE_GB1_B) +#define CMTG_HW_GB_ENABLE REG_BIT(31) #endif /* __INTEL_CMTG_REGS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_color.c b/drivers/gpu/drm/i915/display/intel_color.c index 7ef870cd9a16..87ced9f6ff40 100644 --- a/drivers/gpu/drm/i915/display/intel_color.c +++ b/drivers/gpu/drm/i915/display/intel_color.c @@ -3967,48 +3967,34 @@ xelpd_program_plane_pre_csc_lut(struct intel_dsb *dsb, enum pipe pipe = to_intel_plane(state->plane)->pipe; enum plane_id plane = to_intel_plane(state->plane)->id; const struct drm_color_lut32 *pre_csc_lut = plane_state->hw.degamma_lut->data; - u32 i, lut_size; + int i, lut_size = 128; + u32 lut_val; - if (icl_is_hdr_plane(display, plane)) { - lut_size = 128; + if (!icl_is_hdr_plane(display, plane)) + return; - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + for (i = 0; i < lut_size + 3; i++) { if (pre_csc_lut) { - for (i = 0; i < lut_size; i++) { - u32 lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); - - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - - /* Program the max register to clamp values > 1.0. */ - /* TODO: Restrict to 0x7ffffff */ - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - (1 << 24)); - } while (i++ < 130); + if (i < lut_size) + lut_val = drm_color_lut32_extract(pre_csc_lut[i].green, 24); + /* else duplicate last lut_val */ } else { - for (i = 0; i < lut_size; i++) { - u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); - - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), v); - } - - do { - intel_de_write_dsb(display, dsb, - PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 130); + if (i < lut_size) + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + else + lut_val = 1 << 24; } - intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); + intel_de_write_dsb(display, dsb, + PLANE_PRE_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } + + intel_de_write_dsb(display, dsb, PLANE_PRE_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); } static void @@ -4020,51 +4006,38 @@ xelpd_program_plane_post_csc_lut(struct intel_dsb *dsb, enum pipe pipe = to_intel_plane(state->plane)->pipe; enum plane_id plane = to_intel_plane(state->plane)->id; const struct drm_color_lut32 *post_csc_lut = plane_state->hw.gamma_lut->data; - u32 i, lut_size, lut_val; - - if (icl_is_hdr_plane(display, plane)) { - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); - /* TODO: Add macro */ - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), - PLANE_PAL_PREC_AUTO_INCREMENT); + int i, lut_size = 32; + u32 lut_val; + + if (!icl_is_hdr_plane(display, plane)) + return; + + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + /* TODO: Add macro */ + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), + PLANE_PAL_PREC_AUTO_INCREMENT); + + for (i = 0; i < lut_size + 3; i++) { if (post_csc_lut) { - lut_size = 32; - for (i = 0; i < lut_size; i++) { + if (i < lut_size) lut_val = drm_color_lut32_extract(post_csc_lut[i].green, 24); - - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - lut_val); - } - - /* Segment 2 */ - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - (1 << 24)); - } while (i++ < 34); + /* else clamp to the last LUT value to prevent step discontinuity */ } else { - /*TODO: Add for segment 0 */ - lut_size = 32; - for (i = 0; i < lut_size; i++) { - u32 v = (i * ((1 << 24) - 1)) / (lut_size - 1); - - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), v); - } - - do { - intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), - 1 << 24); - } while (i++ < 34); + if (i < lut_size) + lut_val = (i * ((1 << 24) - 1)) / (lut_size - 1); + else + lut_val = 1 << 24; } - intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); intel_de_write_dsb(display, dsb, - PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0); + PLANE_POST_CSC_GAMC_DATA_ENH(pipe, plane, 0), + lut_val); } + + intel_de_write_dsb(display, dsb, PLANE_POST_CSC_GAMC_INDEX_ENH(pipe, plane, 0), 0); + intel_de_write_dsb(display, dsb, + PLANE_POST_CSC_GAMC_SEG0_INDEX_ENH(pipe, plane, 0), 0); } static void diff --git a/drivers/gpu/drm/i915/display/intel_crt.c b/drivers/gpu/drm/i915/display/intel_crt.c index 243e332bef57..5b8968197fbc 100644 --- a/drivers/gpu/drm/i915/display/intel_crt.c +++ b/drivers/gpu/drm/i915/display/intel_crt.c @@ -397,7 +397,8 @@ intel_crt_mode_valid(struct drm_connector *connector, return MODE_OK; } -static int intel_crt_compute_config(struct intel_encoder *encoder, +static int intel_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -413,7 +414,8 @@ static int intel_crt_compute_config(struct intel_encoder *encoder, return 0; } -static int pch_crt_compute_config(struct intel_encoder *encoder, +static int pch_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -432,7 +434,8 @@ static int pch_crt_compute_config(struct intel_encoder *encoder, return 0; } -static int hsw_crt_compute_config(struct intel_encoder *encoder, +static int hsw_crt_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { diff --git a/drivers/gpu/drm/i915/display/intel_cursor.c b/drivers/gpu/drm/i915/display/intel_cursor.c index 52347668f27d..88384dea868b 100644 --- a/drivers/gpu/drm/i915/display/intel_cursor.c +++ b/drivers/gpu/drm/i915/display/intel_cursor.c @@ -876,7 +876,7 @@ intel_legacy_cursor_update(struct drm_plane *_plane, new_plane_state->uapi.crtc_w = crtc_w; new_plane_state->uapi.crtc_h = crtc_h; - intel_plane_copy_uapi_to_hw_state(new_plane_state, new_plane_state, crtc); + intel_plane_copy_uapi_to_hw_state(NULL, new_plane_state, new_plane_state, crtc); ret = intel_plane_atomic_check_with_state(crtc_state, new_crtc_state, old_plane_state, new_plane_state); diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 24a51ab21b55..452062417ce9 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -9,6 +9,7 @@ #include <drm/drm_print.h> #include "intel_alpm.h" +#include "intel_cmtg.h" #include "intel_cx0_phy.h" #include "intel_cx0_phy_regs.h" #include "intel_display_regs.h" @@ -3418,10 +3419,20 @@ void intel_mtl_pll_enable(struct intel_encoder *encoder, void intel_mtl_pll_enable_clock(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { + struct intel_display *display = to_intel_display(encoder); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); if (intel_tc_port_in_tbt_alt_mode(dig_port)) intel_mtl_tbt_pll_enable_clock(encoder, crtc_state->port_clock); + + /* + * CMTG can be enabled only when the transcoder and port are compatible + * (transcoder A with port A, transcoder B with port B). + */ + if (HAS_LT_PHY(display) && + ((crtc_state->cpu_transcoder == TRANSCODER_A && encoder->port == PORT_A) || + (crtc_state->cpu_transcoder == TRANSCODER_B && encoder->port == PORT_B))) + intel_cmtg_set_clk_select(crtc_state); } /* diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 1428e7a5a318..95c20eb4b4b7 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -60,7 +60,6 @@ void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_cx0_powerdown_change_sequence(struct intel_encoder *encoder, u8 lane_mask, u8 state); -int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); void intel_cx0_setup_powerdown(struct intel_encoder *encoder); bool intel_cx0_is_hdmi_frl(u32 clock); u8 intel_cx0_read(struct intel_encoder *encoder, u8 lane_mask, u16 addr); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 6296635c4e79..2b7eb010511b 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3501,6 +3501,8 @@ static void intel_ddi_enable_hdmi(struct intel_atomic_state *state, } intel_ddi_buf_enable(encoder, buf_ctl); + + intel_hdmi_poll_for_scrambling_enable(crtc_state, connector); } static void intel_ddi_enable(struct intel_atomic_state *state, @@ -4483,7 +4485,8 @@ intel_ddi_compute_output_type(struct intel_encoder *encoder, } } -static int intel_ddi_compute_config(struct intel_encoder *encoder, +static int intel_ddi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -4501,7 +4504,7 @@ static int intel_ddi_compute_config(struct intel_encoder *encoder, ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state); } else { - ret = intel_dp_compute_config(encoder, pipe_config, conn_state); + ret = intel_dp_compute_config(state, encoder, pipe_config, conn_state); } if (ret) @@ -4606,7 +4609,8 @@ intel_ddi_port_sync_transcoders(const struct intel_crtc_state *ref_crtc_state, return transcoders; } -static int intel_ddi_compute_config_late(struct intel_encoder *encoder, +static int intel_ddi_compute_config_late(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -4659,6 +4663,7 @@ static void intel_ddi_encoder_destroy(struct drm_encoder *encoder) drm_encoder_cleanup(encoder); kfree(dig_port->hdcp.port_data.streams); + intel_dp_link_cleanup(&dig_port->dp); kfree(dig_port); } @@ -4696,11 +4701,16 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) struct intel_display *display = to_intel_display(dig_port); struct intel_connector *connector; enum port port = dig_port->base.port; + int err; connector = intel_connector_alloc(); if (!connector) return -ENOMEM; + err = intel_dp_link_init(&dig_port->dp); + if (err) + goto err_dp_init; + dig_port->dp.output_reg = DDI_BUF_CTL(port); if (DISPLAY_VER(display) >= 14) dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain; @@ -4713,8 +4723,9 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) dig_port->dp.preemph_max = intel_ddi_dp_preemph_max; if (!intel_dp_init_connector(dig_port, connector)) { - kfree(connector); - return -EINVAL; + err = -EINVAL; + + goto err_init_connector; } if (dig_port->base.type == INTEL_OUTPUT_EDP) { @@ -4730,6 +4741,13 @@ static int intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) } return 0; + +err_init_connector: + intel_dp_link_cleanup(&dig_port->dp); +err_dp_init: + kfree(connector); + + return err; } static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port) @@ -4738,6 +4756,7 @@ static void intel_ddi_cleanup_dp_connector(struct intel_digital_port *dig_port) struct intel_connector *connector = intel_dp->attached_connector; intel_dp_cleanup_connector(dig_port, connector); + intel_dp_link_cleanup(intel_dp); kfree(connector); } diff --git a/drivers/gpu/drm/i915/display/intel_de.c b/drivers/gpu/drm/i915/display/intel_de.c index 6daee9e82503..a7417905192d 100644 --- a/drivers/gpu/drm/i915/display/intel_de.c +++ b/drivers/gpu/drm/i915/display/intel_de.c @@ -9,11 +9,11 @@ #include "intel_de.h" -static int __intel_de_wait_for_register(struct intel_display *display, - intel_reg_t reg, u32 mask, u32 value, - unsigned int timeout_us, - u32 (*read)(struct intel_display *display, intel_reg_t reg), - u32 *out_val, bool is_atomic) +static int intel_de_wait_for_register(struct intel_display *display, + intel_reg_t reg, u32 mask, u32 value, + unsigned int timeout_us, + u32 (*read)(struct intel_display *display, intel_reg_t reg), + u32 *out_val, bool is_atomic) { const ktime_t end = ktime_add_us(ktime_get_raw(), timeout_us); int wait_max = 1000; @@ -60,28 +60,6 @@ static int __intel_de_wait_for_register(struct intel_display *display, return ret; } -static int intel_de_wait_for_register(struct intel_display *display, - intel_reg_t reg, u32 mask, u32 value, - unsigned int fast_timeout_us, - unsigned int slow_timeout_us, - u32 (*read)(struct intel_display *display, intel_reg_t reg), - u32 *out_value, bool is_atomic) -{ - int ret = -EINVAL; - - if (fast_timeout_us) - ret = __intel_de_wait_for_register(display, reg, mask, value, - fast_timeout_us, read, - out_value, is_atomic); - - if (ret && slow_timeout_us) - ret = __intel_de_wait_for_register(display, reg, mask, value, - slow_timeout_us, read, - out_value, is_atomic); - - return ret; -} - int intel_de_wait_us(struct intel_display *display, intel_reg_t reg, u32 mask, u32 value, unsigned int timeout_us, u32 *out_value) @@ -91,7 +69,7 @@ int intel_de_wait_us(struct intel_display *display, intel_reg_t reg, intel_dmc_wl_get(display, reg); ret = intel_de_wait_for_register(display, reg, mask, value, - timeout_us, 0, + timeout_us, intel_de_read, out_value, false); @@ -109,7 +87,7 @@ int intel_de_wait_ms(struct intel_display *display, intel_reg_t reg, intel_dmc_wl_get(display, reg); ret = intel_de_wait_for_register(display, reg, mask, value, - 2, timeout_ms * 1000, + timeout_ms * 1000, intel_de_read, out_value, false); @@ -123,7 +101,7 @@ int intel_de_wait_fw_ms(struct intel_display *display, intel_reg_t reg, u32 *out_value) { return intel_de_wait_for_register(display, reg, mask, value, - 2, timeout_ms * 1000, + timeout_ms * 1000, intel_de_read_fw, out_value, false); } @@ -133,7 +111,7 @@ int intel_de_wait_fw_us_atomic(struct intel_display *display, intel_reg_t reg, u32 *out_value) { return intel_de_wait_for_register(display, reg, mask, value, - timeout_us, 0, + timeout_us, intel_de_read_fw, out_value, true); } diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 8e269b71f18e..90c05ad08f86 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -60,6 +60,7 @@ #include "intel_bw.h" #include "intel_cdclk.h" #include "intel_clock_gating.h" +#include "intel_cmtg.h" #include "intel_color.h" #include "intel_crt.h" #include "intel_crtc.h" @@ -132,7 +133,6 @@ #include "vlv_dsi_pll.h" #include "vlv_dsi_regs.h" -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state); static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state); static void hsw_set_transconf(const struct intel_crtc_state *crtc_state); static void bdw_set_pipe_misc(struct intel_dsb *dsb, @@ -1504,7 +1504,7 @@ static void ilk_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); ilk_set_pipeconf(crtc_state); } @@ -1635,7 +1635,9 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_cmtg_set_m_n(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); + intel_cmtg_set_timings(crtc_state, MODESET); if (cpu_transcoder != TRANSCODER_EDP) intel_de_write(display, TRANS_MULT(display, cpu_transcoder), @@ -1788,6 +1790,10 @@ static void hsw_crtc_disable(struct intel_atomic_state *state, intel_atomic_get_old_crtc_state(state, crtc); struct intel_crtc *pipe_crtc; + if (crtc->cmtg.enabled && intel_cmtg_is_allowed(old_crtc_state)) { + intel_cmtg_set_clk_select(old_crtc_state); + intel_cmtg_disable(old_crtc_state); + } /* * FIXME collapse everything to one hook. * Need care with mst->ddi interactions. @@ -2048,7 +2054,7 @@ static void i9xx_configure_cpu_transcoder(const struct intel_crtc_state *crtc_st &crtc_state->dp_m2_n2); } - intel_set_transcoder_timings(crtc_state); + intel_set_transcoder_timings(crtc_state, crtc_state->cpu_transcoder); i9xx_set_pipeconf(crtc_state); } @@ -2664,17 +2670,17 @@ transcoder_has_vrr(const struct intel_crtc_state *crtc_state) return HAS_VRR(display) && !transcoder_is_dsi(cpu_transcoder); } -static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state) +void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); enum pipe pipe = crtc->pipe; - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; int vsyncshift = 0; - drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder)); + drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder)); /* We need to be careful not to changed the adjusted mode, for otherwise * the hw state checker will get angry at the mismatch. */ @@ -2703,7 +2709,7 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta */ if (DISPLAY_VER(display) >= 13) { intel_de_write(display, - TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), + TRANS_SET_CONTEXT_LATENCY(display, transcoder), crtc_state->set_context_latency); /* @@ -2718,35 +2724,26 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta if (DISPLAY_VER(display) >= 4 && DISPLAY_VER(display) < 35) intel_de_write(display, - TRANS_VSYNCSHIFT(display, cpu_transcoder), + TRANS_VSYNCSHIFT(display, transcoder), vsyncshift); - intel_de_write(display, TRANS_HTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_HTOTAL(display, transcoder), HACTIVE(adjusted_mode->crtc_hdisplay - 1) | HTOTAL(adjusted_mode->crtc_htotal - 1)); - intel_de_write(display, TRANS_HBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_HBLANK(display, transcoder), HBLANK_START(adjusted_mode->crtc_hblank_start - 1) | HBLANK_END(adjusted_mode->crtc_hblank_end - 1)); - intel_de_write(display, TRANS_HSYNC(display, cpu_transcoder), + intel_de_write(display, TRANS_HSYNC(display, transcoder), HSYNC_START(adjusted_mode->crtc_hsync_start - 1) | HSYNC_END(adjusted_mode->crtc_hsync_end - 1)); - /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not required. Since the support for these bits is going to - * be deprecated in upcoming platforms, avoid writing these bits for the - * platforms that do not use legacy Timing Generator. - */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_vtotal = 1; - - intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_VBLANK(display, transcoder), VBLANK_START(crtc_vblank_start - 1) | VBLANK_END(crtc_vblank_end - 1)); - intel_de_write(display, TRANS_VSYNC(display, cpu_transcoder), + intel_de_write(display, TRANS_VSYNC(display, transcoder), VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); @@ -2754,13 +2751,15 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta * programmed with the VTOTAL_EDP value. Same for VTOTAL_C. This is * documented on the DDI_FUNC_CTL register description, EDP Input Select * bits. */ - if (display->platform.haswell && cpu_transcoder == TRANSCODER_EDP && + if (display->platform.haswell && transcoder == TRANSCODER_EDP && (pipe == PIPE_B || pipe == PIPE_C)) intel_de_write(display, TRANS_VTOTAL(display, pipe), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - if (DISPLAY_VER(display) >= 30) { + if (DISPLAY_VER(display) >= 30 && + transcoder != TRANSCODER_CMTG0 && + transcoder != TRANSCODER_CMTG1) { /* * Address issues for resolutions with high refresh rate that * have small Hblank, specifically where Hblank is smaller than @@ -2769,19 +2768,19 @@ static void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_sta * followed by BE which DPRX devices are unable to handle. * https://groups.vesa.org/wg/DP/document/20494 */ - intel_de_write(display, DP_MIN_HBLANK_CTL(cpu_transcoder), + intel_de_write(display, DP_MIN_HBLANK_CTL(transcoder), crtc_state->min_hblank); } } -static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state) +void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; const struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; u32 crtc_vdisplay, crtc_vtotal, crtc_vblank_start, crtc_vblank_end; - drm_WARN_ON(display->drm, transcoder_is_dsi(cpu_transcoder)); + drm_WARN_ON(display->drm, transcoder_is_dsi(transcoder)); crtc_vdisplay = adjusted_mode->crtc_vdisplay; crtc_vtotal = adjusted_mode->crtc_vtotal; @@ -2796,7 +2795,7 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc if (DISPLAY_VER(display) >= 13) { intel_de_write(display, - TRANS_SET_CONTEXT_LATENCY(display, cpu_transcoder), + TRANS_SET_CONTEXT_LATENCY(display, transcoder), crtc_state->set_context_latency); /* @@ -2813,28 +2812,27 @@ static void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc * The hardware actually ignores TRANS_VBLANK.VBLANK_END in DP mode. * But let's write it anyway to keep the state checker happy. */ - intel_de_write(display, TRANS_VBLANK(display, cpu_transcoder), + intel_de_write(display, TRANS_VBLANK(display, transcoder), VBLANK_START(crtc_vblank_start - 1) | VBLANK_END(crtc_vblank_end - 1)); + /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not required. Since the support for these bits is going to - * be deprecated in upcoming platforms, avoid writing these bits for the - * platforms that do not use legacy Timing Generator. + * DP doesn't have vertical sync, so TRANS_VSYNC only affects + * the position of the vsync interrupt (and does so even when + * using the VRR timing generator!). Thus updating TRANS_VSYNC + * here seems fine even if it isn't double buffered. */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_vtotal = 1; + intel_de_write(display, TRANS_VSYNC(display, transcoder), + VSYNC_START(adjusted_mode->crtc_vsync_start - 1) | + VSYNC_END(adjusted_mode->crtc_vsync_end - 1)); /* * The double buffer latch point for TRANS_VTOTAL * is the transcoder's undelayed vblank. */ - intel_de_write(display, TRANS_VTOTAL(display, cpu_transcoder), + intel_de_write(display, TRANS_VTOTAL(display, transcoder), VACTIVE(crtc_vdisplay - 1) | VTOTAL(crtc_vtotal - 1)); - - intel_vrr_set_fixed_rr_timings(crtc_state); - intel_vrr_transcoder_enable(crtc_state); } static void intel_set_pipe_src_size(const struct intel_crtc_state *crtc_state) @@ -4783,7 +4781,7 @@ intel_modeset_pipe_config(struct intel_atomic_state *state, if (connector_state->crtc != &crtc->base) continue; - ret = encoder->compute_config(encoder, crtc_state, + ret = encoder->compute_config(state, encoder, crtc_state, connector_state); if (ret == -EDEADLK) return ret; @@ -4843,7 +4841,7 @@ intel_modeset_pipe_config_late(struct intel_atomic_state *state, !encoder->compute_config_late) continue; - ret = encoder->compute_config_late(encoder, crtc_state, + ret = encoder->compute_config_late(state, encoder, crtc_state, conn_state); if (ret) return ret; @@ -5189,9 +5187,9 @@ intel_pipe_config_compare(const struct intel_crtc_state *current_config, PIPE_CONF_CHECK_I(name.crtc_vdisplay); \ if (!fastset || !allow_vblank_delay_fastset(current_config)) \ PIPE_CONF_CHECK_I(name.crtc_vblank_start); \ - PIPE_CONF_CHECK_I(name.crtc_vsync_start); \ - PIPE_CONF_CHECK_I(name.crtc_vsync_end); \ if (!fastset || !pipe_config->update_lrr) { \ + PIPE_CONF_CHECK_I(name.crtc_vsync_start); \ + PIPE_CONF_CHECK_I(name.crtc_vsync_end); \ PIPE_CONF_CHECK_I(name.crtc_vtotal); \ PIPE_CONF_CHECK_I(name.crtc_vblank_end); \ } \ @@ -5658,35 +5656,43 @@ int intel_modeset_all_pipes_late(struct intel_atomic_state *state, return 0; } +int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state, + u8 pipe_mask, + struct drm_modeset_acquire_ctx *ctx) +{ + struct intel_display *display = to_intel_display(state); + struct intel_crtc *crtc; + + state->base.acquire_ctx = ctx; + state->internal = true; + + for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) { + struct intel_crtc_state *crtc_state = + intel_atomic_get_crtc_state(&state->base, crtc); + + if (IS_ERR(crtc_state)) + return PTR_ERR(crtc_state); + + crtc_state->uapi.connectors_changed = true; + } + + return drm_atomic_commit(&state->base); +} + int intel_modeset_commit_pipes(struct intel_display *display, u8 pipe_mask, struct drm_modeset_acquire_ctx *ctx) { struct drm_atomic_commit *state; - struct intel_crtc *crtc; int ret; state = drm_atomic_commit_alloc(display->drm); if (!state) return -ENOMEM; - state->acquire_ctx = ctx; - to_intel_atomic_state(state)->internal = true; - - for_each_intel_crtc_in_pipe_mask(display, crtc, pipe_mask) { - struct intel_crtc_state *crtc_state = - intel_atomic_get_crtc_state(state, crtc); - - if (IS_ERR(crtc_state)) { - ret = PTR_ERR(crtc_state); - goto out; - } + ret = intel_modeset_commit_pipes_for_atomic_state(to_intel_atomic_state(state), + pipe_mask, ctx); - crtc_state->uapi.connectors_changed = true; - } - - ret = drm_atomic_commit(state); -out: drm_atomic_commit_put(state); return ret; @@ -5805,6 +5811,8 @@ static bool lrr_params_changed(const struct intel_crtc_state *old_crtc_state, return old_adjusted_mode->crtc_vblank_start != new_adjusted_mode->crtc_vblank_start || old_adjusted_mode->crtc_vblank_end != new_adjusted_mode->crtc_vblank_end || + old_adjusted_mode->crtc_vsync_start != new_adjusted_mode->crtc_vsync_start || + old_adjusted_mode->crtc_vsync_end != new_adjusted_mode->crtc_vsync_end || old_adjusted_mode->crtc_vtotal != new_adjusted_mode->crtc_vtotal || old_crtc_state->set_context_latency != new_crtc_state->set_context_latency; } @@ -6667,12 +6675,19 @@ static void intel_pipe_fastset(const struct intel_crtc_state *old_crtc_state, display->platform.broadwell || display->platform.haswell) hsw_set_linetime_wm(new_crtc_state); - if (new_crtc_state->update_m_n) + if (new_crtc_state->update_m_n) { intel_cpu_transcoder_set_m1_n1(crtc, new_crtc_state->cpu_transcoder, &new_crtc_state->dp_m_n); + intel_cmtg_set_m_n(new_crtc_state); + } - if (new_crtc_state->update_lrr) - intel_set_transcoder_timings_lrr(new_crtc_state); + if (new_crtc_state->update_lrr) { + intel_set_transcoder_timings_lrr(new_crtc_state, new_crtc_state->cpu_transcoder); + intel_cmtg_set_timings(new_crtc_state, LRR); + intel_vrr_set_fixed_rr_timings(new_crtc_state, new_crtc_state->cpu_transcoder); + intel_cmtg_set_vrr_timings(new_crtc_state); + intel_vrr_transcoder_enable(new_crtc_state); + } } static void commit_pipe_pre_planes(struct intel_atomic_state *state, @@ -6870,6 +6885,12 @@ static void intel_update_crtc(struct intel_atomic_state *state, if (intel_crtc_needs_fastset(new_crtc_state) && old_crtc_state->inherited) intel_crtc_arm_fifo_underrun(crtc, new_crtc_state); + + if (crtc->cmtg.enabled && intel_crtc_vrr_enabling(state, crtc) && + intel_cmtg_is_allowed(new_crtc_state)) { + intel_cmtg_set_clk_select(new_crtc_state); + intel_cmtg_disable(new_crtc_state); + } } static void intel_old_crtc_state_disables(struct intel_atomic_state *state, @@ -7428,6 +7449,7 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) struct intel_crtc *crtc; struct intel_power_domain_mask put_domains[I915_MAX_PIPES] = {}; struct ref_tracker *wakeref = NULL; + int power_async_delay; for_each_new_intel_crtc_in_state(state, crtc, new_crtc_state) intel_atomic_dsb_prepare(state, crtc); @@ -7536,9 +7558,14 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) /* Now enable the clocks, plane, pipe, and connectors that we set up. */ display->modeset.funcs->commit_modeset_enables(state); + intel_display_power_dc3co_compute(state); + /* FIXME probably need to sequence this properly */ intel_program_dpkgc_latency(state); + if (intel_display_power_dc3co_allowed(display)) + intel_cmtg_program(state); + intel_wait_for_vblank_workers(state); /* FIXME: We should call drm_atomic_helper_commit_hw_done() here @@ -7632,11 +7659,12 @@ static void intel_atomic_commit_tail(struct intel_atomic_state *state) */ intel_uncore_arm_unclaimed_mmio_detection(uncore); } - /* - * Delay re-enabling DC states by 17 ms to avoid the off->on->off - * toggling overhead at and above 60 FPS. - */ - intel_display_power_put_async_delay(display, POWER_DOMAIN_DC_OFF, wakeref, 17); + + power_async_delay = intel_display_power_select_target_dc_state(state); + + intel_display_power_put_async_delay(display, + POWER_DOMAIN_DC_OFF, wakeref, power_async_delay); + intel_display_rpm_put(display, state->wakeref); /* diff --git a/drivers/gpu/drm/i915/display/intel_display.h b/drivers/gpu/drm/i915/display/intel_display.h index 1963dbc80221..57ea4f2edf2a 100644 --- a/drivers/gpu/drm/i915/display/intel_display.h +++ b/drivers/gpu/drm/i915/display/intel_display.h @@ -424,6 +424,10 @@ void intel_set_m_n(struct intel_display *display, const struct intel_link_m_n *m_n, intel_reg_t data_m_reg, intel_reg_t data_n_reg, intel_reg_t link_m_reg, intel_reg_t link_n_reg); +void intel_set_transcoder_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); +void intel_set_transcoder_timings_lrr(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); void intel_get_m_n(struct intel_display *display, struct intel_link_m_n *m_n, intel_reg_t data_m_reg, intel_reg_t data_n_reg, @@ -474,6 +478,9 @@ int intel_modeset_pipes_in_mask_early(struct intel_atomic_state *state, const char *reason, u8 pipe_mask); int intel_modeset_all_pipes_late(struct intel_atomic_state *state, const char *reason); +int intel_modeset_commit_pipes_for_atomic_state(struct intel_atomic_state *state, + u8 pipe_mask, + struct drm_modeset_acquire_ctx *ctx); int intel_modeset_commit_pipes(struct intel_display *display, u8 pipe_mask, struct drm_modeset_acquire_ctx *ctx); diff --git a/drivers/gpu/drm/i915/display/intel_display_core.h b/drivers/gpu/drm/i915/display/intel_display_core.h index 09ce25a6d4b1..17f7d3abdb9c 100644 --- a/drivers/gpu/drm/i915/display/intel_display_core.h +++ b/drivers/gpu/drm/i915/display/intel_display_core.h @@ -325,14 +325,14 @@ struct intel_display { struct intel_bw_info { /* for each QGV point */ unsigned int deratedbw[I915_NUM_QGV_POINTS]; - /* for each PSF GV point */ - unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; - /* Peak BW for each QGV point */ - unsigned int peakbw[I915_NUM_QGV_POINTS]; - u8 num_qgv_points; - u8 num_psf_gv_points; u8 num_planes; } max[6]; + /* for each PSF GV point */ + unsigned int psf_bw[I915_NUM_PSF_GV_POINTS]; + /* Peak BW for each QGV point */ + unsigned int peakbw[I915_NUM_QGV_POINTS]; + u8 num_qgv_points; + u8 num_psf_gv_points; } bw; struct { @@ -538,6 +538,8 @@ struct intel_display { struct { struct i915_power_domains domains; + /* DC3CO state */ + struct intel_dc3co_state dc3co; /* Shadow for DISPLAY_PHY_CONTROL which can't be safely read */ u32 chv_phy_control; diff --git a/drivers/gpu/drm/i915/display/intel_display_debugfs.c b/drivers/gpu/drm/i915/display/intel_display_debugfs.c index 08004c1ba03f..3f02868ef105 100644 --- a/drivers/gpu/drm/i915/display/intel_display_debugfs.c +++ b/drivers/gpu/drm/i915/display/intel_display_debugfs.c @@ -32,6 +32,7 @@ #include "intel_display_types.h" #include "intel_dmc.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -1342,6 +1343,7 @@ void intel_connector_debugfs_add(struct intel_connector *connector) intel_psr_connector_debugfs_add(connector); intel_alpm_lobf_debugfs_add(connector); intel_dp_link_training_debugfs_add(connector); + intel_dp_link_caps_debugfs_add(connector); intel_link_bw_connector_debugfs_add(connector); if (DISPLAY_VER(display) >= 11 && diff --git a/drivers/gpu/drm/i915/display/intel_display_device.c b/drivers/gpu/drm/i915/display/intel_display_device.c index 69a9f782935c..f17fc2c68472 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.c +++ b/drivers/gpu/drm/i915/display/intel_display_device.c @@ -101,6 +101,8 @@ static const struct intel_display_device_info no_display = {}; #define TRANSCODER_EDP_OFFSET 0x6f000 #define TRANSCODER_DSI0_OFFSET 0x6b000 #define TRANSCODER_DSI1_OFFSET 0x6b800 +#define TRANSCODER_CMTG0_OFFSET 0x6F000 +#define TRANSCODER_CMTG1_OFFSET 0x6F100 #define CURSOR_A_OFFSET 0x70080 #define CURSOR_B_OFFSET 0x700c0 @@ -1352,6 +1354,18 @@ static const struct intel_display_device_info xe2_lpd_display = { BIT(INTEL_FBC_A) | BIT(INTEL_FBC_B) | BIT(INTEL_FBC_C) | BIT(INTEL_FBC_D), .__runtime_defaults.has_dbuf_overlap_detection = true, + .trans_offsets = { + [TRANSCODER_A] = TRANSCODER_A_OFFSET, + [TRANSCODER_B] = TRANSCODER_B_OFFSET, + [TRANSCODER_C] = TRANSCODER_C_OFFSET, + [TRANSCODER_D] = TRANSCODER_D_OFFSET, + [TRANSCODER_CMTG0] = TRANSCODER_CMTG0_OFFSET, + [TRANSCODER_CMTG1] = TRANSCODER_CMTG1_OFFSET, + }, + .__runtime_defaults.cpu_transcoder_mask = + BIT(TRANSCODER_A) | BIT(TRANSCODER_B) | + BIT(TRANSCODER_C) | BIT(TRANSCODER_D) | + BIT(TRANSCODER_CMTG0) | BIT(TRANSCODER_CMTG1), }; static const struct intel_display_device_info wcl_display = { diff --git a/drivers/gpu/drm/i915/display/intel_display_device.h b/drivers/gpu/drm/i915/display/intel_display_device.h index 12e5a522a299..f77b3da2cff5 100644 --- a/drivers/gpu/drm/i915/display/intel_display_device.h +++ b/drivers/gpu/drm/i915/display/intel_display_device.h @@ -159,6 +159,7 @@ struct intel_display_platforms { #define HAS_CUR_FBC(__display) (!HAS_GMCH(__display) && IS_DISPLAY_VER(__display, 7, 13)) #define HAS_D12_PLANE_MINIMIZATION(__display) ((__display)->platform.rocketlake || (__display)->platform.alderlake_s) #define HAS_DBUF_OVERLAP_DETECTION(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dbuf_overlap_detection) +#define HAS_DC3CO(__display) (DISPLAY_VER(__display) >= 35) #define HAS_DDI(__display) (DISPLAY_INFO(__display)->has_ddi) #define HAS_DISPLAY(__display) (DISPLAY_RUNTIME_INFO(__display)->pipe_mask != 0) #define HAS_DMC(__display) (DISPLAY_RUNTIME_INFO(__display)->has_dmc) @@ -292,7 +293,7 @@ struct intel_display_runtime_info { u32 rawclk_freq; u8 pipe_mask; - u8 cpu_transcoder_mask; + u16 cpu_transcoder_mask; u16 port_mask; u8 num_sprites[I915_MAX_PIPES]; diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.c b/drivers/gpu/drm/i915/display/intel_display_driver.c index d0729936f681..bb5301b90231 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.c +++ b/drivers/gpu/drm/i915/display/intel_display_driver.c @@ -43,6 +43,8 @@ #include "intel_dp_tunnel.h" #include "intel_dpll.h" #include "intel_dpll_mgr.h" +#include "intel_dram.h" +#include "intel_encoder.h" #include "intel_fb.h" #include "intel_fbc.h" #include "intel_fbdev.h" @@ -65,6 +67,8 @@ #include "intel_wm.h" #include "skl_watermark.h" +static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown); + bool intel_display_driver_probe_defer(struct pci_dev *pdev) { struct drm_privacy_screen *privacy_screen; @@ -200,11 +204,23 @@ int intel_display_driver_probe_noirq(struct intel_display *display) { int ret; + intel_opregion_setup(display); + + /* + * Fill the dram structure to get the system dram info. This will be + * used for memory latency calculation. + */ + ret = intel_dram_detect(display); + if (ret) + goto cleanup_opregion; + + intel_bw_init_hw(display); + if (HAS_DISPLAY(display)) { ret = drm_vblank_init(display->drm, INTEL_NUM_PIPES(display)); if (ret) - return ret; + goto cleanup_opregion; } intel_bios_init(display); @@ -303,6 +319,8 @@ cleanup_pw_domain_dmc: intel_display_power_driver_remove(display); cleanup_bios: intel_bios_driver_remove(display); +cleanup_opregion: + intel_opregion_cleanup(display); return ret; } @@ -604,6 +622,8 @@ void intel_display_driver_remove_noirq(struct intel_display *display) if (!HAS_DISPLAY(display)) return; + intel_hpd_cancel_work(display); + intel_display_driver_suspend_access(display); /* @@ -644,6 +664,8 @@ void intel_display_driver_remove_nogem(struct intel_display *display) intel_display_power_driver_remove(display); intel_bios_driver_remove(display); + + intel_opregion_cleanup(display); } void intel_display_driver_unregister(struct intel_display *display) @@ -674,34 +696,102 @@ void intel_display_driver_unregister(struct intel_display *display) intel_vga_unregister(display); } +void intel_display_driver_shutdown(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + __intel_display_driver_pm_suspend(display, true); + + intel_encoder_shutdown_all(display); +} + +void intel_display_driver_shutdown_late(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + /* + * The only requirement is to reboot with display DC states disabled, + * for now leaving all display power wells in the INIT power domain + * enabled. + */ + intel_display_power_driver_remove(display); +} + /* * turn all crtc's off, but do not adjust state * This has to be paired with a call to intel_modeset_setup_hw_state. */ -int intel_display_driver_suspend(struct intel_display *display) +static int __intel_display_driver_pm_suspend(struct intel_display *display, bool shutdown) { - struct drm_atomic_commit *state; - int ret; + int ret = 0; if (!HAS_DISPLAY(display)) return 0; - state = drm_atomic_helper_suspend(display->drm); - ret = PTR_ERR_OR_ZERO(state); - if (ret) - drm_err(display->drm, "Suspending crtc's failed with %i\n", - ret); - else - display->restore.modeset_state = state; + /* + * We do a lot of poking in a lot of registers, make sure they work + * properly. + */ + intel_display_power_disable(display); + + drm_client_dev_suspend(display->drm); + + drm_kms_helper_poll_disable(display->drm); + intel_display_driver_disable_user_access(display); + + if (shutdown) { + drm_atomic_helper_shutdown(display->drm); + } else { + struct drm_atomic_commit *state; + + state = drm_atomic_helper_suspend(display->drm); + ret = PTR_ERR_OR_ZERO(state); + if (ret) + drm_err(display->drm, "Suspending crtc's failed with %i\n", + ret); + else + display->restore.modeset_state = state; + } /* ensure all DPT VMAs have been unpinned for intel_dpt_suspend() */ flush_workqueue(display->wq.cleanup); intel_dp_mst_suspend(display); + intel_encoder_block_all_hpds(display); + + intel_hpd_cancel_work(display); + + intel_display_driver_suspend_access(display); + + intel_encoder_suspend_all(display); + return ret; } +int intel_display_driver_pm_suspend(struct intel_display *display) +{ + return __intel_display_driver_pm_suspend(display, false); +} + +void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle) +{ + if (!HAS_DISPLAY(display)) + return; + + intel_display_power_suspend_late(display, s2idle); +} + +void intel_display_driver_pm_resume_early(struct intel_display *display) +{ + if (!HAS_DISPLAY(display)) + return; + + intel_display_power_resume_early(display); +} + int __intel_display_driver_resume(struct intel_display *display, struct drm_atomic_commit *state, @@ -741,7 +831,7 @@ __intel_display_driver_resume(struct intel_display *display, return ret; } -void intel_display_driver_resume(struct intel_display *display) +void intel_display_driver_pm_resume(struct intel_display *display) { struct drm_atomic_commit *state = display->restore.modeset_state; struct drm_modeset_acquire_ctx ctx; @@ -750,6 +840,12 @@ void intel_display_driver_resume(struct intel_display *display) if (!HAS_DISPLAY(display)) return; + intel_display_driver_resume_access(display); + + intel_hpd_init(display); + + intel_encoder_unblock_all_hpds(display); + /* MST sideband requires HPD interrupts enabled */ intel_dp_mst_resume(display); @@ -779,4 +875,15 @@ void intel_display_driver_resume(struct intel_display *display) "Restoring old state failed with %i\n", ret); if (state) drm_atomic_commit_put(state); + + intel_display_driver_enable_user_access(display); + drm_kms_helper_poll_enable(display->drm); + + intel_hpd_poll_disable(display); + + intel_opregion_resume(display); + + drm_client_dev_resume(display->drm); + + intel_display_power_enable(display); } diff --git a/drivers/gpu/drm/i915/display/intel_display_driver.h b/drivers/gpu/drm/i915/display/intel_display_driver.h index 5270c26a32e0..7eca3d17dd82 100644 --- a/drivers/gpu/drm/i915/display/intel_display_driver.h +++ b/drivers/gpu/drm/i915/display/intel_display_driver.h @@ -24,8 +24,13 @@ void intel_display_driver_remove(struct intel_display *display); void intel_display_driver_remove_noirq(struct intel_display *display); void intel_display_driver_remove_nogem(struct intel_display *display); void intel_display_driver_unregister(struct intel_display *display); -int intel_display_driver_suspend(struct intel_display *display); -void intel_display_driver_resume(struct intel_display *display); +void intel_display_driver_shutdown(struct intel_display *display); +void intel_display_driver_shutdown_late(struct intel_display *display); + +int intel_display_driver_pm_suspend(struct intel_display *display); +void intel_display_driver_pm_suspend_late(struct intel_display *display, bool s2idle); +void intel_display_driver_pm_resume_early(struct intel_display *display); +void intel_display_driver_pm_resume(struct intel_display *display); /* interface for intel_display_reset.c */ int __intel_display_driver_resume(struct intel_display *display, diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.c b/drivers/gpu/drm/i915/display/intel_display_irq.c index 4a821b0674fd..bcb0ee22fb56 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.c +++ b/drivers/gpu/drm/i915/display/intel_display_irq.c @@ -1469,6 +1469,18 @@ static void gen8_de_irq_handler(struct intel_display *display, u32 master_ctl) found = true; } + if (DISPLAY_VER(display) == 35) { + if (iir & CMTG_VBLANK_A) { + intel_handle_vblank(display, PIPE_A); + found = true; + } + + if (iir & CMTG_VBLANK_B) { + intel_handle_vblank(display, PIPE_B); + found = true; + } + } + if (DISPLAY_VER(display) >= 11) { u32 te_trigger = iir & (DSI0_TE | DSI1_TE); @@ -2666,3 +2678,10 @@ void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *s drm_printf(p, "DERRMR: 0x%08x\n", snapshot->derrmr); drm_printf(p, "ERR_INT: 0x%08x\n", snapshot->err_int); } + +void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask) +{ + spin_lock_irq(&display->irq.lock); + bdw_update_port_irq(display, bits, mask ? 0 : bits); + spin_unlock_irq(&display->irq.lock); +} diff --git a/drivers/gpu/drm/i915/display/intel_display_irq.h b/drivers/gpu/drm/i915/display/intel_display_irq.h index a1227cee885a..84446bf53401 100644 --- a/drivers/gpu/drm/i915/display/intel_display_irq.h +++ b/drivers/gpu/drm/i915/display/intel_display_irq.h @@ -82,4 +82,6 @@ void i915gm_irq_cstate_wa(struct intel_display *display, bool enable); struct intel_display_irq_snapshot *intel_display_irq_snapshot_capture(struct intel_display *display); void intel_display_irq_snapshot_print(const struct intel_display_irq_snapshot *snapshot, struct drm_printer *p); +void intel_display_irq_port_interrupt_mask(struct intel_display *display, u32 bits, bool mask); + #endif /* __INTEL_DISPLAY_IRQ_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_display_limits.h b/drivers/gpu/drm/i915/display/intel_display_limits.h index 453f7b720815..ea89473c177f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_limits.h +++ b/drivers/gpu/drm/i915/display/intel_display_limits.h @@ -45,6 +45,8 @@ enum transcoder { TRANSCODER_DSI_1, TRANSCODER_DSI_A = TRANSCODER_DSI_0, /* legacy DSI */ TRANSCODER_DSI_C = TRANSCODER_DSI_1, /* legacy DSI */ + TRANSCODER_CMTG0, + TRANSCODER_CMTG1, I915_MAX_TRANSCODERS }; diff --git a/drivers/gpu/drm/i915/display/intel_display_power.c b/drivers/gpu/drm/i915/display/intel_display_power.c index 2e51dfcd5dce..8e312f5b9f6d 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.c +++ b/drivers/gpu/drm/i915/display/intel_display_power.c @@ -14,7 +14,9 @@ #include "intel_cdclk.h" #include "intel_clock_gating.h" #include "intel_combo_phy.h" +#include "intel_crtc.h" #include "intel_de.h" +#include "intel_display.h" #include "intel_display_power.h" #include "intel_display_power_map.h" #include "intel_display_power_well.h" @@ -30,6 +32,8 @@ #include "intel_pch_refclk.h" #include "intel_pmdemand.h" #include "intel_pps_regs.h" +#include "intel_psr.h" +#include "intel_psr_regs.h" #include "intel_snps_phy.h" #include "skl_watermark.h" #include "skl_watermark_regs.h" @@ -267,7 +271,7 @@ sanitize_target_dc_state(struct intel_display *display, static const u32 states[] = { DC_STATE_EN_UPTO_DC6, DC_STATE_EN_UPTO_DC5, - DC_STATE_EN_DC3CO, + DC_STATE_EN_UPTO_DC3CO, DC_STATE_DISABLE, }; int i; @@ -285,6 +289,19 @@ sanitize_target_dc_state(struct intel_display *display, return target_dc_state; } +bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display) +{ + struct i915_power_domains *power_domains = &display->power.domains; + bool ret; + + mutex_lock(&power_domains->lock); + ret = power_domains->dc3co_to_dc6; + power_domains->dc3co_to_dc6 = false; + mutex_unlock(&power_domains->lock); + + return ret; +} + /** * intel_display_power_set_target_dc_state - Set target dc state. * @display: display device @@ -300,6 +317,7 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, struct i915_power_well *power_well; bool dc_off_enabled; struct i915_power_domains *power_domains = &display->power.domains; + u32 old_target_dc_state; mutex_lock(&power_domains->lock); power_well = lookup_power_well(display, SKL_DISP_DC_OFF); @@ -320,8 +338,17 @@ void intel_display_power_set_target_dc_state(struct intel_display *display, if (!dc_off_enabled) intel_power_well_enable(display, power_well); + old_target_dc_state = power_domains->target_dc_state; power_domains->target_dc_state = state; + /* + * CMTG must be restored explicitly after DC6 exit. The dc3co_to_dc6 + * flag helps CMTG determine whether restoration is required. + */ + if (old_target_dc_state == DC_STATE_EN_UPTO_DC3CO && + power_domains->target_dc_state == DC_STATE_EN_UPTO_DC6) + power_domains->dc3co_to_dc6 = true; + if (!dc_off_enabled) intel_power_well_disable(display, power_well); @@ -358,6 +385,144 @@ unlock: return current_dc_state; } +bool intel_display_power_dc3co_supported(struct intel_display *display) +{ + struct i915_power_domains *power_domains = &display->power.domains; + + if (!HAS_DC3CO(display)) + return false; + + return (power_domains->allowed_dc_mask & DC_STATE_EN_UPTO_DC3CO) == DC_STATE_EN_UPTO_DC3CO; +} + +bool intel_display_power_dc3co_allowed(struct intel_display *display) +{ + struct intel_dc3co_state *dc3co = &display->power.dc3co; + bool allowed; + + if (!intel_display_power_dc3co_supported(display)) + return false; + + mutex_lock(&dc3co->lock); + allowed = dc3co->allowed; + mutex_unlock(&dc3co->lock); + + return allowed; +} + +void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger) +{ + struct intel_dc3co_state *dc3co = &display->power.dc3co; + + if (!intel_display_power_dc3co_supported(display)) + return; + + mutex_lock(&dc3co->lock); + dc3co->trigger = trigger; + dc3co->allowed = !!trigger; + mutex_unlock(&dc3co->lock); +} + +static bool intel_dc3co_port_pipe_compatible(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; + enum port port = dig_port->base.port; + int num_pipes = intel_crtc_num_joined_pipes(crtc_state); + + /* Need to follow 1:1 mapping because of CMTG restriction */ + if (DISPLAY_VER(to_intel_display(crtc_state)) == 35) + return num_pipes == 1 && + ((pipe == PIPE_A && port == PORT_A) || + (pipe == PIPE_B && port == PORT_B)); + else + return num_pipes == 1 && pipe <= PIPE_B && port <= PORT_B; +} + +void intel_display_power_dc3co_compute(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + struct intel_crtc *crtc; + struct intel_crtc_state *crtc_state; + struct intel_encoder *encoder; + struct intel_dp *intel_dp; + u8 active_pipes = 0; + enum pipe pipe; + u32 trigger = DC3CO_TRIGGER_NONE; + + if (!intel_display_power_dc3co_supported(display)) + return; + + for_each_intel_crtc(display, crtc) + active_pipes |= crtc->active ? BIT(crtc->pipe) : 0; + + active_pipes = intel_calc_active_pipes(state, active_pipes); + + if (hweight8(active_pipes) != 1) + goto done; + + pipe = ffs(active_pipes) - 1; + crtc = intel_crtc_for_pipe(display, pipe); + + crtc_state = to_intel_crtc_state(crtc->base.state); + + for_each_intel_encoder_mask(display->drm, encoder, + crtc_state->uapi.encoder_mask) { + if (encoder->type != INTEL_OUTPUT_EDP) + goto done; + + intel_dp = enc_to_intel_dp(encoder); + + if (!intel_dc3co_port_pipe_compatible(intel_dp, crtc_state)) + goto done; + + if (intel_psr2_in_deep_sleep(intel_dp)) + goto done; + } + + if (crtc_state->has_lobf) + trigger |= DC3CO_TRIGGER_LOBF; + if (crtc_state->has_panel_replay && intel_dp->as_sdp_supported) + trigger |= DC3CO_TRIGGER_PANEL_REPLAY; + if (crtc_state->has_sel_update) + trigger |= DC3CO_TRIGGER_PSR2; + +done: + intel_display_power_dc3co_update(display, trigger); +} + +/* + * Select the target DC state for this commit and return the async-put delay + * to use when releasing the DC_OFF reference. + * + * Picks DC_STATE_EN_UPTO_DC3CO when DC3CO can be enabled + * otherwise falls back to default DC state of DC_STATE_EN_UPTO_DC6. + * The chosen target is programmed via intel_display_power_set_target_dc_state(). + * + * Returns the async-put delay (in ms) to use when releasing the DC_OFF + * reference: DC3CO_PUT_ASYNC_DELAY_MS when DC3CO was selected, otherwise + * DC6_PUT_ASYNC_DELAY_MS. + */ +int intel_display_power_select_target_dc_state(struct intel_atomic_state *state) +{ + struct intel_display *display = to_intel_display(state); + u32 target_dc_state; + + if (!intel_display_power_dc3co_supported(display)) + return DC6_PUT_ASYNC_DELAY_MS; + + if (intel_display_power_dc3co_allowed(display)) + target_dc_state = DC_STATE_EN_UPTO_DC3CO; + else + target_dc_state = DC_STATE_EN_UPTO_DC6; + + intel_display_power_set_target_dc_state(display, target_dc_state); + + return target_dc_state == DC_STATE_EN_UPTO_DC3CO ? + DC3CO_PUT_ASYNC_DELAY_MS : DC6_PUT_ASYNC_DELAY_MS; +} + static void __async_put_domains_mask(struct i915_power_domains *power_domains, struct intel_power_domain_mask *mask) { @@ -956,7 +1121,9 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc) if (!HAS_DISPLAY(display)) return 0; - if (DISPLAY_VER(display) >= 20) + if (DISPLAY_VER(display) >= 35) + max_dc = 4; + else if (DISPLAY_VER(display) >= 20) max_dc = 2; else if (display->platform.dg2) max_dc = 1; @@ -999,10 +1166,10 @@ static u32 get_allowed_dc_mask(struct intel_display *display, int enable_dc) switch (requested_dc) { case 4: - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6; + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6; break; case 3: - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC5; + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC5; break; case 2: mask |= DC_STATE_EN_UPTO_DC6; @@ -1037,6 +1204,7 @@ int intel_display_power_init(struct intel_display *display) sanitize_target_dc_state(display, DC_STATE_EN_UPTO_DC6); mutex_init(&power_domains->lock); + mutex_init(&display->power.dc3co.lock); INIT_DELAYED_WORK(&power_domains->async_put_work, intel_display_power_put_async_work); @@ -1634,9 +1802,12 @@ static void tgl_bw_buddy_init(struct intel_display *display) if (table[config].page_mask == 0) { drm_dbg_kms(display->drm, "Unknown memory configuration; disabling address buddy logic.\n"); - for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) - intel_de_write(display, BW_BUDDY_CTL(i), - BW_BUDDY_DISABLE); + + if (DISPLAY_VER(display) < 20) { + for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) + intel_de_write(display, BW_BUDDY_CTL(i), + BW_BUDDY_DISABLE); + } } else { for_each_set_bit(i, &abox_mask, BITS_PER_TYPE(abox_mask)) { intel_de_write(display, BW_BUDDY_PAGE_MASK(i), diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index 56dc89eed3f8..546af67b680b 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -9,9 +9,12 @@ #include <linux/mutex.h> #include <linux/workqueue.h> +#include "intel_display_limits.h" + enum aux_ch; enum port; struct i915_power_well; +struct intel_atomic_state; struct intel_display; struct intel_encoder; struct ref_tracker; @@ -131,6 +134,36 @@ struct intel_power_domain_mask { DECLARE_BITMAP(bits, POWER_DOMAIN_NUM); }; +/* + * DC3CO enabling triggers (bitmask). + * DC3CO may be enabled when at least one of these triggers is active. + * Additional constraints may still apply. + */ +#define DC3CO_TRIGGER_NONE (0) +#define DC3CO_TRIGGER_PSR2 BIT(0) +#define DC3CO_TRIGGER_LOBF BIT(1) +#define DC3CO_TRIGGER_PANEL_REPLAY BIT(2) +#define DC3CO_TRIGGER_ALL (DC3CO_TRIGGER_PSR2 | \ + DC3CO_TRIGGER_LOBF | \ + DC3CO_TRIGGER_PANEL_REPLAY) + +/* + * Delay to re-enable DC5/DC6 states by 17 ms to avoid the off->on->off + * toggling overhead at and above 60 FPS. + */ +#define DC6_PUT_ASYNC_DELAY_MS 17 +/* + * Use minimal re-enable delay to allow DC3CO entry on + * the next idle frame. + */ +#define DC3CO_PUT_ASYNC_DELAY_MS 1 + +struct intel_dc3co_state { + struct mutex lock; /* protects allowed and trigger fields */ + bool allowed; /* DC3CO compute result */ + u32 trigger; /* Bitmask of active DC3CO triggers */ +}; + struct i915_power_domains { /* * Power wells needed for initialization at driver init and suspend @@ -138,6 +171,7 @@ struct i915_power_domains { */ bool initializing; bool display_core_suspended; + bool dc3co_to_dc6; int power_well_count; u32 dc_state; @@ -179,9 +213,15 @@ void intel_display_power_sanitize_state(struct intel_display *display); void intel_display_power_suspend_late(struct intel_display *display, bool s2idle); void intel_display_power_resume_early(struct intel_display *display); +bool intel_display_power_get_and_reset_dc3co_to_dc6(struct intel_display *display); void intel_display_power_set_target_dc_state(struct intel_display *display, u32 state); u32 intel_display_power_get_current_dc_state(struct intel_display *display); +bool intel_display_power_dc3co_supported(struct intel_display *display); +void intel_display_power_dc3co_update(struct intel_display *display, u32 trigger); +bool intel_display_power_dc3co_allowed(struct intel_display *display); +void intel_display_power_dc3co_compute(struct intel_atomic_state *state); +int intel_display_power_select_target_dc_state(struct intel_atomic_state *state); void intel_display_power_runtime_suspend(struct intel_display *display); void intel_display_power_runtime_resume(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.c b/drivers/gpu/drm/i915/display/intel_display_power_well.c index 04bd0dde5bed..02cb4d800e23 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.c @@ -726,12 +726,28 @@ static void assert_can_disable_dc9(struct intel_display *display) */ } +static u32 dc_state_ro_mask(struct intel_display *display) +{ + if (DISPLAY_VER(display) >= 20) + return DC_STATE_EN_CSR_MASK_CMTG_1 | DC_STATE_EN_CSR_MASK_CMTG_0; + else if (DISPLAY_VER(display) >= 13 && !display->platform.dg2) + return DC_STATE_EN_CSR_MASK_CMTG_0; + + return 0; +} + static void gen9_write_dc_state(struct intel_display *display, u32 state) { int rewrites = 0; int rereads = 0; u32 v; + /* + * Mask out RO status bits from read-back comparison. + * HW may set these bits independently, so exclude them + * to prevent the verify loop from retrying due to RO bits mismatch. + */ + u32 ro_mask = dc_state_ro_mask(display); intel_de_write(display, DC_STATE_EN, state); @@ -743,7 +759,7 @@ static void gen9_write_dc_state(struct intel_display *display, do { v = intel_de_read(display, DC_STATE_EN); - if (v != state) { + if ((v & ~ro_mask) != (state & ~ro_mask)) { intel_de_write(display, DC_STATE_EN, state); rewrites++; rereads = 0; @@ -753,10 +769,10 @@ static void gen9_write_dc_state(struct intel_display *display, } while (rewrites < 100); - if (v != state) + if ((v & ~ro_mask) != (state & ~ro_mask)) drm_err(display->drm, - "Writing dc state to 0x%x failed, now 0x%x\n", - state, v); + "Writing dc state to 0x%x failed, now 0x%x (ro_mask=0x%x)\n", + state, v, ro_mask); /* Most of the times we need one retry, avoid spam */ if (rewrites > 1) @@ -772,7 +788,7 @@ static u32 gen9_dc_mask(struct intel_display *display) mask = DC_STATE_EN_UPTO_DC5; if (DISPLAY_VER(display) >= 12) - mask |= DC_STATE_EN_DC3CO | DC_STATE_EN_UPTO_DC6 + mask |= DC_STATE_EN_UPTO_DC3CO | DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; else if (DISPLAY_VER(display) == 11) mask |= DC_STATE_EN_UPTO_DC6 | DC_STATE_EN_DC9; @@ -866,21 +882,30 @@ void gen9_set_dc_state(struct intel_display *display, u32 state) power_domains->dc_state = val & mask; } -static void tgl_enable_dc3co(struct intel_display *display) +void xe3lpd_enable_dc_count(struct intel_display *display) { - drm_dbg_kms(display->drm, "Enabling DC3CO\n"); - gen9_set_dc_state(display, DC_STATE_EN_DC3CO); + if (DISPLAY_VER(display) < 35) + return; + + intel_de_write(display, DC_COUNT_EN, DC_COUNT_EN_COUNTER_ENABLE); } -static void tgl_disable_dc3co(struct intel_display *display) +static void assert_can_enable_dc3co(struct intel_display *display) { - drm_dbg_kms(display->drm, "Disabling DC3CO\n"); - intel_de_rmw(display, DC_STATE_EN, DC_STATE_DC3CO_STATUS, 0); - gen9_set_dc_state(display, DC_STATE_DISABLE); - /* - * Delay of 200us DC3CO Exit time B.Spec 49196 - */ - usleep_range(200, 210); + drm_WARN_ONCE(display->drm, + (intel_de_read(display, DC_STATE_EN) & + DC_STATE_EN_UPTO_DC3CO), + "DC3CO already programmed to be enabled.\n"); + + assert_main_dmc_loaded(display); +} + +static void xe3lpd_enable_dc3co(struct intel_display *display) +{ + assert_can_enable_dc3co(display); + drm_dbg_kms(display->drm, "Enabling DC3CO\n"); + intel_dmc_wl_enable(display, DC_STATE_EN_UPTO_DC3CO); + gen9_set_dc_state(display, DC_STATE_EN_UPTO_DC3CO); } static void assert_can_enable_dc5(struct intel_display *display) @@ -1039,8 +1064,8 @@ static void bxt_verify_dpio_phy_power_wells(struct intel_display *display) static bool gen9_dc_off_power_well_enabled(struct intel_display *display, struct i915_power_well *power_well) { - return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_DC3CO) == 0 && - (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC5_DC6_MASK) == 0); + return ((intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO) == 0 && + (intel_de_read(display, DC_STATE_EN) & DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK) == 0); } static void gen9_assert_dbuf_enabled(struct intel_display *display) @@ -1061,11 +1086,6 @@ void gen9_disable_dc_states(struct intel_display *display) struct intel_cdclk_config cdclk_config = {}; u32 old_state = power_domains->dc_state; - if (power_domains->target_dc_state == DC_STATE_EN_DC3CO) { - tgl_disable_dc3co(display); - return; - } - if (HAS_DISPLAY(display)) { intel_dmc_wl_get_noreg(display); gen9_set_dc_state(display, DC_STATE_DISABLE); @@ -1076,9 +1096,13 @@ void gen9_disable_dc_states(struct intel_display *display) } if (old_state == DC_STATE_EN_UPTO_DC5 || - old_state == DC_STATE_EN_UPTO_DC6) + old_state == DC_STATE_EN_UPTO_DC6 || + old_state == DC_STATE_EN_UPTO_DC3CO) intel_dmc_wl_disable(display); + if (old_state == DC_STATE_EN_UPTO_DC3CO) + return; + intel_cdclk_get_cdclk(display, &cdclk_config); /* Can't read out voltage_level so can't use intel_cdclk_changed() */ drm_WARN_ON(display->drm, @@ -1114,8 +1138,8 @@ static void gen9_dc_off_power_well_disable(struct intel_display *display, return; switch (power_domains->target_dc_state) { - case DC_STATE_EN_DC3CO: - tgl_enable_dc3co(display); + case DC_STATE_EN_UPTO_DC3CO: + xe3lpd_enable_dc3co(display); break; case DC_STATE_EN_UPTO_DC6: skl_enable_dc6(display); diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.h b/drivers/gpu/drm/i915/display/intel_display_power_well.h index 8f5524da2d06..0ce64b894436 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.h +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.h @@ -159,6 +159,7 @@ void gen9_set_dc_state(struct intel_display *display, u32 state); void gen9_disable_dc_states(struct intel_display *display); void bxt_enable_dc9(struct intel_display *display); void bxt_disable_dc9(struct intel_display *display); +void xe3lpd_enable_dc_count(struct intel_display *display); extern const struct i915_power_well_ops i9xx_always_on_power_well_ops; extern const struct i915_power_well_ops chv_pipe_power_well_ops; diff --git a/drivers/gpu/drm/i915/display/intel_display_regs.h b/drivers/gpu/drm/i915/display/intel_display_regs.h index 4321f8b529da..39e50423132f 100644 --- a/drivers/gpu/drm/i915/display/intel_display_regs.h +++ b/drivers/gpu/drm/i915/display/intel_display_regs.h @@ -1458,6 +1458,8 @@ #define GEN9_AUX_CHANNEL_B (1 << 25) #define DSI1_TE (1 << 24) #define DSI0_TE (1 << 23) +#define CMTG_VBLANK_B (1 << 17) +#define CMTG_VBLANK_A (1 << 14) #define GEN8_DE_PORT_HOTPLUG(hpd_pin) REG_BIT(3 + _HPD_PIN_DDI(hpd_pin)) #define BXT_DE_PORT_HOTPLUG_MASK (GEN8_DE_PORT_HOTPLUG(HPD_PORT_A) | \ GEN8_DE_PORT_HOTPLUG(HPD_PORT_B) | \ @@ -2767,28 +2769,28 @@ enum skl_power_gate { */ /* CDCLK_CTL */ #define CDCLK_CTL _MMIO(0x46000) -#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) +#define CDCLK_FREQ_SEL_MASK REG_GENMASK(27, 26) /* skl */ #define CDCLK_FREQ_450_432 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 0) -#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1) +#define CDCLK_FREQ_540 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 1) #define CDCLK_FREQ_337_308 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 2) #define CDCLK_FREQ_675_617 REG_FIELD_PREP(CDCLK_FREQ_SEL_MASK, 3) -#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) +#define MDCLK_SOURCE_SEL_MASK REG_GENMASK(25, 25) /* lnl+ */ #define MDCLK_SOURCE_SEL_CD2XCLK REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 0) #define MDCLK_SOURCE_SEL_CDCLK_PLL REG_FIELD_PREP(MDCLK_SOURCE_SEL_MASK, 1) -#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) +#define BXT_CDCLK_CD2X_DIV_SEL_MASK REG_GENMASK(23, 22) /* bxt+ */ #define BXT_CDCLK_CD2X_DIV_SEL_1 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 0) #define BXT_CDCLK_CD2X_DIV_SEL_1_5 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 1) #define BXT_CDCLK_CD2X_DIV_SEL_2 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 2) #define BXT_CDCLK_CD2X_DIV_SEL_4 REG_FIELD_PREP(BXT_CDCLK_CD2X_DIV_SEL_MASK, 3) -#define BXT_CDCLK_CD2X_PIPE(pipe) ((pipe) << 20) -#define CDCLK_DIVMUX_CD_OVERRIDE (1 << 19) -#define BXT_CDCLK_CD2X_PIPE_NONE BXT_CDCLK_CD2X_PIPE(3) -#define ICL_CDCLK_CD2X_PIPE(pipe) (_PICK(pipe, 0, 2, 6) << 19) -#define ICL_CDCLK_CD2X_PIPE_NONE (7 << 19) -#define TGL_CDCLK_CD2X_PIPE(pipe) BXT_CDCLK_CD2X_PIPE(pipe) -#define TGL_CDCLK_CD2X_PIPE_NONE ICL_CDCLK_CD2X_PIPE_NONE -#define BXT_CDCLK_SSA_PRECHARGE_ENABLE (1 << 16) -#define CDCLK_FREQ_DECIMAL_MASK (0x7ff) +#define BXT_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 20) /* bxt/glk */ +#define BXT_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, (pipe)) +#define BXT_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(BXT_CDCLK_CD2X_PIPE_MASK, 3) +#define ICL_CDCLK_CD2X_PIPE_MASK REG_GENMASK(21, 19) /* icl+ */ +#define ICL_CDCLK_CD2X_PIPE(pipe) REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, (pipe) << 1) +#define ICL_CDCLK_CD2X_PIPE_NONE REG_FIELD_PREP(ICL_CDCLK_CD2X_PIPE_MASK, 7) +#define CDCLK_DIVMUX_CD_OVERRIDE REG_BIT(19) /* pre-icl */ +#define BXT_CDCLK_SSA_PRECHARGE_ENABLE REG_BIT(16) /* bxt/glk */ +#define CDCLK_FREQ_DECIMAL_MASK REG_GENMASK(10, 0) /* pre-lnl */ /* CDCLK_SQUASH_CTL */ #define CDCLK_SQUASH_CTL _MMIO(0x46008) @@ -3070,19 +3072,29 @@ enum skl_power_gate { /* GEN9 DC */ #define DC_STATE_EN _MMIO(0x45504) #define DC_STATE_DISABLE 0 -#define DC_STATE_EN_DC3CO REG_BIT(30) #define DC_STATE_DC3CO_STATUS REG_BIT(29) #define HOLD_PHY_CLKREQ_PG1_LATCH REG_BIT(21) #define HOLD_PHY_PG1_LATCH REG_BIT(20) -#define DC_STATE_EN_UPTO_DC5 (1 << 0) #define DC_STATE_EN_DC9 (1 << 3) -#define DC_STATE_EN_UPTO_DC6 (2 << 0) -#define DC_STATE_EN_UPTO_DC5_DC6_MASK 0x3 +#define DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK REG_GENMASK(1, 0) +#define DC_STATE_EN_DISABLE REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 0) +#define DC_STATE_EN_UPTO_DC5 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 1) +#define DC_STATE_EN_UPTO_DC6 REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 2) +#define DC_STATE_EN_UPTO_DC3CO REG_FIELD_PREP(DC_STATE_EN_UPTO_DC3CO_DC5_DC6_MASK, 3) +/* display version 20+ */ +#define DC_STATE_EN_CSR_MASK_CMTG_1 REG_BIT(11) +/* display version 13+, except dg2 */ +#define DC_STATE_EN_CSR_MASK_CMTG_0 REG_BIT(10) #define DC_STATE_DEBUG _MMIO(0x45520) #define DC_STATE_DEBUG_MASK_CORES (1 << 0) #define DC_STATE_DEBUG_MASK_MEMORY_UP (1 << 1) +#define DC_COUNT_EN _MMIO(0x457B4) +#define DC_COUNT_EN_COUNTER_ENABLE REG_BIT(31) + +#define DC_STATE_DC3CO_RESIDENCY _MMIO(0x457B8) + #define D_COMP_BDW _MMIO(0x138144) /* Pipe WM_LINETIME - watermark line time */ diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index c21e0c0ef0b1..c048da7d6fea 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -58,6 +58,8 @@ struct cec_notifier; struct drm_printer; struct intel_connector; struct intel_ddi_buf_trans; +struct intel_dp_link_caps; +struct intel_dp_link_training; struct intel_fbc; struct intel_global_objs_state; struct intel_hdcp_shim; @@ -150,7 +152,6 @@ struct intel_framebuffer { unsigned int min_alignment; unsigned int vtd_guard; - unsigned int (*panic_tiling)(unsigned int x, unsigned int y, unsigned int width); struct intel_panic *panic; }; @@ -177,10 +178,12 @@ struct intel_encoder { enum intel_output_type (*compute_output_type)(struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); - int (*compute_config)(struct intel_encoder *, + int (*compute_config)(struct intel_atomic_state *, + struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); - int (*compute_config_late)(struct intel_encoder *, + int (*compute_config_late)(struct intel_atomic_state *, + struct intel_encoder *, struct intel_crtc_state *, struct drm_connector_state *); void (*pre_pll_enable)(struct intel_atomic_state *, @@ -1185,7 +1188,6 @@ struct intel_crtc_state { bool pkg_c_latency_used; /* Only used for state verification. */ enum intel_panel_replay_dsc_support panel_replay_dsc_support; - u32 dc3co_exitline; u16 su_y_granularity; u8 active_non_psr_pipes; u8 entry_setup_frames; @@ -1573,6 +1575,10 @@ struct intel_crtc { #endif bool vblank_psr_notify; + + struct { + bool enabled; + } cmtg; }; struct intel_plane_error { @@ -1772,14 +1778,16 @@ struct intel_psr { ktime_t last_exit; bool sink_not_reliable; bool irq_aux_error; + /* DC3CO allowed used to control PSR configuration */ + bool dc3co_allowed; + /* DC3CO disable work */ + struct delayed_work dc3co_work; u16 su_w_granularity; u16 su_y_granularity; bool source_panel_replay_support; bool sink_panel_replay_support; bool panel_replay_enabled; - u32 dc3co_exitline; u32 dc3co_exit_delay; - struct delayed_work dc3co_work; u8 entry_setup_frames; u8 io_wake_lines; @@ -1795,6 +1803,14 @@ struct intel_psr { struct ref_tracker *vblank_wakeref; }; +struct intel_dp_link_config { + int rate; + int lane_count; +}; + +#define INTEL_DP_LINK_CONFIG_NULL \ + ((struct intel_dp_link_config){}) + struct intel_dp { intel_reg_t output_reg; u32 DP; @@ -1820,29 +1836,9 @@ struct intel_dp { bool use_rate_select; /* Max sink lane count as reported by DP_MAX_LANE_COUNT */ int max_sink_lane_count; - /* intersection of source and sink rates */ - int num_common_rates; - int common_rates[DP_MAX_SUPPORTED_RATES]; - int max_common_lane_count; struct { /* TODO: move the rest of link specific fields to here */ bool active; - /* common rate,lane_count configs in bw order */ - int num_configs; -#define INTEL_DP_MAX_LANE_COUNT 4 -#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1) -#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) -#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS) -#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ - INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) - struct intel_dp_link_config { - u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; - u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; - } configs[INTEL_DP_MAX_LINK_CONFIGS]; - /* Max lane count for the current link */ - int max_lane_count; - /* Max rate for the current link */ - int max_rate; /* * Link parameters for which the MST topology was probed. * Tracking these ensures that the MST path resources are @@ -1851,13 +1847,8 @@ struct intel_dp { */ int mst_probed_lane_count; int mst_probed_rate; - int force_lane_count; - int force_rate; - bool retrain_disabled; - /* Sequential link training failures after a passing LT */ - int seq_train_failures; - int force_train_failure; - bool force_retrain; + struct intel_dp_link_training *training; + struct intel_dp_link_caps *caps; } link; bool reset_link_params; int mso_link_count; diff --git a/drivers/gpu/drm/i915/display/intel_dmc.c b/drivers/gpu/drm/i915/display/intel_dmc.c index 481fb65b7110..11f5dbf91e68 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc.c +++ b/drivers/gpu/drm/i915/display/intel_dmc.c @@ -941,6 +941,8 @@ void intel_dmc_load_program(struct intel_display *display) gen9_set_dc_state_debugmask(display); + xe3lpd_enable_dc_count(display); + pipedmc_clock_gating_wa(display, false); } @@ -1647,19 +1649,20 @@ static int intel_dmc_debugfs_status_show(struct seq_file *m, void *unused) DMC_VERSION_MINOR(dmc->version)); if (DISPLAY_VER(display) >= 12) { - intel_reg_t dc3co_reg; + if (DISPLAY_VER(display) >= 35) { + dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; + seq_printf(m, "DC3CO count: %d\n", + intel_de_read(display, XE3P_DMC_DC3CO_COUNT)); - if (display->platform.dgfx || DISPLAY_VER(display) >= 14) { - dc3co_reg = DG1_DMC_DEBUG3; + seq_printf(m, "DC3CO residency: %d\n", + intel_de_read(display, DC_STATE_DC3CO_RESIDENCY)); + } else if (display->platform.dgfx || DISPLAY_VER(display) >= 14) { dc5_reg = DG1_DMC_DEBUG_DC5_COUNT; } else { - dc3co_reg = TGL_DMC_DEBUG3; dc5_reg = TGL_DMC_DEBUG_DC5_COUNT; dc6_reg = TGL_DMC_DEBUG_DC6_COUNT; } - seq_printf(m, "DC3CO count: %d\n", - intel_de_read(display, dc3co_reg)); } else { dc5_reg = display->platform.broxton ? BXT_DMC_DC3_DC5_COUNT : SKL_DMC_DC3_DC5_COUNT; diff --git a/drivers/gpu/drm/i915/display/intel_dmc_regs.h b/drivers/gpu/drm/i915/display/intel_dmc_regs.h index 38e342b45af0..6b7978fb8986 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_regs.h +++ b/drivers/gpu/drm/i915/display/intel_dmc_regs.h @@ -531,6 +531,8 @@ enum pipedmc_event_id { #define TGL_DMC_DEBUG3 _MMIO(0x101090) #define DG1_DMC_DEBUG3 _MMIO(0x13415c) +#define XE3P_DMC_DC3CO_COUNT _MMIO(0x8f05c) + #define DMC_WAKELOCK_CFG _MMIO(0x8F1B0) #define DMC_WAKELOCK_CFG_ENABLE REG_BIT(31) #define DMC_WAKELOCK1_CTL _MMIO(0x8F140) diff --git a/drivers/gpu/drm/i915/display/intel_dmc_wl.c b/drivers/gpu/drm/i915/display/intel_dmc_wl.c index b007343721e1..ab4e0e9573df 100644 --- a/drivers/gpu/drm/i915/display/intel_dmc_wl.c +++ b/drivers/gpu/drm/i915/display/intel_dmc_wl.c @@ -267,7 +267,7 @@ static bool intel_dmc_wl_check_range(struct intel_display *display, * the DMC and requires a DC exit for proper access. */ switch (dc_state) { - case DC_STATE_EN_DC3CO: + case DC_STATE_EN_UPTO_DC3CO: ranges = xe3lpd_dc3co_dmc_ranges; break; case DC_STATE_EN_UPTO_DC5: diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index c81fc00a334c..6e3fa6662cbe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -33,7 +33,6 @@ #include <linux/notifier.h> #include <linux/seq_buf.h> #include <linux/slab.h> -#include <linux/sort.h> #include <linux/string_helpers.h> #include <linux/timekeeping.h> #include <linux/types.h> @@ -71,6 +70,7 @@ #include "intel_dp.h" #include "intel_dp_aux.h" #include "intel_dp_hdcp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -313,7 +313,7 @@ static void intel_dp_set_max_sink_lane_count(struct intel_dp *intel_dp) } /* Get length of rates array potentially limited by max_rate. */ -static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) +int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) { int i; @@ -326,31 +326,6 @@ static int intel_dp_rate_limit_len(const int *rates, int len, int max_rate) return 0; } -/* Get length of common rates array potentially limited by max_rate. */ -static int intel_dp_common_len_rate_limit(const struct intel_dp *intel_dp, - int max_rate) -{ - return intel_dp_rate_limit_len(intel_dp->common_rates, - intel_dp->num_common_rates, max_rate); -} - -int intel_dp_common_rate(struct intel_dp *intel_dp, int index) -{ - struct intel_display *display = to_intel_display(intel_dp); - - if (drm_WARN_ON(display->drm, - index < 0 || index >= intel_dp->num_common_rates)) - return 162000; - - return intel_dp->common_rates[index]; -} - -/* Theoretical max between source and sink */ -int intel_dp_max_common_rate(struct intel_dp *intel_dp) -{ - return intel_dp_common_rate(intel_dp, intel_dp->num_common_rates - 1); -} - int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) { int vbt_max_lanes = intel_bios_dp_max_lane_count(dig_port->base.devdata); @@ -364,43 +339,35 @@ int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port) /* * Theoretical max between source and sink. - * Return %true if the max common lane count changed. */ -static bool intel_dp_set_max_common_lane_count(struct intel_dp *intel_dp) +static int intel_dp_get_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); int sink_max = intel_dp->max_sink_lane_count; int lane_max = intel_tc_port_max_lane_count(dig_port); int lttpr_max = drm_dp_lttpr_max_lane_count(intel_dp->lttpr_common_caps); - int old_max_common_lane_count = intel_dp->max_common_lane_count; if (lttpr_max) sink_max = min(sink_max, lttpr_max); - intel_dp->max_common_lane_count = min3(source_max, sink_max, lane_max); - - return intel_dp->max_common_lane_count != old_max_common_lane_count; -} - -int intel_dp_max_common_lane_count(struct intel_dp *intel_dp) -{ - return intel_dp->max_common_lane_count; -} - -static int forced_lane_count(struct intel_dp *intel_dp) -{ - return clamp(intel_dp->link.force_lane_count, 1, intel_dp_max_common_lane_count(intel_dp)); + return min3(source_max, sink_max, lane_max); } int intel_dp_max_lane_count(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; + struct intel_dp_link_config forced_params; int lane_count; - if (intel_dp->link.force_lane_count) - lane_count = forced_lane_count(intel_dp); + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + if (forced_params.lane_count) + lane_count = forced_params.lane_count; else - lane_count = intel_dp->link.max_lane_count; + lane_count = max_link_limits.lane_count; switch (lane_count) { case 1: @@ -415,8 +382,12 @@ int intel_dp_max_lane_count(struct intel_dp *intel_dp) static int intel_dp_min_lane_count(struct intel_dp *intel_dp) { - if (intel_dp->link.force_lane_count) - return forced_lane_count(intel_dp); + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); + + if (forced_params.lane_count) + return forced_params.lane_count; return 1; } @@ -698,174 +669,44 @@ int intel_dp_rate_index(const int *rates, int len, int rate) return -1; } -static int intel_dp_link_config_rate(struct intel_dp *intel_dp, - const struct intel_dp_link_config *lc) -{ - return intel_dp_common_rate(intel_dp, lc->link_rate_idx); -} - -static int intel_dp_link_config_lane_count(const struct intel_dp_link_config *lc) -{ - return 1 << lc->lane_count_exp; -} - -static int intel_dp_link_config_bw(struct intel_dp *intel_dp, - const struct intel_dp_link_config *lc) -{ - return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(intel_dp, lc), - intel_dp_link_config_lane_count(lc)); -} - -static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) -{ - struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ - const struct intel_dp_link_config *lc_a = a; - const struct intel_dp_link_config *lc_b = b; - int bw_a = intel_dp_link_config_bw(intel_dp, lc_a); - int bw_b = intel_dp_link_config_bw(intel_dp, lc_b); - - if (bw_a != bw_b) - return bw_a - bw_b; - - return intel_dp_link_config_rate(intel_dp, lc_a) - - intel_dp_link_config_rate(intel_dp, lc_b); -} - -static void intel_dp_link_config_init(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_dp_link_config *lc; - int num_common_lane_configs; - int i; - int j; - - if (drm_WARN_ON(display->drm, !is_power_of_2(intel_dp_max_common_lane_count(intel_dp)))) - return; - - num_common_lane_configs = ilog2(intel_dp_max_common_lane_count(intel_dp)) + 1; - - if (drm_WARN_ON(display->drm, intel_dp->num_common_rates * num_common_lane_configs > - ARRAY_SIZE(intel_dp->link.configs))) - return; - - intel_dp->link.num_configs = intel_dp->num_common_rates * num_common_lane_configs; - - lc = &intel_dp->link.configs[0]; - for (i = 0; i < intel_dp->num_common_rates; i++) { - for (j = 0; j < num_common_lane_configs; j++) { - lc->lane_count_exp = j; - lc->link_rate_idx = i; - - lc++; - } - } - - sort_r(intel_dp->link.configs, intel_dp->link.num_configs, - sizeof(intel_dp->link.configs[0]), - link_config_cmp_by_bw, NULL, - intel_dp); -} - -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count) -{ - struct intel_display *display = to_intel_display(intel_dp); - const struct intel_dp_link_config *lc; - - if (drm_WARN_ON(display->drm, idx < 0 || idx >= intel_dp->link.num_configs)) - idx = 0; - - lc = &intel_dp->link.configs[idx]; - - *link_rate = intel_dp_link_config_rate(intel_dp, lc); - *lane_count = intel_dp_link_config_lane_count(lc); -} - -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count) -{ - int link_rate_idx = intel_dp_rate_index(intel_dp->common_rates, intel_dp->num_common_rates, - link_rate); - int lane_count_exp = ilog2(lane_count); - int i; - - for (i = 0; i < intel_dp->link.num_configs; i++) { - const struct intel_dp_link_config *lc = &intel_dp->link.configs[i]; - - if (lc->lane_count_exp == lane_count_exp && - lc->link_rate_idx == link_rate_idx) - return i; - } - - return -1; -} - -/* Return %true if the common rates changed. */ -static bool intel_dp_set_common_rates(struct intel_dp *intel_dp) +static void intel_dp_get_common_rates(struct intel_dp *intel_dp, + int common_rates[DP_MAX_SUPPORTED_RATES], + int *num_common_rates) { struct intel_display *display = to_intel_display(intel_dp); - int num_old_common_rates = intel_dp->num_common_rates; - int old_common_rates[DP_MAX_SUPPORTED_RATES]; drm_WARN_ON(display->drm, !intel_dp->num_source_rates || !intel_dp->num_sink_rates); - /* TODO: Add a struct containing both rates and number of rates. */ - static_assert(__same_type(old_common_rates[0], intel_dp->common_rates[0]) && - sizeof(old_common_rates) == sizeof(intel_dp->common_rates)); - memcpy(old_common_rates, intel_dp->common_rates, - num_old_common_rates * sizeof(old_common_rates[0])); - - intel_dp->num_common_rates = intersect_rates(intel_dp->source_rates, - intel_dp->num_source_rates, - intel_dp->sink_rates, - intel_dp->num_sink_rates, - intel_dp->common_rates); + *num_common_rates = intersect_rates(intel_dp->source_rates, + intel_dp->num_source_rates, + intel_dp->sink_rates, + intel_dp->num_sink_rates, + common_rates); /* Paranoia, there should always be something in common. */ - if (drm_WARN_ON(display->drm, intel_dp->num_common_rates == 0)) { - intel_dp->common_rates[0] = 162000; - intel_dp->num_common_rates = 1; + if (drm_WARN_ON(display->drm, *num_common_rates == 0)) { + common_rates[0] = 162000; + *num_common_rates = 1; } - - return num_old_common_rates != intel_dp->num_common_rates || - memcmp(old_common_rates, intel_dp->common_rates, - num_old_common_rates * sizeof(old_common_rates[0])); } /* Return %true if any common link param changed. */ static bool intel_dp_set_common_link_params(struct intel_dp *intel_dp) { + int num_common_rates; + int common_rates[DP_MAX_SUPPORTED_RATES]; bool params_changed = false; - if (intel_dp_set_common_rates(intel_dp)) + intel_dp_get_common_rates(intel_dp, common_rates, &num_common_rates); + if (intel_dp_link_caps_update(intel_dp->link.caps, + common_rates, num_common_rates, + intel_dp_get_max_common_lane_count(intel_dp))) params_changed = true; - if (intel_dp_set_max_common_lane_count(intel_dp)) - params_changed = true; - - intel_dp_link_config_init(intel_dp); - return params_changed; } -bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, - u8 lane_count) -{ - /* - * FIXME: we need to synchronize the current link parameters with - * hardware readout. Currently fast link training doesn't work on - * boot-up. - */ - if (link_rate == 0 || - link_rate > intel_dp->link.max_rate) - return false; - - if (lane_count == 0 || - lane_count > intel_dp_max_lane_count(intel_dp)) - return false; - - return true; -} - u32 intel_dp_mode_to_fec_clock(u32 mode_clock) { return div_u64(mul_u32_u32(mode_clock, DP_DSC_FEC_OVERHEAD_FACTOR), @@ -1693,41 +1534,40 @@ static void intel_dp_print_rates(struct intel_dp *intel_dp) seq_buf_print_array(&s, intel_dp->sink_rates, intel_dp->num_sink_rates); drm_dbg_kms(display->drm, "sink rates: %s\n", seq_buf_str(&s)); - seq_buf_clear(&s); - seq_buf_print_array(&s, intel_dp->common_rates, intel_dp->num_common_rates); - drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); -} - -static int forced_link_rate(struct intel_dp *intel_dp) -{ - int len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.force_rate); - - if (len == 0) - return intel_dp_common_rate(intel_dp, 0); - - return intel_dp_common_rate(intel_dp, len - 1); + intel_dp_link_caps_print_common_rates(intel_dp->link.caps); } int intel_dp_max_link_rate(struct intel_dp *intel_dp) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; + struct intel_dp_link_config forced_params; int len; - if (intel_dp->link.force_rate) - return forced_link_rate(intel_dp); + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); - len = intel_dp_common_len_rate_limit(intel_dp, intel_dp->link.max_rate); + if (forced_params.rate) + return forced_params.rate; - return intel_dp_common_rate(intel_dp, len - 1); + intel_dp_link_caps_get_max_limits(link_caps, &max_link_limits); + len = intel_dp_common_len_rate_limit(link_caps, max_link_limits.rate); + + return intel_dp_common_rate(link_caps, len - 1); } static int intel_dp_min_link_rate(struct intel_dp *intel_dp) { - if (intel_dp->link.force_rate) - return forced_link_rate(intel_dp); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); - return intel_dp_common_rate(intel_dp, 0); + if (forced_params.rate) + return forced_params.rate; + + return intel_dp_common_rate(link_caps, 0); } int intel_dp_rate_select(struct intel_dp *intel_dp, int rate) @@ -1911,6 +1751,7 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state, const struct link_config_limits *limits) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int bpp, i, lane_count, clock = intel_dp_mode_clock(pipe_config, conn_state); int link_rate, link_avail; @@ -1920,8 +1761,8 @@ intel_dp_compute_link_config_wide(struct intel_dp *intel_dp, int link_bpp_x16 = intel_dp_output_format_link_bpp_x16(pipe_config->output_format, bpp); - for (i = 0; i < intel_dp->num_common_rates; i++) { - link_rate = intel_dp_common_rate(intel_dp, i); + for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { + link_rate = intel_dp_common_rate(link_caps, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) continue; @@ -2145,12 +1986,13 @@ static int dsc_compute_link_config(struct intel_dp *intel_dp, const struct link_config_limits *limits, int dsc_bpp_x16) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; const struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; int link_rate, lane_count; int i; - for (i = 0; i < intel_dp->num_common_rates; i++) { - link_rate = intel_dp_common_rate(intel_dp, i); + for (i = 0; i < intel_dp_link_caps_num_common_rates(intel_dp->link.caps); i++) { + link_rate = intel_dp_common_rate(link_caps, i); if (link_rate < limits->min_rate || link_rate > limits->max_rate) continue; @@ -3685,19 +3527,19 @@ int intel_dp_compute_min_hblank(struct intel_crtc_state *crtc_state, } int -intel_dp_compute_config(struct intel_encoder *encoder, +intel_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - 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); struct intel_connector *connector = intel_dp->attached_connector; int ret = 0, link_bpp_x16; if (intel_dp_is_edp(intel_dp)) { - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, connector); if (ret) return ret; } @@ -3810,12 +3652,10 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, void intel_dp_reset_link_params(struct intel_dp *intel_dp) { - intel_dp->link.max_lane_count = intel_dp_max_common_lane_count(intel_dp); - intel_dp->link.max_rate = intel_dp_max_common_rate(intel_dp); + intel_dp_link_caps_reset(intel_dp->link.caps); intel_dp->link.mst_probed_lane_count = 0; intel_dp->link.mst_probed_rate = 0; - intel_dp->link.retrain_disabled = false; - intel_dp->link.seq_train_failures = 0; + intel_dp_link_training_reset(intel_dp->link.training); } /* Enable backlight PWM and backlight PP control. */ @@ -5715,32 +5555,6 @@ void intel_read_dp_sdp(struct intel_encoder *encoder, } } -static bool intel_dp_link_ok(struct intel_dp *intel_dp, - u8 link_status[DP_LINK_STATUS_SIZE]) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; - bool uhbr = intel_dp->link_rate >= 1000000; - bool ok; - - if (uhbr) - ok = drm_dp_128b132b_lane_channel_eq_done(link_status, - intel_dp->lane_count); - else - ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count); - - if (ok) - return true; - - intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status); - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] %s link not ok, retraining\n", - encoder->base.base.id, encoder->base.name, - uhbr ? "128b/132b" : "8b/10b"); - - return false; -} - static void intel_dp_mst_hpd_irq(struct intel_dp *intel_dp, u8 *esi, u8 *ack) { @@ -5774,7 +5588,7 @@ static bool intel_dp_check_mst_status(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); - bool force_retrain = intel_dp->link.force_retrain; + bool force_retrain = intel_dp_link_training_get_force_retrain(intel_dp->link.training); bool reprobe_needed = false; for (;;) { @@ -5847,78 +5661,6 @@ intel_dp_handle_hdmi_link_status_change(struct intel_dp *intel_dp) } } -static int -intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE]) -{ - int err; - - memset(link_status, 0, DP_LINK_STATUS_SIZE); - - if (intel_dp_mst_active_streams(intel_dp) > 0) - err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI, - link_status, DP_LINK_STATUS_SIZE - 2); - else - err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX, - link_status); - - if (err) - return err; - - if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] & - DP_DOWNSTREAM_PORT_STATUS_CHANGED) - WRITE_ONCE(intel_dp->downstream_port_changed, true); - - return 0; -} - -static bool -intel_dp_needs_link_retrain(struct intel_dp *intel_dp) -{ - u8 link_status[DP_LINK_STATUS_SIZE]; - - if (!intel_dp->link.active) - return false; - - /* - * While PSR source HW is enabled, it will control main-link sending - * frames, enabling and disabling it so trying to do a retrain will fail - * as the link would or not be on or it could mix training patterns - * and frame data at the same time causing retrain to fail. - * Also when exiting PSR, HW will retrain the link anyways fixing - * any link status error. - */ - if (intel_psr_enabled(intel_dp)) - return false; - - if (intel_dp->link.force_retrain) - return true; - - if (intel_dp_read_link_status(intel_dp, link_status) < 0) - return false; - - /* - * Validate the cached values of intel_dp->link_rate and - * intel_dp->lane_count before attempting to retrain. - * - * FIXME would be nice to user the crtc state here, but since - * we need to call this from the short HPD handler that seems - * a bit hard. - */ - if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate, - intel_dp->lane_count)) - return false; - - if (intel_dp->link.retrain_disabled) - return false; - - if (intel_dp->link.seq_train_failures) - return true; - - /* Retrain if link not ok */ - return !intel_dp_link_ok(intel_dp, link_status) && - !intel_psr_link_ok(intel_dp); -} - bool intel_dp_has_connector(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state) { @@ -6010,86 +5752,6 @@ void intel_dp_flush_connector_commits(struct intel_connector *connector) wait_for_connector_hw_done(connector->base.state); } -static bool intel_dp_is_connected(struct intel_dp *intel_dp) -{ - struct intel_connector *connector = intel_dp->attached_connector; - - return connector->base.status == connector_status_connected || - intel_dp->is_mst; -} - -static int intel_dp_retrain_link(struct intel_encoder *encoder, - struct drm_modeset_acquire_ctx *ctx) -{ - struct intel_display *display = to_intel_display(encoder); - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - u8 pipe_mask; - int ret; - - if (!intel_dp_is_connected(intel_dp)) - return 0; - - ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex, - ctx); - if (ret) - return ret; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return 0; - - ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); - if (ret) - return ret; - - if (pipe_mask == 0) - return 0; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return 0; - - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] retraining link (forced %s)\n", - encoder->base.base.id, encoder->base.name, - str_yes_no(intel_dp->link.force_retrain)); - - ret = intel_modeset_commit_pipes(display, pipe_mask, ctx); - if (ret == -EDEADLK) - return ret; - - intel_dp->link.force_retrain = false; - - if (ret) - drm_dbg_kms(display->drm, - "[ENCODER:%d:%s] link retraining failed: %pe\n", - encoder->base.base.id, encoder->base.name, - ERR_PTR(ret)); - - return ret; -} - -void intel_dp_link_check(struct intel_encoder *encoder) -{ - struct drm_modeset_acquire_ctx ctx; - int ret; - - intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) - ret = intel_dp_retrain_link(encoder, &ctx); -} - -void intel_dp_check_link_state(struct intel_dp *intel_dp) -{ - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - struct intel_encoder *encoder = &dig_port->base; - - if (!intel_dp_is_connected(intel_dp)) - return; - - if (!intel_dp_needs_link_retrain(intel_dp)) - return; - - intel_encoder_link_check_queue_work(encoder, 0); -} - static void intel_dp_handle_device_service_irq(struct intel_dp *intel_dp, u8 irq_mask) { struct intel_display *display = to_intel_display(intel_dp); @@ -6185,7 +5847,7 @@ intel_dp_short_pulse(struct intel_dp *intel_dp) /* * Force checking the link status for DPCD_REV < 1.2 * TODO: let the link status check depend on LINK_STATUS_CHANGED - * or intel_dp->link.force_retrain for DPCD_REV >= 1.2 + * or intel_dp->link.training.force_retrain for DPCD_REV >= 1.2 */ esi[3] |= LINK_STATUS_CHANGED; if (intel_dp_handle_link_service_irq(intel_dp, esi[3])) @@ -7688,3 +7350,25 @@ u8 intel_dp_as_sdp_transmission_time(void) return DP_PR_AS_SDP_SETUP_TIME_T1; } + +int intel_dp_link_init(struct intel_dp *intel_dp) +{ + intel_dp->link.training = intel_dp_link_training_init(intel_dp); + if (!intel_dp->link.training) + return -ENOMEM; + + intel_dp->link.caps = intel_dp_link_caps_init(intel_dp); + if (!intel_dp->link.caps) { + intel_dp_link_training_cleanup(intel_dp->link.training); + + return -ENOMEM; + } + + return 0; +} + +void intel_dp_link_cleanup(struct intel_dp *intel_dp) +{ + intel_dp_link_caps_cleanup(intel_dp->link.caps); + intel_dp_link_training_cleanup(intel_dp->link.training); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 46a7f5c70981..02b691df6755 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -59,8 +59,6 @@ int intel_dp_get_active_pipes(struct intel_dp *intel_dp, struct drm_modeset_acquire_ctx *ctx, u8 *pipe_mask); void intel_dp_flush_connector_commits(struct intel_connector *connector); -void intel_dp_link_check(struct intel_encoder *encoder); -void intel_dp_check_link_state(struct intel_dp *intel_dp); void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode); void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); @@ -73,7 +71,8 @@ void intel_dp_sink_disable_decompression(struct intel_atomic_state *state, void intel_dp_encoder_suspend(struct intel_encoder *intel_encoder); void intel_dp_encoder_shutdown(struct intel_encoder *intel_encoder); void intel_dp_encoder_flush_work(struct drm_encoder *encoder); -int intel_dp_compute_config(struct intel_encoder *encoder, +int intel_dp_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state); bool intel_dp_needs_8b10b_fec(const struct intel_crtc_state *crtc_state, @@ -102,17 +101,13 @@ void intel_edp_backlight_off(const struct drm_connector_state *conn_state); void intel_edp_fixup_vbt_bpp(struct intel_encoder *encoder, int pipe_bpp); void intel_dp_mst_suspend(struct intel_display *display); void intel_dp_mst_resume(struct intel_display *display); +int intel_dp_rate_limit_len(const int *rates, int len, int max_rate); int intel_dp_max_source_lane_count(struct intel_digital_port *dig_port); 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); -int intel_dp_common_rate(struct intel_dp *intel_dp, int index); int intel_dp_rate_index(const int *rates, int len, int rate); -int intel_dp_link_config_index(struct intel_dp *intel_dp, int link_rate, int lane_count); -void intel_dp_link_config_get(struct intel_dp *intel_dp, int idx, int *link_rate, int *lane_count); void intel_dp_update_sink_caps(struct intel_dp *intel_dp); void intel_dp_reset_link_params(struct intel_dp *intel_dp); @@ -209,8 +204,6 @@ void intel_dp_get_dsc_sink_cap(u8 dpcd_rev, struct intel_connector *connector); bool intel_dp_has_gamut_metadata_dip(struct intel_encoder *encoder); -bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, - u8 lane_count); bool intel_dp_has_connector(struct intel_dp *intel_dp, const struct drm_connector_state *conn_state); int intel_dp_dsc_max_src_input_bpc(struct intel_display *display); @@ -243,4 +236,7 @@ bool intel_dp_joiner_candidate_valid(struct intel_connector *connector, u8 intel_dp_as_sdp_transmission_time(void); +int intel_dp_link_init(struct intel_dp *intel_dp); +void intel_dp_link_cleanup(struct intel_dp *intel_dp); + #endif /* __INTEL_DP_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.c b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c new file mode 100644 index 000000000000..1c34ba6c49c3 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.c @@ -0,0 +1,675 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2026 Intel Corporation + */ + +#include <linux/bitops.h> +#include <linux/debugfs.h> +#include <linux/log2.h> +#include <linux/seq_buf.h> +#include <linux/slab.h> +#include <linux/sort.h> +#include <linux/string.h> +#include <linux/types.h> + +#include <drm/drm_print.h> + +#include "intel_display_core.h" +#include "intel_display_types.h" +#include "intel_dp.h" +#include "intel_dp_link_caps.h" + +struct intel_dp_link_caps { + struct intel_dp *dp; + + /* Rate, lane count caps common to source and sink. */ + int num_rates; + int rates[DP_MAX_SUPPORTED_RATES]; + int max_lane_count; + + /* common rate,lane_count configs in bw order */ + int num_configs; +#define INTEL_DP_MAX_LANE_COUNT 4 +#define INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS (ilog2(INTEL_DP_MAX_LANE_COUNT) + 1) +#define INTEL_DP_LANE_COUNT_EXP_BITS order_base_2(INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) +#define INTEL_DP_LINK_RATE_IDX_BITS (BITS_PER_TYPE(u8) - INTEL_DP_LANE_COUNT_EXP_BITS) +#define INTEL_DP_MAX_LINK_CONFIGS (DP_MAX_SUPPORTED_RATES * \ + INTEL_DP_MAX_SUPPORTED_LANE_CONFIGS) + struct intel_dp_link_config_entry { + /* index into rates[] */ + u8 link_rate_idx:INTEL_DP_LINK_RATE_IDX_BITS; + u8 lane_count_exp:INTEL_DP_LANE_COUNT_EXP_BITS; + } configs[INTEL_DP_MAX_LINK_CONFIGS]; + + /* + * Forced parameters requested via debugfs. Remains set across sink + * disconnects. + */ + struct intel_dp_link_config forced_params; + + /* + * User set maximum limits. These limits constrain the currently + * allowed set of configurations and are not adjusted when sink + * capabilities change. + * + * max_limits.rate/lane_count may come from different allowed + * configurations, i.e. the (max_limits.rate, max_limits.lane_count) + * tuple itself may not be an allowed configuration. + */ + struct intel_dp_link_config max_limits; +}; + +/* Get length of common rates array potentially limited by max_rate. */ +int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, + int max_rate) +{ + return intel_dp_rate_limit_len(link_caps->rates, + link_caps->num_rates, max_rate); +} + +int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index) +{ + struct intel_display *display = to_intel_display(link_caps->dp); + + if (drm_WARN_ON(display->drm, + index < 0 || index >= link_caps->num_rates)) + return 162000; + + return link_caps->rates[index]; +} + +int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate) +{ + return intel_dp_rate_index(link_caps->rates, + link_caps->num_rates, + rate); +} + +/* Theoretical max between source and sink */ +int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps) +{ + return intel_dp_common_rate(link_caps, link_caps->num_rates - 1); +} + +int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps) +{ + return link_caps->num_rates; +} + +void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps) +{ + struct intel_display *display = to_intel_display(link_caps->dp); + DECLARE_SEQ_BUF(s, 128); + int i; + + for (i = 0; i < link_caps->num_rates; i++) + seq_buf_printf(&s, "%s%d", i ? ", " : "", link_caps->rates[i]); + + drm_dbg_kms(display->drm, "common rates: %s\n", seq_buf_str(&s)); +} + +int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps) +{ + return link_caps->max_lane_count; +} + +static int forced_lane_count(struct intel_dp_link_caps *link_caps) +{ + if (!link_caps->forced_params.lane_count) + return 0; + + return clamp(link_caps->forced_params.lane_count, + 1, intel_dp_link_caps_max_common_lane_count(link_caps)); +} + +static int forced_link_rate(struct intel_dp_link_caps *link_caps) +{ + int len; + + if (!link_caps->forced_params.rate) + return 0; + + len = intel_dp_common_len_rate_limit(link_caps, link_caps->forced_params.rate); + if (len == 0) + return intel_dp_common_rate(link_caps, 0); + + return intel_dp_common_rate(link_caps, len - 1); +} + +void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *forced_params) +{ + forced_params->rate = forced_link_rate(link_caps); + forced_params->lane_count = forced_lane_count(link_caps); +} + +static int intel_dp_link_config_rate(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config_entry *lce) +{ + return intel_dp_common_rate(link_caps, lce->link_rate_idx); +} + +static int intel_dp_link_config_lane_count(const struct intel_dp_link_config_entry *lce) +{ + return 1 << lce->lane_count_exp; +} + +static void set_max_link_limits_no_update(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + link_caps->max_limits = *max_link_limits; +} + +static void reset_max_link_limits_no_update(struct intel_dp_link_caps *link_caps) +{ + struct intel_dp_link_config max_link_limits = { + .rate = intel_dp_max_common_rate(link_caps), + .lane_count = intel_dp_link_caps_max_common_lane_count(link_caps), + }; + + set_max_link_limits_no_update(link_caps, &max_link_limits); +} + +/** + * intel_dp_link_caps_get_max_limits - get the current maximum link limits + * @link_caps: link capabilities state + * @max_link_limits: returned maximum link limits + * + * Return the current maximum rate and lane count limits in + * @max_link_limits. + * + * These limits constrain the set of allowed configurations. + * + * The limits are set to the maximum common supported values after + * intel_dp_link_caps_reset() is called, and can later be modified by + * intel_dp_link_caps_set_max_limits(). The max rate and lane count + * parameters are independent limits, so the pair does not necessarily + * define a valid configuration. + * + * This function may be called without serializing against updates to + * @link_caps. However, without such serialization the returned value may be + * an out-of-sync (link rate, lane count) tuple, i.e. the parameters may + * belong to different update snapshots in time. + */ +void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_link_limits) +{ + *max_link_limits = link_caps->max_limits; +} + +/** + * intel_dp_link_caps_set_max_limits - set the current maximum link limits + * @link_caps: link capabilities state + * @max_link_limits: new maximum link limits + * + * Set the current maximum rate and lane count limits to @max_link_limits, + * constraining the set of allowed configurations. + * + * Unlike intel_dp_link_caps_get_max_limits(), the caller must serialize + * this call against concurrent queries and updates to @link_caps, in line + * with the rest of the API. + * + * Return: + * - %true if the @link_caps cached max limits value got updated with + * @max_link_limits. + * - %false if @max_link_limits is invalid. + */ +bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits) +{ + set_max_link_limits_no_update(link_caps, max_link_limits); + + /* TODO: validate max_link_limits */ + return true; +} + +/** + * intel_dp_link_caps_reset_max_limits - reset the current maximum link limits + * @link_caps: link capabilities state + * + * Reset the current maximum link limits to the maximum supported common link + * rate and lane count. + */ +void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps) +{ + reset_max_link_limits_no_update(link_caps); +} + +static int intel_dp_link_config_bw(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config_entry *lce) +{ + return drm_dp_max_dprx_data_rate(intel_dp_link_config_rate(link_caps, lce), + intel_dp_link_config_lane_count(lce)); +} + +static int link_config_cmp_by_bw(const void *a, const void *b, const void *p) +{ + struct intel_dp *intel_dp = (struct intel_dp *)p; /* remove const */ + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + + const struct intel_dp_link_config_entry *lce_a = a; + const struct intel_dp_link_config_entry *lce_b = b; + int bw_a = intel_dp_link_config_bw(link_caps, lce_a); + int bw_b = intel_dp_link_config_bw(link_caps, lce_b); + + if (bw_a != bw_b) + return bw_a - bw_b; + + return intel_dp_link_config_rate(link_caps, lce_a) - + intel_dp_link_config_rate(link_caps, lce_b); +} + +/* Return %true if the supported link parameters have changed. */ +bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, + const int *rates, int num_rates, int max_lane_count) +{ + struct intel_dp *intel_dp = link_caps->dp; + struct intel_display *display = to_intel_display(intel_dp); + struct intel_dp_link_config_entry *lce; + bool link_params_changed = false; + int num_common_lane_configs; + int i; + int j; + + if (drm_WARN_ON(display->drm, !is_power_of_2(max_lane_count))) + return false; + + if (drm_WARN_ON(display->drm, num_rates > ARRAY_SIZE(link_caps->rates))) + return false; + + num_common_lane_configs = ilog2(max_lane_count) + 1; + + if (drm_WARN_ON(display->drm, num_rates * num_common_lane_configs > + ARRAY_SIZE(link_caps->configs))) + return false; + + /* TODO: Add a struct containing both rates and number of rates. */ + static_assert(__same_type(rates[0], link_caps->rates[0])); + if (num_rates != link_caps->num_rates || + memcmp(rates, link_caps->rates, num_rates * sizeof(rates[0]))) + link_params_changed = true; + + if (max_lane_count != link_caps->max_lane_count) + link_params_changed = true; + + memcpy(link_caps->rates, rates, num_rates * sizeof(rates[0])); + link_caps->num_rates = num_rates; + link_caps->max_lane_count = max_lane_count; + + link_caps->num_configs = num_rates * num_common_lane_configs; + + lce = &link_caps->configs[0]; + for (i = 0; i < link_caps->num_rates; i++) { + for (j = 0; j < num_common_lane_configs; j++) { + lce->lane_count_exp = j; + lce->link_rate_idx = i; + + lce++; + } + } + + sort_r(link_caps->configs, link_caps->num_configs, + sizeof(link_caps->configs[0]), + link_config_cmp_by_bw, NULL, + intel_dp); + + return link_params_changed; +} + +void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, + int idx, int *link_rate, int *lane_count) +{ + struct intel_display *display = to_intel_display(link_caps->dp); + const struct intel_dp_link_config_entry *lce; + + if (drm_WARN_ON(display->drm, idx < 0 || idx >= link_caps->num_configs)) + idx = 0; + + lce = &link_caps->configs[idx]; + + *link_rate = intel_dp_link_config_rate(link_caps, lce); + *lane_count = intel_dp_link_config_lane_count(lce); +} + +int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, + int link_rate, int lane_count) +{ + int link_rate_idx = intel_dp_rate_index(link_caps->rates, link_caps->num_rates, + link_rate); + int lane_count_exp = ilog2(lane_count); + int i; + + for (i = 0; i < link_caps->num_configs; i++) { + const struct intel_dp_link_config_entry *lce = &link_caps->configs[i]; + + if (lce->lane_count_exp == lane_count_exp && + lce->link_rate_idx == link_rate_idx) + return i; + } + + return -1; +} + +/** + * intel_dp_link_caps_reset - reset link capability restrictions + * @link_caps: link capabilities state + * + * Reset all current restrictions except for the user requested forced + * parameters, thus updating the set of allowed configurations and the + * derived maximum link information accordingly. + * + * This function is regularly called after a sink is connected, either + * for the first time to the connector or after a previous sink was + * disconnected from it, and intel_dp_link_caps_update() was called. + */ +void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps) +{ + /* TODO: Update the maximum link information. */ + reset_max_link_limits_no_update(link_caps); +} + +static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int current_rate = -1; + int force_rate; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + if (intel_dp->link.active) + current_rate = intel_dp->link_rate; + + force_rate = link_caps->forced_params.rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_printf(m, "%sauto%s", + force_rate == 0 ? "[" : "", + force_rate == 0 ? "]" : ""); + + for (i = 0; i < intel_dp->num_source_rates; i++) + seq_printf(m, " %s%d%s%s", + intel_dp->source_rates[i] == force_rate ? "[" : "", + intel_dp->source_rates[i], + intel_dp->source_rates[i] == current_rate ? "*" : "", + intel_dp->source_rates[i] == force_rate ? "]" : ""); + + seq_putc(m, '\n'); + + return 0; +} + +static int parse_link_rate(struct intel_dp_link_caps *link_caps, const char __user *ubuf, size_t len) +{ + struct intel_dp *intel_dp = link_caps->dp; + char *kbuf; + const char *p; + int rate; + int ret = 0; + + kbuf = memdup_user_nul(ubuf, len); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + p = strim(kbuf); + + if (!strcmp(p, "auto")) { + rate = 0; + } else { + ret = kstrtoint(p, 0, &rate); + if (ret < 0) + goto out_free; + + if (intel_dp_rate_index(intel_dp->source_rates, + intel_dp->num_source_rates, + rate) < 0) + ret = -EINVAL; + } + +out_free: + kfree(kbuf); + + return ret < 0 ? ret : rate; +} + +static ssize_t i915_dp_force_link_rate_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 = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int rate; + int err; + + rate = parse_link_rate(link_caps, ubuf, len); + if (rate < 0) + return rate; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_reset_link_params(intel_dp); + link_caps->forced_params.rate = rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + *offp += len; + + return len; +} +DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate); + +static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) +{ + struct intel_connector *connector = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int current_lane_count = -1; + int force_lane_count; + int err; + int i; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + if (intel_dp->link.active) + current_lane_count = intel_dp->lane_count; + force_lane_count = link_caps->forced_params.lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + seq_printf(m, "%sauto%s", + force_lane_count == 0 ? "[" : "", + force_lane_count == 0 ? "]" : ""); + + for (i = 1; i <= 4; i <<= 1) + seq_printf(m, " %s%d%s%s", + i == force_lane_count ? "[" : "", + i, + i == current_lane_count ? "*" : "", + i == force_lane_count ? "]" : ""); + + seq_putc(m, '\n'); + + return 0; +} + +static int parse_lane_count(const char __user *ubuf, size_t len) +{ + char *kbuf; + const char *p; + int lane_count; + int ret = 0; + + kbuf = memdup_user_nul(ubuf, len); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + p = strim(kbuf); + + if (!strcmp(p, "auto")) { + lane_count = 0; + } else { + ret = kstrtoint(p, 0, &lane_count); + if (ret < 0) + goto out_free; + + switch (lane_count) { + case 1: + case 2: + case 4: + break; + default: + ret = -EINVAL; + } + } + +out_free: + kfree(kbuf); + + return ret < 0 ? ret : lane_count; +} + +static ssize_t i915_dp_force_lane_count_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 = to_intel_connector(m->private); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int lane_count; + int err; + + lane_count = parse_lane_count(ubuf, len); + if (lane_count < 0) + return lane_count; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_reset_link_params(intel_dp); + link_caps->forced_params.lane_count = lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + *offp += len; + + return len; +} +DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count); + +static int i915_dp_max_link_rate_show(void *data, u64 *val) +{ + struct intel_connector *connector = to_intel_connector(data); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_config max_link_limits; + int err; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); + *val = max_link_limits.rate; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n"); + +static int i915_dp_max_lane_count_show(void *data, u64 *val) +{ + struct intel_connector *connector = to_intel_connector(data); + struct intel_display *display = to_intel_display(connector); + struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_config max_link_limits; + int err; + + err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); + if (err) + return err; + + intel_dp_flush_connector_commits(connector); + + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); + *val = max_link_limits.lane_count; + + drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); + + +/** + * intel_dp_link_caps_debugfs_add - add link caps debugfs files for a connector + * @connector: connector to add the debugfs files for + * + * Add the link-capability debugfs files for a DP @connector. + */ +void intel_dp_link_caps_debugfs_add(struct intel_connector *connector) +{ + struct dentry *root = connector->base.debugfs_entry; + + if (connector->base.connector_type != DRM_MODE_CONNECTOR_DisplayPort && + connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) + return; + + debugfs_create_file("i915_dp_force_link_rate", 0644, root, + connector, &i915_dp_force_link_rate_fops); + + debugfs_create_file("i915_dp_force_lane_count", 0644, root, + connector, &i915_dp_force_lane_count_fops); + + debugfs_create_file("i915_dp_max_link_rate", 0444, root, + connector, &i915_dp_max_link_rate_fops); + + debugfs_create_file("i915_dp_max_lane_count", 0444, root, + connector, &i915_dp_max_lane_count_fops); +} + +struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp) +{ + struct intel_dp_link_caps *link_caps; + + link_caps = kzalloc_obj(*link_caps); + if (!link_caps) + return NULL; + + link_caps->dp = intel_dp; + + return link_caps; +} + +void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps) +{ + kfree(link_caps); +} diff --git a/drivers/gpu/drm/i915/display/intel_dp_link_caps.h b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h new file mode 100644 index 000000000000..af9028e7cb98 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_dp_link_caps.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __INTEL_DP_LINK_CAPS_H__ +#define __INTEL_DP_LINK_CAPS_H__ + +#include <linux/types.h> + +struct intel_connector; +struct intel_dp; +struct intel_dp_link_caps; +struct intel_dp_link_config; + +int intel_dp_common_len_rate_limit(struct intel_dp_link_caps *link_caps, + int max_rate); +int intel_dp_common_rate(struct intel_dp_link_caps *link_caps, int index); +int intel_dp_link_caps_common_rate_idx(struct intel_dp_link_caps *link_caps, int rate); +int intel_dp_max_common_rate(struct intel_dp_link_caps *link_caps); +int intel_dp_link_caps_num_common_rates(struct intel_dp_link_caps *link_caps); +int intel_dp_link_caps_max_common_lane_count(struct intel_dp_link_caps *link_caps); + +void intel_dp_link_caps_print_common_rates(struct intel_dp_link_caps *link_caps); + +void intel_dp_link_caps_get_forced_params(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *forced_params); + +int intel_dp_link_config_index(struct intel_dp_link_caps *link_caps, + int link_rate, int lane_count); +void intel_dp_link_config_get(struct intel_dp_link_caps *link_caps, + int idx, int *link_rate, int *lane_count); + +void intel_dp_link_caps_get_max_limits(struct intel_dp_link_caps *link_caps, + struct intel_dp_link_config *max_link_limits); +bool intel_dp_link_caps_set_max_limits(struct intel_dp_link_caps *link_caps, + const struct intel_dp_link_config *max_link_limits); +void intel_dp_link_caps_reset_max_limits(struct intel_dp_link_caps *link_caps); + +bool intel_dp_link_caps_update(struct intel_dp_link_caps *link_caps, + const int *rates, int num_rates, int max_lane_count); +void intel_dp_link_caps_reset(struct intel_dp_link_caps *link_caps); + +void intel_dp_link_caps_debugfs_add(struct intel_connector *connector); + +struct intel_dp_link_caps *intel_dp_link_caps_init(struct intel_dp *intel_dp); +void intel_dp_link_caps_cleanup(struct intel_dp_link_caps *link_caps); + +#endif /* __INTEL_DP_LINK_CAPS_H__ */ 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 e566f2b49594..b521dd11b62a 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -27,18 +27,344 @@ #include <drm/display/drm_dp_helper.h> #include <drm/drm_print.h> +#include "intel_display.h" #include "intel_display_core.h" #include "intel_display_jiffies.h" #include "intel_display_types.h" #include "intel_display_utils.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" +#include "intel_dp_mst.h" #include "intel_encoder.h" #include "intel_hdmi.h" #include "intel_hotplug.h" +#include "intel_modeset_lock.h" #include "intel_panel.h" #include "intel_psr.h" +/** + * DOC: DisplayPort link training + * + * This documents the Intel DisplayPort link training implementation and + * its internal interfaces, with a current focus on link recovery. + * + * Documentation of the full link training procedure is not yet included. + * + * The Intel DP link recovery logic governs how the driver reacts to + * link training failures and to links that degrade asynchronously + * after a previously successful training. Recovery is first attempted + * via automatic retraining (``autoretrain``) and, when that is no + * longer possible, by selecting fallback link configurations and + * notifying userspace to recover the link via a modeset. + * + * Recovery sequence and userspace notification + * -------------------------------------------- + * + * After the first link training failure following initialization or a + * previously successful training, recovery is first attempted by the + * driver via automatic retraining, without userspace involvement. + * During this phase, a given link configuration is attempted twice + * before being abandoned: after the initial link training failure, an + * automatic retraining modeset is performed with the same link + * parameters, constituting the second attempt. + * + * Once automatic retraining is no longer possible, recovery is delegated + * to userspace, which must select a new modeset configuration, as the + * kernel must not do so. From this point onwards, each link configuration + * may be attempted only once as userspace iterates through alternative + * configurations. A successful link training restores the automatic + * retraining model for subsequent failures. + * + * The failure of the last automatic retraining attempt is reported to + * userspace, and from that point onward the driver notifies userspace of + * each subsequent failure. This allows userspace to both initiate + * recovery via modesets and observe the outcome of those recovery + * attempts, even when no further fallback configurations remain. + * + * Link training failures are always reported to userspace, even when they + * result from a kernel-internal modeset. Such modesets only re-apply the + * existing userspace-provided state and must not modify it. A failure + * triggered by such a modeset is therefore treated the same as a link + * degradation after a previously successful training, and recovery is + * handled by userspace in place of the kernel caller. + * + * Contexts + * -------- + * + * The following execution contexts (A/B/C) describe how the different + * recovery states are reached but are not themselves implementation + * states. The actual state machine is defined by &enum + * intel_dp_link_training_recovery_state. + * + * A. Modeset context: + * + * Triggered by: + * - link training during a modeset, or + * - via the "i915_dp_force_link_training_failure" debugfs entry, + * forcing this path by emulating a link training failure. + * + * Transitions: + * - A1 Link training succeeds. + * + * A link check work to recover any degraded link is scheduled + * (and handled if needed in context B). + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * - A2 First link training fails after initialization or a previously + * successful link training. + * + * An automatic retraining work is scheduled (and handled in + * context B) with the same link parameters with which the link + * training failed. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING. + * + * - A3 Link training fails again after A2 or A3. + * + * Through fallback selection, the driver attempts to restrict the + * allowed link configurations for subsequent modesets. This may + * be done either by lowering global limits (rate/lane caps), or by + * disabling only the currently failing configuration while leaving + * all other configurations allowed, even if they use higher rate or + * lane count. + * + * (The current implementation may still apply parameter capping as + * a coarse fallback selection mechanism. This is transitional and is + * expected to be replaced by a scheme that disables only the failing + * configuration, rather than removing configurations that have not + * been observed to fail and may still train successfully.) + * + * This case may repeat in a loop: + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED -> + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * via repeated A3a -> A3a transitions until the configuration fallback + * space is exhausted, reaching the A3b terminal case. + * + * - A3a Fallback selection succeeds. + * + * Userspace is notified to retry the modeset. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED. + * + * - A3b Fallback selection fails. + * + * Userspace is notified of the failure and may continue recovery + * by retrying the modeset with the remaining allowed link + * configuration. + * + * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK. + * + * B. Automatic retraining context: + * + * Triggered by: + * - after a successful link training in context A1 followed by + * asynchronous link degradation, or + * - after the first failed link training attempt in context A2, or + * - via the "i915_dp_force_link_retrain" debugfs entry, which may + * bypass normal gating and force this path. + * + * Transitions: + * - B1 ``Autoretrain`` modeset check and link training succeeds. + * + * The case is handled as in A1, scheduling a link check work to + * recover any degraded link. + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * - B2 ``Autoretrain`` modeset check succeeds but link training fails. + * + * - B2a Previously the link degraded asynchronously (current state + * is %INTEL_DP_LINK_RECOVERY_IDLE). + * + * This corresponds to a first failure in a new failure + * sequence and is handled as in A2: an automatic retraining + * attempt is scheduled with the same link parameters. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING. + * + * - B2b Previously a link training failed (current state is + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING). + * + * In non-regular (debug-forced) scenarios this may also be + * reached from + * %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED or + * %INTEL_DP_LINK_RECOVERY_NO_FALLBACK, effectively behaving + * like a userspace-driven recovery attempt. + * + * The failure is handled as in A3, performing a fallback selection: + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED (via A3a). + * + * or + * + * State -> %INTEL_DP_LINK_RECOVERY_NO_FALLBACK (via A3b). + * + * - B3 ``Autoretrain`` modeset check fails (and hence the link training + * cannot be started). + * + * The modeset check may fail, for example, due to external conditions + * such as changed shared link bandwidth, which can make previously + * valid modeset parameters no longer acceptable. + * + * In this case, automatic retraining is disabled without selecting + * a fallback configuration. The driver hands recovery over to + * userspace without modifying the allowed configuration set, so a + * subsequent userspace modeset will retry with the current link + * configuration. Userspace is in a better position to select new + * modeset parameters (e.g. video mode or enabled outputs) that + * satisfy the updated constraints, as the driver is only allowed + * to retry the modeset with the existing userspace-provided modeset + * configuration. + * + * This policy preserves the normal retry model, where a given link + * configuration is attempted twice in the automatic retraining + * flow before being abandoned: after a first link training failure, + * an automatic retraining modeset is performed with the same link + * parameters, and if its atomic check passes, the link training + * itself may either succeed or fail, constituting the second + * attempt. In this case, however, the retry modeset's atomic check + * failed, so no second link training attempt with those parameters + * was performed, and selecting a fallback would cause that + * configuration to be tried only once rather than twice. + * + * The userspace-driven link recovery continues with subsequent + * userspace modesets handled in A3. + * + * State -> %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED. + * + * C. State reset context: + * + * Triggered by: + * - sink capability changes, or + * - sink disconnect/reconnect, or + * - system suspend/resume or power transitions where HPD + * handling may have been suppressed, or + * - successful link training. + * + * Transitions: + * - The recovery state is reset from any of the recovery states + * + * State -> %INTEL_DP_LINK_RECOVERY_IDLE. + * + * After reset, the driver may re-check link status and schedule + * retraining if the link is found to remain degraded. + * + * State transition summary + * ------------------------ + * + * - From %INTEL_DP_LINK_RECOVERY_IDLE + * + * - To %INTEL_DP_LINK_RECOVERY_IDLE + * + * - | In context: B1 + * | Action: no action + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING + * + * - | In contexts: A2, B2a + * | Action: queue ``autoretrain`` work + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In context: B3 + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In contexts: A3a, B2b + * | Action: select fallback configurations, notify userspace + * + * - | In context: B3 + * | Action: notify userspace + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b, B2b + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - To %INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED + * + * - | In contexts: A3a, B2b + * | Action: select fallback configurations, notify userspace + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b, B2b + * | Action: notify userspace + * + * - From %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - To %INTEL_DP_LINK_RECOVERY_NO_FALLBACK + * + * - | In contexts: A3b + * | Action: notify userspace + * + * - From any state + * + * - To %INTEL_DP_LINK_RECOVERY_IDLE + * + * - | In contexts: C + * | Action: no action + * + * Recovery flows + * -------------- + * + * Userspace modeset link recovery:: + * + * [IDLE] + * | + * | userspace modeset link training fails + * | (autoretrain link recovery work scheduled) + * v + * [AUTORETRAIN_PENDING]-- autoretrain link recovery succeeds -> [IDLE] + * | + * | autoretrain link recovery modeset check or link training fails + * | + * +--o--+ + * modeset check fails | | link training fails + * (userspace notified) | | + * | o-------- no fallback (userspace notified) ---> [NO_FALLBACK] + * | | + * +-------------+ | | fallback selected (userspace notified) + * | | | | + * | v v v + * | [AUTORETRAIN_DISABLED]--- userspace link recovery succeeds ----> [IDLE] + * | | + * | | userspace link recovery fails + * | | + * +-------------------o------------- no fallback (userspace notified) ----> [NO_FALLBACK] + * fallback selected + * (userspace notified) + * + * Asynchronous link degradation recovery:: + * + * [IDLE] + * | + * | link degrades + * | (autoretrain link recovery performed) + * | + * o--- autoretrain link recovery succeeds ---> [IDLE] + * | + * | autoretrain link recovery modeset check or link training fails + * | + * +--o--+ + * modeset check fails | | link training fails + * (userspace notified) | | (autoretrain work scheduled) + * v v + * [AUTORETRAIN_DISABLED*] [AUTORETRAIN_PENDING*] + * + * ``*`` marks states where the sequence continues from the corresponding state + * in the Userspace modeset link recovery flow above. + * + */ + #define LT_MSG_PREFIX "[CONNECTOR:%d:%s][ENCODER:%d:%s][%s] " #define LT_MSG_ARGS(_intel_dp, _dp_phy) (_intel_dp)->attached_connector->base.base.id, \ (_intel_dp)->attached_connector->base.name, \ @@ -60,7 +386,72 @@ lt_dbg(_intel_dp, _dp_phy, "Sink disconnected: " _format, ## __VA_ARGS__); \ } while (0) -#define MAX_SEQ_TRAIN_FAILURES 2 +/* + * enum intel_dp_link_recovery_state - LT recovery state + * @INTEL_DP_LINK_RECOVERY_IDLE: + * No link training failure is currently tracked and no recovery is + * in progress. This is the initial state after driver initialization, + * power state transitions, sink (re-)connection, or after a successful + * link training. + * + * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + * A first link training failure has been observed and an automatic + * retraining attempt with the same link parameters is pending. Exactly + * one such attempt is allowed before switching to userspace-driven + * recovery. + * + * @INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED: + * Automatic retraining is no longer possible. At this point, a + * fallback selection is made and userspace is notified to take over + * recovery, performing modesets with parameters it determines are + * required. The driver then selects a link configuration from the + * remaining fallback configuration set. Subsequent link training + * failures trigger further fallback selections and userspace + * notifications. + * + * @INTEL_DP_LINK_RECOVERY_NO_FALLBACK: + * Fallback selection is no longer possible, as no usable fallback link + * configurations remain. Recovery must proceed via userspace modesets + * using the remaining allowed link configuration. Userspace continues + * to be notified of subsequent link training failures. + * + * Describes the link recovery state used by the Intel DP link recovery + * logic. + * + * See also: + * - DOC: DisplayPort link training + * - link_recovery_autoretrain_pending() + * - link_recovery_autoretrain_allowed() + * - link_recovery_has_no_fallback() + * - link_recovery_mark_train_failure() + * - link_recovery_mark_autoretrain_modeset_failure() + * - link_recovery_mark_no_fallback() + * - link_recovery_reset() + */ +enum intel_dp_link_recovery_state { + /* + * Keep the enum values ordered from least to most severe + * recovery state; helper logic relies on that ordering. + */ + INTEL_DP_LINK_RECOVERY_IDLE, + INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING, + INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED, + INTEL_DP_LINK_RECOVERY_NO_FALLBACK, +}; + +struct intel_dp_link_training { + struct intel_dp *dp; + + enum intel_dp_link_recovery_state recovery_state; + + int force_train_failure; + bool force_retrain; +}; + +static struct intel_dp_link_training *connector_to_link_training(struct intel_connector *connector) +{ + return intel_attached_dp(connector)->link.training; +} static void intel_dp_reset_lttpr_common_caps(struct intel_dp *intel_dp) { @@ -245,12 +636,12 @@ int intel_dp_read_dprx_caps(struct intel_dp *intel_dp, u8 dpcd[DP_RECEIVER_CAP_S * transparent mode link training mode. * * Returns: - * >0 if LTTPRs were detected and the non-transparent LT mode was set. The + * - >0 if LTTPRs were detected and the non-transparent LT mode was + * set. The DPRX capabilities are read out. + * - 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of + * a detection failure and the transparent LT mode was set. The * DPRX capabilities are read out. - * 0 if no LTTPRs or more than 8 LTTPRs were detected or in case of a - * detection failure and the transparent LT mode was set. The DPRX - * capabilities are read out. - * <0 Reading out the DPRX capabilities failed. + * - <0 Reading out the DPRX capabilities failed. */ int intel_dp_init_lttpr_and_dprx_caps(struct intel_dp *intel_dp) { @@ -1247,6 +1638,124 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp, return sink_status & DP_INTRA_HOP_AUX_REPLY_INDICATION ? 1 : 0; } +static bool +link_recovery_autoretrain_pending(struct intel_dp_link_training *link_training) +{ + return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING; +} + +/* + * Automatic retraining is a driver-driven link recovery mechanism that + * retrains the link with the current userspace provided modeset + * configuration and link parameters. + * + * Autoretrain is allowed while the link configurations available for + * retraining, i.e. those not disabled yet via fallback selection, still + * make it possible to retrain the link for the current userspace provided + * modeset configuration. + * + * Once automatic retraining is no longer allowed, userspace driven link + * recovery via userspace notifications and userspace modesets takes over. + * + * See also: + * - DOC: DisplayPort link training + */ +static bool +link_recovery_autoretrain_allowed(struct intel_dp_link_training *link_training) +{ + switch (link_training->recovery_state) { + case INTEL_DP_LINK_RECOVERY_IDLE: + case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + return true; + default: + return false; + } +} + +static bool +link_recovery_has_no_fallback(struct intel_dp_link_training *link_training) +{ + return link_training->recovery_state == INTEL_DP_LINK_RECOVERY_NO_FALLBACK; +} + +/* + * Record a link training failure and advance the recovery state to + * indicate the next required recovery step. + * + * The caller must proceed with recovery as instructed by the return + * value, either via automatic retraining or, once automatic retraining + * is no longer possible, via userspace modesets after fallback + * selection. + * + * Note that the error reported via this function is the error seen by + * the link training failure handler proper after an actual link + * training failure indicated by the sink device, and so the error and + * corresponding actions required are distinct from an autoretrain + * modeset failure. See link_recovery_mark_autoretrain_modeset_failure() to + * report a modeset failure. + * + * See also: + * - DOC: DisplayPort link training + */ +static bool +link_recovery_mark_train_failure(struct intel_dp_link_training *link_training) +{ + switch (link_training->recovery_state) { + case INTEL_DP_LINK_RECOVERY_IDLE: + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING; + break; + case INTEL_DP_LINK_RECOVERY_AUTORETRAIN_PENDING: + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED; + break; + default: + break; + } + + return link_recovery_autoretrain_allowed(link_training); +} + +/* + * Record a failure of the autoretrain modeset before link training + * itself could run. + * + * Note that the error reported via this function and the corresponding + * expected actions are distinct from an actual link training failure: + * the modeset failed before a link training attempt could be performed. + * See link_recovery_mark_train_failure() to report an actual link + * training failure. + * + * Update the state to indicate that further recovery is to be delegated to + * userspace via a regular modeset. + * + * See also: + * - DOC: DisplayPort link training + */ +static void +link_recovery_mark_autoretrain_modeset_failure(struct intel_dp_link_training *link_training) +{ + if (link_recovery_autoretrain_allowed(link_training)) + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_AUTORETRAIN_DISABLED; +} + +/* Record that no more link fallback configuration is available. */ +static void +link_recovery_mark_no_fallback(struct intel_dp_link_training *link_training) +{ + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_NO_FALLBACK; +} + +/** + * link_recovery_reset - reset the link recovery state + * @link_training: link training state + * + * Reset the link recovery state to indicate that no link recovery is + * required. + */ +static void link_recovery_reset(struct intel_dp_link_training *link_training) +{ + link_training->recovery_state = INTEL_DP_LINK_RECOVERY_IDLE; +} + /** * intel_dp_stop_link_train - stop link training * @intel_dp: DP struct @@ -1266,6 +1775,7 @@ intel_dp_128b132b_intra_hop(struct intel_dp *intel_dp, void intel_dp_stop_link_train(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { + struct intel_dp_link_training *link_training = intel_dp->link.training; struct intel_display *display = to_intel_display(intel_dp); struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; int ret; @@ -1286,8 +1796,8 @@ void intel_dp_stop_link_train(struct intel_dp *intel_dp, intel_hpd_unblock(encoder); if (!display->hotplug.ignore_long_hpd && - intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES) { - int delay_ms = intel_dp->link.seq_train_failures ? 0 : 2000; + link_recovery_autoretrain_allowed(link_training)) { + int delay_ms = link_recovery_autoretrain_pending(link_training) ? 0 : 2000; intel_encoder_link_check_queue_work(encoder, delay_ms); } @@ -1340,18 +1850,23 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, int *new_link_rate, int *new_lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int link_rate; int lane_count; int i; - i = intel_dp_link_config_index(intel_dp, crtc_state->port_clock, crtc_state->lane_count); + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + + i = intel_dp_link_config_index(intel_dp->link.caps, + crtc_state->port_clock, crtc_state->lane_count); for (i--; i >= 0; i--) { - intel_dp_link_config_get(intel_dp, i, &link_rate, &lane_count); + intel_dp_link_config_get(intel_dp->link.caps, i, &link_rate, &lane_count); - if ((intel_dp->link.force_rate && - intel_dp->link.force_rate != link_rate) || - (intel_dp->link.force_lane_count && - intel_dp->link.force_lane_count != lane_count)) + if ((forced_params.rate && + forced_params.rate != link_rate) || + (forced_params.lane_count && + forced_params.lane_count != lane_count)) continue; break; @@ -1368,20 +1883,22 @@ static bool reduce_link_params_in_bw_order(struct intel_dp *intel_dp, static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config forced_params; int rate_index; int new_rate; - if (intel_dp->link.force_rate) + intel_dp_link_caps_get_forced_params(link_caps, &forced_params); + if (forced_params.rate) return -1; - rate_index = intel_dp_rate_index(intel_dp->common_rates, - intel_dp->num_common_rates, - current_rate); + rate_index = intel_dp_link_caps_common_rate_idx(link_caps, + current_rate); if (rate_index <= 0) return -1; - new_rate = intel_dp_common_rate(intel_dp, rate_index - 1); + new_rate = intel_dp_common_rate(link_caps, rate_index - 1); /* TODO: Make switching from UHBR to non-UHBR rates work. */ if (drm_dp_is_uhbr_rate(current_rate) != drm_dp_is_uhbr_rate(new_rate)) @@ -1392,7 +1909,10 @@ static int reduce_link_rate(struct intel_dp *intel_dp, int current_rate) static int reduce_lane_count(struct intel_dp *intel_dp, int current_lane_count) { - if (intel_dp->link.force_lane_count) + struct intel_dp_link_config forced_params; + + intel_dp_link_caps_get_forced_params(intel_dp->link.caps, &forced_params); + if (forced_params.lane_count) return -1; if (current_lane_count == 1) @@ -1405,6 +1925,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state, int *new_link_rate, int *new_lane_count) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; int link_rate; int lane_count; @@ -1412,7 +1933,7 @@ static bool reduce_link_params_in_rate_lane_order(struct intel_dp *intel_dp, link_rate = reduce_link_rate(intel_dp, crtc_state->port_clock); if (link_rate < 0) { lane_count = reduce_lane_count(intel_dp, crtc_state->lane_count); - link_rate = intel_dp_max_common_rate(intel_dp); + link_rate = intel_dp_max_common_rate(link_caps); } if (lane_count < 0) @@ -1439,6 +1960,8 @@ static bool reduce_link_params(struct intel_dp *intel_dp, const struct intel_crt static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + struct intel_dp_link_config max_link_limits; int new_link_rate; int new_lane_count; @@ -1464,8 +1987,11 @@ static int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, crtc_state->lane_count, crtc_state->port_clock, new_lane_count, new_link_rate); - intel_dp->link.max_rate = new_link_rate; - intel_dp->link.max_lane_count = new_lane_count; + max_link_limits.rate = new_link_rate; + max_link_limits.lane_count = new_lane_count; + + /* TODO: handle an update failure */ + intel_dp_link_caps_set_max_limits(link_caps, &max_link_limits); return 0; } @@ -1780,6 +2306,9 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, struct intel_display *display = to_intel_display(state); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); struct intel_encoder *encoder = &dig_port->base; + struct intel_dp_link_training *link_training = + intel_dp->link.training; + bool autoretrain_allowed; bool passed; /* * Reinit the LTTPRs here to ensure that they are switched to @@ -1803,15 +2332,15 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, else passed = intel_dp_link_train_all_phys(intel_dp, crtc_state, lttpr_count); - if (intel_dp->link.force_train_failure) { - intel_dp->link.force_train_failure--; + if (link_training->force_train_failure) { + link_training->force_train_failure--; lt_dbg(intel_dp, DP_PHY_DPRX, "Forcing link training failure\n"); } else if (passed) { - intel_dp->link.seq_train_failures = 0; + link_recovery_reset(link_training); return; } - intel_dp->link.seq_train_failures++; + autoretrain_allowed = link_recovery_mark_train_failure(link_training); /* * Ignore the link failure in CI @@ -1830,13 +2359,13 @@ void intel_dp_start_link_train(struct intel_atomic_state *state, return; } - if (intel_dp->link.seq_train_failures < MAX_SEQ_TRAIN_FAILURES) + if (autoretrain_allowed) return; if (intel_dp_schedule_fallback_link_training(state, intel_dp, crtc_state)) return; - intel_dp->link.retrain_disabled = true; + link_recovery_mark_no_fallback(link_training); if (!passed) lt_err(intel_dp, DP_PHY_DPRX, "Can't reduce link training parameters after failure\n"); @@ -1864,256 +2393,307 @@ void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, lt_dbg(intel_dp, DP_PHY_DPRX, "DP2.0 SDP CRC16 for 128b/132b enabled\n"); } -static int i915_dp_force_link_rate_show(struct seq_file *m, void *data) +bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, + u8 lane_count) { - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int current_rate = -1; - int force_rate; - int err; - int i; - - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; - - if (intel_dp->link.active) - current_rate = intel_dp->link_rate; - force_rate = intel_dp->link.force_rate; - - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + struct intel_dp_link_config max_link_limits; - seq_printf(m, "%sauto%s", - force_rate == 0 ? "[" : "", - force_rate == 0 ? "]" : ""); + /* + * FIXME: we need to synchronize the current link parameters with + * hardware readout. Currently fast link training doesn't work on + * boot-up. + * + * NOTE: + * This may be called from both serialized (locked and synced against + * async commit tails) and unserialized (e.g. HPD IRQ) contexts. It + * uses the current max link limits as upper bounds to reject + * obviously bogus values, even if those bounds may be observed in a + * transient or slightly stale state. + * + * This is not a full validation of the link configuration. Even in + * serialized contexts, additional constraints (e.g. source limitations, + * bandwidth checks, and other atomic state dependencies) are only + * verified during the atomic check of the subsequent commit. + * + * max_link_limits only provides independent upper bounds for rate and + * lane count. Callers must not assume it is itself an allowed link + * configuration. Although that happens to be true for now, it will + * stop being guaranteed once fallback depends only on disabled configs. + */ + intel_dp_link_caps_get_max_limits(intel_dp->link.caps, &max_link_limits); - for (i = 0; i < intel_dp->num_source_rates; i++) - seq_printf(m, " %s%d%s%s", - intel_dp->source_rates[i] == force_rate ? "[" : "", - intel_dp->source_rates[i], - intel_dp->source_rates[i] == current_rate ? "*" : "", - intel_dp->source_rates[i] == force_rate ? "]" : ""); + if (link_rate == 0 || + link_rate > max_link_limits.rate) + return false; - seq_putc(m, '\n'); + if (lane_count == 0 || + lane_count > max_link_limits.lane_count) + return false; - return 0; + return true; } -static int parse_link_rate(struct intel_dp *intel_dp, const char __user *ubuf, size_t len) +static bool intel_dp_link_ok(struct intel_dp *intel_dp, + u8 link_status[DP_LINK_STATUS_SIZE]) { - char *kbuf; - const char *p; - int rate; - int ret = 0; - - kbuf = memdup_user_nul(ubuf, len); - if (IS_ERR(kbuf)) - return PTR_ERR(kbuf); + struct intel_display *display = to_intel_display(intel_dp); + struct intel_encoder *encoder = &dp_to_dig_port(intel_dp)->base; + bool uhbr = intel_dp->link_rate >= 1000000; + bool ok; - p = strim(kbuf); + if (uhbr) + ok = drm_dp_128b132b_lane_channel_eq_done(link_status, + intel_dp->lane_count); + else + ok = drm_dp_channel_eq_ok(link_status, intel_dp->lane_count); - if (!strcmp(p, "auto")) { - rate = 0; - } else { - ret = kstrtoint(p, 0, &rate); - if (ret < 0) - goto out_free; - - if (intel_dp_rate_index(intel_dp->source_rates, - intel_dp->num_source_rates, - rate) < 0) - ret = -EINVAL; - } + if (ok) + return true; -out_free: - kfree(kbuf); + intel_dp_dump_link_status(intel_dp, DP_PHY_DPRX, link_status); + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] %s link not ok, retraining\n", + encoder->base.base.id, encoder->base.name, + uhbr ? "128b/132b" : "8b/10b"); - return ret < 0 ? ret : rate; + return false; } -static ssize_t i915_dp_force_link_rate_write(struct file *file, - const char __user *ubuf, - size_t len, loff_t *offp) +static int +intel_dp_read_link_status(struct intel_dp *intel_dp, u8 link_status[DP_LINK_STATUS_SIZE]) { - struct seq_file *m = file->private_data; - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int rate; int err; - rate = parse_link_rate(intel_dp, ubuf, len); - if (rate < 0) - return rate; + memset(link_status, 0, DP_LINK_STATUS_SIZE); + + if (intel_dp_mst_active_streams(intel_dp) > 0) + err = drm_dp_dpcd_read_data(&intel_dp->aux, DP_LANE0_1_STATUS_ESI, + link_status, DP_LINK_STATUS_SIZE - 2); + else + err = drm_dp_dpcd_read_phy_link_status(&intel_dp->aux, DP_PHY_DPRX, + link_status); - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_rate = rate; + if (link_status[DP_LANE_ALIGN_STATUS_UPDATED - DP_LANE0_1_STATUS] & + DP_DOWNSTREAM_PORT_STATUS_CHANGED) + WRITE_ONCE(intel_dp->downstream_port_changed, true); - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + return 0; +} - *offp += len; +bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training) +{ + return link_training->force_retrain; +} - return len; +static void intel_dp_link_training_set_force_retrain(struct intel_dp_link_training *link_training, + bool forced) +{ + link_training->force_retrain = forced; } -DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_link_rate); -static int i915_dp_force_lane_count_show(struct seq_file *m, void *data) +static bool +intel_dp_needs_link_retrain(struct intel_dp *intel_dp) { - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int current_lane_count = -1; - int force_lane_count; - int err; - int i; + struct intel_dp_link_training *link_training = intel_dp->link.training; + u8 link_status[DP_LINK_STATUS_SIZE]; - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + if (!intel_dp->link.active) + return false; - if (intel_dp->link.active) - current_lane_count = intel_dp->lane_count; - force_lane_count = intel_dp->link.force_lane_count; + /* + * While PSR source HW is enabled, it will control main-link sending + * frames, enabling and disabling it so trying to do a retrain will fail + * as the link would or not be on or it could mix training patterns + * and frame data at the same time causing retrain to fail. + * Also when exiting PSR, HW will retrain the link anyways fixing + * any link status error. + */ + if (intel_psr_enabled(intel_dp)) + return false; - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + if (intel_dp_link_training_get_force_retrain(link_training)) + return true; - seq_printf(m, "%sauto%s", - force_lane_count == 0 ? "[" : "", - force_lane_count == 0 ? "]" : ""); + if (intel_dp_read_link_status(intel_dp, link_status) < 0) + return false; + + /* + * Validate the cached values of intel_dp->link_rate and + * intel_dp->lane_count before attempting to retrain. + * + * FIXME would be nice to user the crtc state here, but since + * we need to call this from the short HPD handler that seems + * a bit hard. + */ + if (!intel_dp_link_params_valid(intel_dp, intel_dp->link_rate, + intel_dp->lane_count)) + return false; - for (i = 1; i <= 4; i <<= 1) - seq_printf(m, " %s%d%s%s", - i == force_lane_count ? "[" : "", - i, - i == current_lane_count ? "*" : "", - i == force_lane_count ? "]" : ""); + if (!link_recovery_autoretrain_allowed(link_training)) + return false; - seq_putc(m, '\n'); + if (link_recovery_autoretrain_pending(link_training)) + return true; - return 0; + /* Retrain if link not ok */ + return !intel_dp_link_ok(intel_dp, link_status) && + !intel_psr_link_ok(intel_dp); } -static int parse_lane_count(const char __user *ubuf, size_t len) +static bool intel_dp_is_connected(struct intel_dp *intel_dp) { - char *kbuf; - const char *p; - int lane_count; - int ret = 0; + struct intel_connector *connector = intel_dp->attached_connector; - kbuf = memdup_user_nul(ubuf, len); - if (IS_ERR(kbuf)) - return PTR_ERR(kbuf); - - p = strim(kbuf); + return connector->base.status == connector_status_connected || + intel_dp->is_mst; +} - if (!strcmp(p, "auto")) { - lane_count = 0; - } else { - ret = kstrtoint(p, 0, &lane_count); - if (ret < 0) - goto out_free; - - switch (lane_count) { - case 1: - case 2: - case 4: - break; - default: - ret = -EINVAL; - } - } +static void queue_modeset_retry_for_links_in_state(struct intel_atomic_state *state, + struct intel_encoder *encoder, + u8 pipe_mask) +{ + const struct intel_crtc_state *crtc_state; + struct intel_crtc *crtc; -out_free: - kfree(kbuf); + for_each_new_intel_crtc_in_state(state, crtc, crtc_state) { + if (!(BIT(crtc->pipe) & pipe_mask)) + continue; - return ret < 0 ? ret : lane_count; + intel_dp_queue_modeset_retry_for_link(state, encoder, crtc_state); + } } -static ssize_t i915_dp_force_lane_count_write(struct file *file, - const char __user *ubuf, - size_t len, loff_t *offp) +static int intel_dp_retrain_link(struct intel_encoder *encoder, + struct drm_modeset_acquire_ctx *ctx) { - struct seq_file *m = file->private_data; - struct intel_connector *connector = to_intel_connector(m->private); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int lane_count; - int err; + struct intel_display *display = to_intel_display(encoder); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_dp_link_training *link_training = + intel_dp->link.training; + struct intel_atomic_state *state; + struct drm_atomic_commit *_state; + u8 pipe_mask; + int ret; - lane_count = parse_lane_count(ubuf, len); - if (lane_count < 0) - return lane_count; + if (!intel_dp_is_connected(intel_dp)) + return 0; - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + ret = drm_modeset_lock(&display->drm->mode_config.connection_mutex, + ctx); + if (ret) + return ret; - intel_dp_reset_link_params(intel_dp); - intel_dp->link.force_lane_count = lane_count; + if (!intel_dp_needs_link_retrain(intel_dp)) + return 0; - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); + if (ret) + return ret; - *offp += len; + if (pipe_mask == 0) + return 0; - return len; -} -DEFINE_SHOW_STORE_ATTRIBUTE(i915_dp_force_lane_count); + if (!intel_dp_needs_link_retrain(intel_dp)) + return 0; -static int i915_dp_max_link_rate_show(void *data, u64 *val) -{ - struct intel_connector *connector = to_intel_connector(data); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int err; + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] retraining link (forced %s)\n", + encoder->base.base.id, encoder->base.name, + str_yes_no(intel_dp_link_training_get_force_retrain(link_training))); - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + _state = drm_atomic_commit_alloc(display->drm); + if (!_state) + return -ENOMEM; - *val = intel_dp->link.max_rate; + state = to_intel_atomic_state(_state); - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + ret = intel_modeset_commit_pipes_for_atomic_state(state, pipe_mask, ctx); + if (ret == -EDEADLK) + goto out; - return 0; + intel_dp_link_training_set_force_retrain(link_training, false); + + if (ret) { + drm_dbg_kms(display->drm, + "[ENCODER:%d:%s] link retraining failed: %pe\n", + encoder->base.base.id, encoder->base.name, + ERR_PTR(ret)); + /* + * intel_dp_needs_link_retrain() only performs a coarse check of + * retrainability, so the modeset commit may still fail. Disable + * further auto-retrain attempts in that case. + * + * A sink capability change may restore the retrainable state (see + * intel_dp_update_sink_caps(), intel_dp_reset_link_params()), + * allowing retraining to be attempted again. + */ + link_recovery_mark_autoretrain_modeset_failure(link_training); + queue_modeset_retry_for_links_in_state(state, encoder, pipe_mask); + } +out: + drm_atomic_commit_put(&state->base); + + return ret; } -DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_link_rate_fops, i915_dp_max_link_rate_show, NULL, "%llu\n"); -static int i915_dp_max_lane_count_show(void *data, u64 *val) +void intel_dp_link_check(struct intel_encoder *encoder) { - struct intel_connector *connector = to_intel_connector(data); - struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); - int err; + struct drm_modeset_acquire_ctx ctx; + int ret; - err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); - if (err) - return err; + intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) + ret = intel_dp_retrain_link(encoder, &ctx); +} - *val = intel_dp->link.max_lane_count; +void intel_dp_check_link_state(struct intel_dp *intel_dp) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &dig_port->base; - drm_modeset_unlock(&display->drm->mode_config.connection_mutex); + if (!intel_dp_is_connected(intel_dp)) + return; - return 0; + /* + * NOTE: + * This may race with an ongoing modeset updating the max link limits + * and, with that, the link's retrainability, so + * intel_dp_needs_link_retrain() may observe stale state. + * + * This is harmless: stale params captured as valid may spuriously + * allow retraining here, but the decision is rechecked later in a + * properly serialized context. + * + * Conversely, stale params captured as invalid may skip retraining, + * but that can only happen before the modeset has completed its own + * link training for the new, valid configuration, after which the + * link state is rechecked. + * + * See intel_dp_link_params_valid() for capturing and validating the + * params. + */ + if (!intel_dp_needs_link_retrain(intel_dp)) + return; + + intel_encoder_link_check_queue_work(encoder, 0); } -DEFINE_DEBUGFS_ATTRIBUTE(i915_dp_max_lane_count_fops, i915_dp_max_lane_count_show, NULL, "%llu\n"); static int i915_dp_force_link_training_failure_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - *val = intel_dp->link.force_train_failure; + intel_dp_flush_connector_commits(connector); + + *val = link_training->force_train_failure; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2124,7 +2704,7 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; if (val > 2) @@ -2134,7 +2714,9 @@ static int i915_dp_force_link_training_failure_write(void *data, u64 val) if (err) return err; - intel_dp->link.force_train_failure = val; + intel_dp_flush_connector_commits(connector); + + link_training->force_train_failure = val; drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2148,14 +2730,16 @@ static int i915_dp_force_link_retrain_show(void *data, u64 *val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - *val = intel_dp->link.force_retrain; + intel_dp_flush_connector_commits(connector); + + *val = intel_dp_link_training_get_force_retrain(link_training); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2166,14 +2750,17 @@ static int i915_dp_force_link_retrain_write(void *data, u64 val) { struct intel_connector *connector = to_intel_connector(data); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); + struct intel_dp *intel_dp = link_training->dp; int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - intel_dp->link.force_retrain = val; + intel_dp_flush_connector_commits(connector); + + intel_dp_link_training_set_force_retrain(link_training, val); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2189,14 +2776,17 @@ static int i915_dp_link_retrain_disabled_show(struct seq_file *m, void *data) { struct intel_connector *connector = to_intel_connector(m->private); struct intel_display *display = to_intel_display(connector); - struct intel_dp *intel_dp = intel_attached_dp(connector); + struct intel_dp_link_training *link_training = connector_to_link_training(connector); int err; err = drm_modeset_lock_single_interruptible(&display->drm->mode_config.connection_mutex); if (err) return err; - seq_printf(m, "%s\n", str_yes_no(intel_dp->link.retrain_disabled)); + intel_dp_flush_connector_commits(connector); + + /* TODO: Expose this via a debugfs entry reflecting what the state represents. */ + seq_printf(m, "%s\n", str_yes_no(link_recovery_has_no_fallback(link_training))); drm_modeset_unlock(&display->drm->mode_config.connection_mutex); @@ -2212,18 +2802,6 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) connector->base.connector_type != DRM_MODE_CONNECTOR_eDP) return; - debugfs_create_file("i915_dp_force_link_rate", 0644, root, - connector, &i915_dp_force_link_rate_fops); - - debugfs_create_file("i915_dp_force_lane_count", 0644, root, - connector, &i915_dp_force_lane_count_fops); - - debugfs_create_file("i915_dp_max_link_rate", 0444, root, - connector, &i915_dp_max_link_rate_fops); - - debugfs_create_file("i915_dp_max_lane_count", 0444, root, - connector, &i915_dp_max_lane_count_fops); - debugfs_create_file("i915_dp_force_link_training_failure", 0644, root, connector, &i915_dp_force_link_training_failure_fops); @@ -2233,3 +2811,26 @@ void intel_dp_link_training_debugfs_add(struct intel_connector *connector) debugfs_create_file("i915_dp_link_retrain_disabled", 0444, root, connector, &i915_dp_link_retrain_disabled_fops); } + +void intel_dp_link_training_reset(struct intel_dp_link_training *link_training) +{ + link_recovery_reset(link_training); +} + +struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp) +{ + struct intel_dp_link_training *link_training; + + link_training = kzalloc_obj(*link_training); + if (!link_training) + return NULL; + + link_training->dp = intel_dp; + + return link_training; +} + +void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training) +{ + kfree(link_training); +} 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 18c34c1a472f..ef16fcabd6da 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.h +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.h @@ -12,6 +12,8 @@ struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; struct intel_dp; +struct intel_dp_link_training; +struct intel_encoder; 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); @@ -54,6 +56,19 @@ static inline u8 intel_dp_training_pattern_symbol(u8 pattern) void intel_dp_128b132b_sdp_crc16(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state); +bool intel_dp_link_params_valid(struct intel_dp *intel_dp, int link_rate, + u8 lane_count); + +bool intel_dp_link_training_get_force_retrain(struct intel_dp_link_training *link_training); + +void intel_dp_link_check(struct intel_encoder *encoder); +void intel_dp_check_link_state(struct intel_dp *intel_dp); + void intel_dp_link_training_debugfs_add(struct intel_connector *connector); +void intel_dp_link_training_reset(struct intel_dp_link_training *link_training); + +struct intel_dp_link_training *intel_dp_link_training_init(struct intel_dp *intel_dp); +void intel_dp_link_training_cleanup(struct intel_dp_link_training *link_training); + #endif /* __INTEL_DP_LINK_TRAINING_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 0aa3e6b4c781..ecc90e8faee1 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -697,12 +697,12 @@ static int mst_stream_compute_link_for_joined_pipes(struct intel_encoder *encode return 0; } -static int mst_stream_compute_config(struct intel_encoder *encoder, +static int mst_stream_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct intel_dp *intel_dp = to_primary_dp(encoder); struct intel_connector *connector = @@ -925,11 +925,11 @@ int intel_dp_mst_atomic_check_link(struct intel_atomic_state *state, return 0; } -static int mst_stream_compute_config_late(struct intel_encoder *encoder, +static int mst_stream_compute_config_late(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { - struct intel_atomic_state *state = to_intel_atomic_state(conn_state->state); struct intel_dp *intel_dp = to_primary_dp(encoder); /* lowest numbered transcoder will be designated master */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_test.c b/drivers/gpu/drm/i915/display/intel_dp_test.c index ba44769c9cfb..da7632536dac 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_test.c +++ b/drivers/gpu/drm/i915/display/intel_dp_test.c @@ -14,6 +14,7 @@ #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_test.h" @@ -32,6 +33,7 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, struct intel_crtc_state *pipe_config, struct link_config_limits *limits) { + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; struct intel_display *display = to_intel_display(intel_dp); /* For DP Compliance we override the computed bpp for the pipe */ @@ -54,9 +56,8 @@ void intel_dp_test_compute_config(struct intel_dp *intel_dp, */ if (intel_dp_link_params_valid(intel_dp, intel_dp->compliance.test_link_rate, intel_dp->compliance.test_lane_count)) { - index = intel_dp_rate_index(intel_dp->common_rates, - intel_dp->num_common_rates, - intel_dp->compliance.test_link_rate); + index = intel_dp_link_caps_common_rate_idx(link_caps, + intel_dp->compliance.test_link_rate); if (index >= 0) { limits->min_rate = intel_dp->compliance.test_link_rate; limits->max_rate = intel_dp->compliance.test_link_rate; diff --git a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c index d6bd1f7e01e1..76e9753766b9 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_tunnel.c +++ b/drivers/gpu/drm/i915/display/intel_dp_tunnel.c @@ -11,6 +11,7 @@ #include "intel_display_limits.h" #include "intel_display_types.h" #include "intel_dp.h" +#include "intel_dp_link_caps.h" #include "intel_dp_link_training.h" #include "intel_dp_mst.h" #include "intel_dp_tunnel.h" @@ -56,8 +57,9 @@ static int kbytes_to_mbits(int kbytes) static int get_current_link_bw(struct intel_dp *intel_dp) { - int rate = intel_dp_max_common_rate(intel_dp); - int lane_count = intel_dp_max_common_lane_count(intel_dp); + struct intel_dp_link_caps *link_caps = intel_dp->link.caps; + int rate = intel_dp_max_common_rate(link_caps); + int lane_count = intel_dp_link_caps_max_common_lane_count(link_caps); return intel_dp_max_link_data_rate(intel_dp, rate, lane_count); } diff --git a/drivers/gpu/drm/i915/display/intel_dsb.c b/drivers/gpu/drm/i915/display/intel_dsb.c index fec8a56e21ea..d9a270362a82 100644 --- a/drivers/gpu/drm/i915/display/intel_dsb.c +++ b/drivers/gpu/drm/i915/display/intel_dsb.c @@ -210,6 +210,18 @@ static int dsb_scanline_to_hw(struct intel_atomic_state *state, return (scanline + vtotal - intel_crtc_scanline_offset(crtc_state)) % vtotal; } +static +bool pre_commit_use_safe_window(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct intel_display *display = to_intel_display(crtc->base.dev); + + if (intel_vrr_always_use_vrr_tg(display)) + return true; + + return pre_commit_is_vrr_active(state, crtc); +} + /* * Bspec suggests that we should always set DSB_SKIP_WAITS_EN. We have approach * different from what is explained in Bspec on how flip is considered being @@ -229,7 +241,7 @@ static u32 dsb_chicken(struct intel_atomic_state *state, u32 chicken = intel_psr_use_trans_push(new_crtc_state) ? DSB_SKIP_WAITS_EN : 0; - if (pre_commit_is_vrr_active(state, crtc)) + if (pre_commit_use_safe_window(state, crtc)) chicken |= DSB_CTRL_WAIT_SAFE_WINDOW | DSB_CTRL_NO_WAIT_VBLANK | DSB_INST_WAIT_SAFE_WINDOW | @@ -798,6 +810,12 @@ void intel_dsb_vblank_evade(struct intel_atomic_state *state, end = intel_vrr_vmax_vblank_start(crtc_state); start = end - vblank_delay - latency; intel_dsb_wait_scanline_out(state, dsb, start, end); + } else if (pre_commit_use_safe_window(state, crtc)) { + int vblank_delay = crtc_state->set_context_latency; + + end = intel_mode_vblank_start(&crtc_state->hw.adjusted_mode); + start = end - vblank_delay - latency; + intel_dsb_wait_scanline_out(state, dsb, start, end); } else { int vblank_delay = intel_mode_vblank_delay(&crtc_state->hw.adjusted_mode); @@ -891,7 +909,7 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, &crtc_state->hw.adjusted_mode; int wait_scanlines; - if (pre_commit_is_vrr_active(state, crtc)) { + if (pre_commit_use_safe_window(state, crtc)) { /* * If the push happened before the vmin decision boundary * we don't know how far we are from the undelayed vblank. @@ -902,9 +920,17 @@ void intel_dsb_wait_for_delayed_vblank(struct intel_atomic_state *state, * the hardware itself guarantees that we're SCL lines * away from the delayed vblank, and we won't be inside * the vmin safe window so this extra wait does nothing. + * + * Experimentally, DSB may observe a slightly stale + * PIPEDSL value. When the actual scanline has just reached + * safe_window_start, WAIT_DSL_OUT may complete immediately + * due to the stale value. + * + * Shift the start back by one scanline to ensure the wait + * window is entered reliably. */ intel_dsb_wait_scanline_out(state, dsb, - intel_vrr_safe_window_start(crtc_state), + intel_vrr_safe_window_start(crtc_state) - 1, intel_vrr_vmin_safe_window_end(crtc_state)); /* * When the push is sent during vblank it will trigger diff --git a/drivers/gpu/drm/i915/display/intel_dvo.c b/drivers/gpu/drm/i915/display/intel_dvo.c index dd1a995c2979..f157699a7c4c 100644 --- a/drivers/gpu/drm/i915/display/intel_dvo.c +++ b/drivers/gpu/drm/i915/display/intel_dvo.c @@ -242,7 +242,8 @@ intel_dvo_mode_valid(struct drm_connector *_connector, return intel_dvo->dev.dev_ops->mode_valid(&intel_dvo->dev, mode); } -static int intel_dvo_compute_config(struct intel_encoder *encoder, +static int intel_dvo_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -256,7 +257,7 @@ static int intel_dvo_compute_config(struct intel_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.c b/drivers/gpu/drm/i915/display/intel_gmbus.c index 049157c41fe2..60a70dea5d85 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.c +++ b/drivers/gpu/drm/i915/display/intel_gmbus.c @@ -54,21 +54,21 @@ struct intel_gmbus { }; enum gmbus_gpio { - GPIOA, - GPIOB, - GPIOC, - GPIOD, - GPIOE, - GPIOF, - GPIOG, - GPIOH, - __GPIOI_UNUSED, - GPIOJ, - GPIOK, - GPIOL, - GPIOM, - GPION, - GPIOO, + GPIO_0, + GPIO_1, + GPIO_2, + GPIO_3, + GPIO_4, + GPIO_5, + GPIO_6, + GPIO_7, + GPIO_8, + GPIO_9, + GPIO_10, + GPIO_11, + GPIO_12, + GPIO_13, + GPIO_14, }; struct gmbus_pin { @@ -78,77 +78,82 @@ struct gmbus_pin { /* Map gmbus pin pairs to names and registers. */ static const struct gmbus_pin gmbus_pins[] = { - [GMBUS_PIN_SSC] = { "ssc", GPIOB }, - [GMBUS_PIN_VGADDC] = { "vga", GPIOA }, - [GMBUS_PIN_PANEL] = { "panel", GPIOC }, - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, + [GMBUS_PIN_SSC] = { "ssc", GPIO_1 }, + [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, + [GMBUS_PIN_PANEL] = { "panel", GPIO_2 }, + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; -static const struct gmbus_pin gmbus_pins_bdw[] = { - [GMBUS_PIN_VGADDC] = { "vga", GPIOA }, - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, +static const struct gmbus_pin gmbus_pins_lpt_h[] = { + [GMBUS_PIN_VGADDC] = { "vga", GPIO_0 }, + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; -static const struct gmbus_pin gmbus_pins_skl[] = { - [GMBUS_PIN_DPC] = { "dpc", GPIOD }, - [GMBUS_PIN_DPB] = { "dpb", GPIOE }, - [GMBUS_PIN_DPD] = { "dpd", GPIOF }, +static const struct gmbus_pin gmbus_pins_lpt_lp[] = { + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, +}; + +static const struct gmbus_pin gmbus_pins_spt[] = { + [GMBUS_PIN_DPC] = { "dpc", GPIO_3 }, + [GMBUS_PIN_DPB] = { "dpb", GPIO_4 }, + [GMBUS_PIN_DPD] = { "dpd", GPIO_5 }, }; static const struct gmbus_pin gmbus_pins_bxt[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIOC }, - [GMBUS_PIN_3_BXT] = { "misc", GPIOD }, + [GMBUS_PIN_1] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3] = { "misc", GPIO_3 }, }; static const struct gmbus_pin gmbus_pins_cnp[] = { - [GMBUS_PIN_1_BXT] = { "dpb", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpc", GPIOC }, - [GMBUS_PIN_3_BXT] = { "misc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, + [GMBUS_PIN_1] = { "dpb", GPIO_1 }, + [GMBUS_PIN_2] = { "dpc", GPIO_2 }, + [GMBUS_PIN_3] = { "misc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_icp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM }, - [GMBUS_PIN_13_TC5_TGP] = { "tc5", GPION }, - [GMBUS_PIN_14_TC6_TGP] = { "tc6", GPIOO }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 }, + [GMBUS_PIN_13_TC5] = { "tc5", GPIO_13 }, + [GMBUS_PIN_14_TC6] = { "tc6", GPIO_14 }, }; static const struct gmbus_pin gmbus_pins_dg1[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, }; static const struct gmbus_pin gmbus_pins_dg2[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, }; static const struct gmbus_pin gmbus_pins_mtp[] = { - [GMBUS_PIN_1_BXT] = { "dpa", GPIOB }, - [GMBUS_PIN_2_BXT] = { "dpb", GPIOC }, - [GMBUS_PIN_3_BXT] = { "dpc", GPIOD }, - [GMBUS_PIN_4_CNP] = { "dpd", GPIOE }, - [GMBUS_PIN_5_MTP] = { "dpe", GPIOF }, - [GMBUS_PIN_9_TC1_ICP] = { "tc1", GPIOJ }, - [GMBUS_PIN_10_TC2_ICP] = { "tc2", GPIOK }, - [GMBUS_PIN_11_TC3_ICP] = { "tc3", GPIOL }, - [GMBUS_PIN_12_TC4_ICP] = { "tc4", GPIOM }, + [GMBUS_PIN_1] = { "dpa", GPIO_1 }, + [GMBUS_PIN_2] = { "dpb", GPIO_2 }, + [GMBUS_PIN_3] = { "dpc", GPIO_3 }, + [GMBUS_PIN_4] = { "dpd", GPIO_4 }, + [GMBUS_PIN_5] = { "dpe", GPIO_5 }, + [GMBUS_PIN_9_TC1] = { "tc1", GPIO_9 }, + [GMBUS_PIN_10_TC2] = { "tc2", GPIO_10 }, + [GMBUS_PIN_11_TC3] = { "tc3", GPIO_11 }, + [GMBUS_PIN_12_TC4] = { "tc4", GPIO_12 }, }; static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, @@ -175,12 +180,15 @@ static const struct gmbus_pin *get_gmbus_pin(struct intel_display *display, } else if (display->platform.geminilake || display->platform.broxton) { pins = gmbus_pins_bxt; size = ARRAY_SIZE(gmbus_pins_bxt); - } else if (DISPLAY_VER(display) == 9) { - pins = gmbus_pins_skl; - size = ARRAY_SIZE(gmbus_pins_skl); - } else if (display->platform.broadwell) { - pins = gmbus_pins_bdw; - size = ARRAY_SIZE(gmbus_pins_bdw); + } else if (HAS_PCH_SPT(display)) { + pins = gmbus_pins_spt; + size = ARRAY_SIZE(gmbus_pins_spt); + } else if (HAS_PCH_LPT_LP(display)) { + pins = gmbus_pins_lpt_lp; + size = ARRAY_SIZE(gmbus_pins_lpt_lp); + } else if (HAS_PCH_LPT(display)) { + pins = gmbus_pins_lpt_h; + size = ARRAY_SIZE(gmbus_pins_lpt_h); } else { pins = gmbus_pins; size = ARRAY_SIZE(gmbus_pins); diff --git a/drivers/gpu/drm/i915/display/intel_gmbus.h b/drivers/gpu/drm/i915/display/intel_gmbus.h index 35a200a9efc0..5fdeab1aa794 100644 --- a/drivers/gpu/drm/i915/display/intel_gmbus.h +++ b/drivers/gpu/drm/i915/display/intel_gmbus.h @@ -20,17 +20,17 @@ struct intel_display; #define GMBUS_PIN_DPB 5 /* SDVO, HDMIB */ #define GMBUS_PIN_DPD 6 /* HDMID */ #define GMBUS_PIN_RESERVED 7 /* 7 reserved */ -#define GMBUS_PIN_1_BXT 1 /* BXT+ (atom) and CNP+ (big core) */ -#define GMBUS_PIN_2_BXT 2 -#define GMBUS_PIN_3_BXT 3 -#define GMBUS_PIN_4_CNP 4 -#define GMBUS_PIN_5_MTP 5 -#define GMBUS_PIN_9_TC1_ICP 9 -#define GMBUS_PIN_10_TC2_ICP 10 -#define GMBUS_PIN_11_TC3_ICP 11 -#define GMBUS_PIN_12_TC4_ICP 12 -#define GMBUS_PIN_13_TC5_TGP 13 -#define GMBUS_PIN_14_TC6_TGP 14 +#define GMBUS_PIN_1 1 /* BXT+ (atom) and CNP+ (big core) */ +#define GMBUS_PIN_2 2 +#define GMBUS_PIN_3 3 +#define GMBUS_PIN_4 4 +#define GMBUS_PIN_5 5 +#define GMBUS_PIN_9_TC1 9 /* ICP+ */ +#define GMBUS_PIN_10_TC2 10 +#define GMBUS_PIN_11_TC3 11 +#define GMBUS_PIN_12_TC4 12 +#define GMBUS_PIN_13_TC5 13 +#define GMBUS_PIN_14_TC6 14 #define GMBUS_NUM_PINS 15 /* including 0 */ diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 9076c2b176ec..8a019d3574df 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -71,7 +71,7 @@ bool intel_hdmi_is_frl(u32 clock) { - u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 }; + static const u32 rates[] = { 300000, 600000, 800000, 1000000, 1200000 }; int i; for (i = 0; i < ARRAY_SIZE(rates); i++) @@ -2729,6 +2729,32 @@ intel_hdmi_add_properties(struct intel_hdmi *intel_hdmi, struct drm_connector *_ } /* + * HDMI 2.0 spec, section 6.1.3.1 (Scrambling Control): after + * enabling Scrambling_Enable and starting scrambled video + * transmission, poll Scrambling_Status for up to 200 ms. + */ +void +intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector) +{ + struct intel_connector *connector = to_intel_connector(_connector); + struct intel_display *display = to_intel_display(crtc_state); + bool scrambling_enabled = false; + int ret; + + if (!crtc_state->hdmi_scrambling) + return; + + /* Poll for a max of 200 msec as per HDMI spec */ + ret = poll_timeout_us(scrambling_enabled = drm_scdc_get_scrambling_status(&connector->base), + scrambling_enabled, 1000, 200 * 1000, false); + if (ret) + drm_dbg_kms(display->drm, + "[CONNECTOR:%d:%s] Timed out waiting for scrambling enable\n", + connector->base.base.id, connector->base.name); +} + +/* * intel_hdmi_handle_sink_scrambling: handle sink scrambling/clock ratio setup * @encoder: intel_encoder * @connector: drm_connector @@ -2799,14 +2825,14 @@ static u8 bxt_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (port) { case PORT_B: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PORT_C: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; default: MISSING_CASE(port); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2819,20 +2845,17 @@ static u8 cnp_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (port) { case PORT_B: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PORT_C: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; case PORT_D: - ddc_pin = GMBUS_PIN_4_CNP; - break; - case PORT_F: - ddc_pin = GMBUS_PIN_3_BXT; + ddc_pin = GMBUS_PIN_4; break; default: MISSING_CASE(port); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2844,12 +2867,12 @@ static u8 icl_encoder_to_ddc_pin(struct intel_encoder *encoder) enum port port = encoder->port; if (intel_encoder_is_combo(encoder)) - return GMBUS_PIN_1_BXT + port; + return GMBUS_PIN_1 + port; else if (intel_encoder_is_tc(encoder)) - return GMBUS_PIN_9_TC1_ICP + intel_encoder_to_tc(encoder); + return GMBUS_PIN_9_TC1 + intel_encoder_to_tc(encoder); drm_WARN(display->drm, 1, "Unknown port:%c\n", port_name(port)); - return GMBUS_PIN_2_BXT; + return GMBUS_PIN_2; } static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2859,17 +2882,17 @@ static u8 mcc_encoder_to_ddc_pin(struct intel_encoder *encoder) switch (phy) { case PHY_A: - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; case PHY_B: - ddc_pin = GMBUS_PIN_2_BXT; + ddc_pin = GMBUS_PIN_2; break; case PHY_C: - ddc_pin = GMBUS_PIN_9_TC1_ICP; + ddc_pin = GMBUS_PIN_9_TC1; break; default: MISSING_CASE(phy); - ddc_pin = GMBUS_PIN_1_BXT; + ddc_pin = GMBUS_PIN_1; break; } return ddc_pin; @@ -2889,9 +2912,9 @@ static u8 rkl_encoder_to_ddc_pin(struct intel_encoder *encoder) * all outputs. */ if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C) - return GMBUS_PIN_9_TC1_ICP + phy - PHY_C; + return GMBUS_PIN_9_TC1 + phy - PHY_C; - return GMBUS_PIN_1_BXT + phy; + return GMBUS_PIN_1 + phy; } static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2908,9 +2931,9 @@ static u8 gen9bc_tgp_encoder_to_ddc_pin(struct intel_encoder *encoder) * all outputs. */ if (INTEL_PCH_TYPE(display) >= PCH_TGP && phy >= PHY_C) - return GMBUS_PIN_9_TC1_ICP + phy - PHY_C; + return GMBUS_PIN_9_TC1 + phy - PHY_C; - return GMBUS_PIN_1_BXT + phy; + return GMBUS_PIN_1 + phy; } static u8 dg1_encoder_to_ddc_pin(struct intel_encoder *encoder) @@ -2929,9 +2952,9 @@ static u8 adls_encoder_to_ddc_pin(struct intel_encoder *encoder) * except first combo output. */ if (phy == PHY_A) - return GMBUS_PIN_1_BXT; + return GMBUS_PIN_1; - return GMBUS_PIN_9_TC1_ICP + phy - PHY_B; + return GMBUS_PIN_9_TC1 + phy - PHY_B; } static u8 g4x_encoder_to_ddc_pin(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index be2fad57e4ad..0fa3661568e8 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -70,5 +70,7 @@ void hsw_read_infoframe(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, unsigned int type, void *frame, ssize_t len); +void intel_hdmi_poll_for_scrambling_enable(const struct intel_crtc_state *crtc_state, + struct drm_connector *_connector); #endif /* __INTEL_HDMI_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_initial_plane.c b/drivers/gpu/drm/i915/display/intel_initial_plane.c index 6aa253678ec9..e414b5d1085c 100644 --- a/drivers/gpu/drm/i915/display/intel_initial_plane.c +++ b/drivers/gpu/drm/i915/display/intel_initial_plane.c @@ -170,7 +170,7 @@ intel_find_initial_plane_obj(struct intel_crtc *crtc, drm_framebuffer_get(fb); plane_state->uapi.crtc = &crtc->base; - intel_plane_copy_uapi_to_hw_state(plane_state, plane_state, crtc); + intel_plane_copy_uapi_to_hw_state(NULL, plane_state, plane_state, crtc); atomic_or(plane->frontbuffer_bit, &to_intel_frontbuffer(fb)->bits); diff --git a/drivers/gpu/drm/i915/display/intel_lpe_audio.c b/drivers/gpu/drm/i915/display/intel_lpe_audio.c index 775493306a83..ff2cf479d8e1 100644 --- a/drivers/gpu/drm/i915/display/intel_lpe_audio.c +++ b/drivers/gpu/drm/i915/display/intel_lpe_audio.c @@ -262,10 +262,10 @@ void intel_lpe_audio_irq_handler(struct intel_display *display) if (!HAS_LPE_AUDIO(display)) return; - ret = generic_handle_irq(display->audio.lpe.irq); + ret = generic_handle_irq_safe(display->audio.lpe.irq); if (ret) drm_err_ratelimited(display->drm, - "error handling LPE audio irq: %d\n", ret); + "LPE audio: irq handling failed (%pe)\n", ERR_PTR(ret)); } /** diff --git a/drivers/gpu/drm/i915/display/intel_lt_phy.c b/drivers/gpu/drm/i915/display/intel_lt_phy.c index 615ee980470e..8fc6d230493f 100644 --- a/drivers/gpu/drm/i915/display/intel_lt_phy.c +++ b/drivers/gpu/drm/i915/display/intel_lt_phy.c @@ -1223,11 +1223,7 @@ intel_lt_phy_program_port_clock_ctl(struct intel_encoder *encoder, else val |= XELPDP_DDI_CLOCK_SELECT_PREP(display, XELPDP_DDI_CLOCK_SELECT_MAXPCLK); - /* DP2.0 10G and 20G rates enable MPLLA*/ - if (port_clock == 1000000 || port_clock == 2000000) - val |= XELPDP_SSC_ENABLE_PLLA; - else - val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + val |= ltpll->ssc_enabled ? XELPDP_SSC_ENABLE_PLLA : 0; intel_de_rmw(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | @@ -2178,8 +2174,9 @@ void intel_lt_phy_dump_hw_state(struct drm_printer *p, { int i, j; - drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %d, tbt mode: %d\n", - hw_state->lane_count, hw_state->ssc_enabled, hw_state->tbt_mode); + drm_printf(p, "lt_phy_pll_hw_state: lane count: %d, ssc enabled: %s, tbt mode: %s\n", + hw_state->lane_count, str_yes_no(hw_state->ssc_enabled), + str_yes_no(hw_state->tbt_mode)); for (i = 0; i < 3; i++) { drm_printf(p, "config[%d] = 0x%.4x,\n", @@ -2221,6 +2218,14 @@ static bool intel_lt_phy_pll_is_enabled(struct intel_encoder *encoder) XELPDP_LANE_PCLK_PLL_ACK(0); } +static bool readout_ssc_state(struct intel_encoder *encoder) +{ + struct intel_display *display = to_intel_display(encoder); + + return intel_de_read(display, XELPDP_PORT_CLOCK_CTL(display, encoder->port)) & + XELPDP_SSC_ENABLE_PLLA; +} + bool intel_lt_phy_tbt_pll_readout_hw_state(struct intel_display *display, struct intel_dpll *pll, struct intel_dpll_hw_state *hw_state) @@ -2250,6 +2255,7 @@ bool intel_lt_phy_pll_readout_hw_state(struct intel_encoder *encoder, owned_lane_mask = intel_lt_phy_get_owned_lane_mask(encoder); lane = owned_lane_mask & INTEL_LT_PHY_LANE0 ? : INTEL_LT_PHY_LANE1; wakeref = intel_lt_phy_transaction_begin(encoder); + pll_state->ssc_enabled = readout_ssc_state(encoder); pll_state->lane_count = intel_readout_lane_count(encoder, INTEL_LT_PHY_LANE0, INTEL_LT_PHY_LANE1); diff --git a/drivers/gpu/drm/i915/display/intel_lvds.c b/drivers/gpu/drm/i915/display/intel_lvds.c index c8098104d853..872753478cf2 100644 --- a/drivers/gpu/drm/i915/display/intel_lvds.c +++ b/drivers/gpu/drm/i915/display/intel_lvds.c @@ -413,7 +413,8 @@ intel_lvds_mode_valid(struct drm_connector *_connector, return MODE_OK; } -static int intel_lvds_compute_config(struct intel_encoder *encoder, +static int intel_lvds_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state) { @@ -459,7 +460,7 @@ static int intel_lvds_compute_config(struct intel_encoder *encoder, * with the panel scaling set up to source from the H/VDisplay * of the original mode. */ - ret = intel_panel_compute_config(connector, adjusted_mode); + ret = intel_panel_compute_config(state, crtc_state, connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_panel.c b/drivers/gpu/drm/i915/display/intel_panel.c index 20c548eea6da..81e638d0c7b3 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.c +++ b/drivers/gpu/drm/i915/display/intel_panel.c @@ -67,21 +67,62 @@ static bool is_best_fixed_mode(struct intel_connector *connector, if (!best_mode) return true; - /* - * With VRR always pick a mode with equal/higher than requested - * vrefresh, which we can then reduce to match the requested - * vrefresh by extending the vblank length. - */ - if (intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh) && - fixed_mode_vrefresh < vrefresh) - return false; - /* pick the fixed_mode that is closest in terms of vrefresh */ return abs(fixed_mode_vrefresh - vrefresh) < abs(drm_mode_vrefresh(best_mode) - vrefresh); } +static bool is_vrr_compatible(const struct drm_display_mode *mode1, + const struct drm_display_mode *mode2) +{ + return drm_mode_match(mode1, mode2, + DRM_MODE_MATCH_CLOCK | + DRM_MODE_MATCH_TIMINGS_VRR | + DRM_MODE_MATCH_FLAGS | + DRM_MODE_MATCH_3D_FLAGS); +} + +static const struct drm_display_mode * +intel_panel_fixed_mode_vrr(struct intel_connector *connector, + const struct drm_display_mode *mode, + const struct drm_display_mode *vrr_ref_mode) +{ + const struct drm_display_mode *fixed_mode, *best_mode = NULL; + int vrefresh = drm_mode_vrefresh(mode); + + if (!intel_vrr_is_in_range(connector, vrefresh)) + return NULL; + + if (vrr_ref_mode && + !intel_vrr_is_in_range(connector, drm_mode_vrefresh(vrr_ref_mode))) + return NULL; + + list_for_each_entry(fixed_mode, &connector->panel.fixed_modes, head) { + int fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + if (!intel_vrr_is_in_range(connector, fixed_mode_vrefresh)) + continue; + + /* + * With VRR always pick a mode with equal/higher than requested + * vrefresh, which we can then reduce to match the requested + * vrefresh by extending the vblank length. + */ + if (fixed_mode_vrefresh < vrefresh) + continue; + + if (vrr_ref_mode && + !is_vrr_compatible(fixed_mode, vrr_ref_mode)) + continue; + + if (is_best_fixed_mode(connector, vrefresh, + fixed_mode_vrefresh, best_mode)) + best_mode = fixed_mode; + } + + return best_mode; +} + const struct drm_display_mode * intel_panel_fixed_mode(struct intel_connector *connector, const struct drm_display_mode *mode) @@ -197,56 +238,107 @@ enum drrs_type intel_panel_drrs_type(struct intel_connector *connector) return connector->panel.vbt.drrs_type; } -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode) +static int intel_panel_compute_config_vrr(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) { - const struct drm_display_mode *fixed_mode = - intel_panel_fixed_mode(connector, adjusted_mode); + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + const struct drm_display_mode *fixed_mode = NULL; int vrefresh, fixed_mode_vrefresh; - bool is_vrr; + + /* + * Attempt a VRR based refresh rate change if possible + * when userspace has forbidden a full modeset. + */ + if (!state->base.allow_modeset) { + struct intel_crtc *crtc = to_intel_crtc(crtc_state->uapi.crtc); + const struct intel_crtc_state *old_crtc_state = + intel_atomic_get_old_crtc_state(state, crtc); + + if (old_crtc_state->hw.enable && + old_crtc_state->uapi.encoder_mask == crtc_state->uapi.encoder_mask) + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, + &old_crtc_state->hw.adjusted_mode); + } if (!fixed_mode) + fixed_mode = intel_panel_fixed_mode_vrr(connector, adjusted_mode, NULL); + + if (!fixed_mode) + return -EINVAL; + + vrefresh = drm_mode_vrefresh(adjusted_mode); + fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); + + drm_mode_copy(adjusted_mode, fixed_mode); + + if (fixed_mode_vrefresh != vrefresh) { + int vsync_start_offset = adjusted_mode->vtotal - adjusted_mode->vsync_start; + int vsync_end_offset = adjusted_mode->vtotal - adjusted_mode->vsync_end; + + adjusted_mode->vtotal = + DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000, + adjusted_mode->htotal * vrefresh); + + adjusted_mode->vsync_start = adjusted_mode->vtotal - vsync_start_offset; + adjusted_mode->vsync_end = adjusted_mode->vtotal - vsync_end_offset; + } + + drm_mode_set_crtcinfo(adjusted_mode, 0); + + return 0; +} + +static int intel_panel_compute_config_fixed_rr(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) +{ + struct drm_display_mode *adjusted_mode = &crtc_state->hw.adjusted_mode; + const struct drm_display_mode *fixed_mode; + int vrefresh, fixed_mode_vrefresh; + + fixed_mode = intel_panel_fixed_mode(connector, adjusted_mode); + if (!fixed_mode) return 0; vrefresh = drm_mode_vrefresh(adjusted_mode); fixed_mode_vrefresh = drm_mode_vrefresh(fixed_mode); /* - * Assume that we shouldn't muck about with the - * timings if they don't land in the VRR range. + * We don't want to lie too much to the user about the refresh + * rate they're going to get. But we have to allow a bit of latitude + * for Xorg since it likes to automagically cook up modes with slightly + * off refresh rates. */ - is_vrr = intel_vrr_is_in_range(connector, vrefresh) && - intel_vrr_is_in_range(connector, fixed_mode_vrefresh); - - if (!is_vrr) { - /* - * We don't want to lie too much to the user about the refresh - * rate they're going to get. But we have to allow a bit of latitude - * for Xorg since it likes to automagically cook up modes with slightly - * off refresh rates. - */ - if (abs(vrefresh - fixed_mode_vrefresh) > 1) { - drm_dbg_kms(connector->base.dev, - "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", - connector->base.base.id, connector->base.name, - vrefresh, fixed_mode_vrefresh); + if (abs(vrefresh - fixed_mode_vrefresh) > 1) { + drm_dbg_kms(connector->base.dev, + "[CONNECTOR:%d:%s] Requested mode vrefresh (%d Hz) does not match fixed mode vrefresh (%d Hz)\n", + connector->base.base.id, connector->base.name, + vrefresh, fixed_mode_vrefresh); - return -EINVAL; - } + return -EINVAL; } drm_mode_copy(adjusted_mode, fixed_mode); - if (is_vrr && fixed_mode_vrefresh != vrefresh) - adjusted_mode->vtotal = - DIV_ROUND_CLOSEST(adjusted_mode->clock * 1000, - adjusted_mode->htotal * vrefresh); - drm_mode_set_crtcinfo(adjusted_mode, 0); return 0; } +int intel_panel_compute_config(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector) +{ + int ret; + + ret = intel_panel_compute_config_vrr(state, crtc_state, connector); + if (ret) + ret = intel_panel_compute_config_fixed_rr(state, crtc_state, connector); + + return ret; +} + static void intel_panel_add_edid_alt_fixed_modes(struct intel_connector *connector) { struct intel_display *display = to_intel_display(connector); diff --git a/drivers/gpu/drm/i915/display/intel_panel.h b/drivers/gpu/drm/i915/display/intel_panel.h index 23bd227826c9..30c6078ecb1b 100644 --- a/drivers/gpu/drm/i915/display/intel_panel.h +++ b/drivers/gpu/drm/i915/display/intel_panel.h @@ -14,6 +14,7 @@ struct drm_connector; struct drm_connector_state; struct drm_display_mode; struct drm_edid; +struct intel_atomic_state; struct intel_connector; struct intel_crtc_state; struct intel_display; @@ -45,8 +46,9 @@ enum drm_mode_status intel_panel_mode_valid(struct intel_connector *connector, const struct drm_display_mode *mode, int *target_clock); -int intel_panel_compute_config(struct intel_connector *connector, - struct drm_display_mode *adjusted_mode); +int intel_panel_compute_config(struct intel_atomic_state *state, + struct intel_crtc_state *crtc_state, + struct intel_connector *connector); void intel_panel_add_edid_fixed_modes(struct intel_connector *connector, bool use_alt_fixed_modes); void intel_panel_add_vbt_lfp_fixed_mode(struct intel_connector *connector); diff --git a/drivers/gpu/drm/i915/display/intel_parent.c b/drivers/gpu/drm/i915/display/intel_parent.c index a5816561be40..a5e41ea66921 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.c +++ b/drivers/gpu/drm/i915/display/intel_parent.c @@ -251,9 +251,11 @@ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display) return display->parent->panic->alloc(); } -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb) +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - return display->parent->panic->setup(panic, sb); + return display->parent->panic->setup(panic, sb, obj, tiling); } void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic) diff --git a/drivers/gpu/drm/i915/display/intel_parent.h b/drivers/gpu/drm/i915/display/intel_parent.h index 27e35f891a6b..595d4148b8eb 100644 --- a/drivers/gpu/drm/i915/display/intel_parent.h +++ b/drivers/gpu/drm/i915/display/intel_parent.h @@ -105,7 +105,9 @@ void intel_parent_overlay_cleanup(struct intel_display *display); /* panic */ struct intel_panic *intel_parent_panic_alloc(struct intel_display *display); -int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, struct drm_scanout_buffer *sb); +int intel_parent_panic_setup(struct intel_display *display, struct intel_panic *panic, + struct drm_scanout_buffer *sb, struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)); void intel_parent_panic_finish(struct intel_display *display, struct intel_panic *panic); /* pc8 */ diff --git a/drivers/gpu/drm/i915/display/intel_plane.c b/drivers/gpu/drm/i915/display/intel_plane.c index 7017ed6cb9d1..a440f92ff00c 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.c +++ b/drivers/gpu/drm/i915/display/intel_plane.c @@ -408,25 +408,27 @@ intel_plane_colorop_replace_blob(struct intel_plane_state *plane_state, } static void -intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +intel_plane_color_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc) { struct drm_colorop *iter_colorop, *colorop; struct drm_colorop_state *new_colorop_state; - struct drm_atomic_commit *state = plane_state->uapi.state; struct intel_colorop *intel_colorop; struct drm_property_blob *blob; - struct intel_atomic_state *intel_atomic_state = to_intel_atomic_state(state); - struct intel_crtc_state *new_crtc_state = intel_atomic_state ? - intel_atomic_get_new_crtc_state(intel_atomic_state, crtc) : NULL; + struct intel_crtc_state *new_crtc_state = state ? + intel_atomic_get_new_crtc_state(state, crtc) : NULL; bool changed = false; int i = 0; + if (!state) + return; + iter_colorop = from_plane_state->uapi.color_pipeline; while (iter_colorop) { - for_each_new_colorop_in_state(state, colorop, new_colorop_state, i) { + for_each_new_colorop_in_state(&state->base, colorop, new_colorop_state, i) { if (new_colorop_state->colorop == iter_colorop) { blob = new_colorop_state->bypass ? NULL : new_colorop_state->data; intel_colorop = to_intel_colorop(colorop); @@ -442,7 +444,8 @@ intel_plane_color_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, new_crtc_state->plane_color_changed = true; } -void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc) { @@ -471,19 +474,25 @@ void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, plane_state->uapi.src = drm_plane_state_src(&from_plane_state->uapi); plane_state->uapi.dst = drm_plane_state_dest(&from_plane_state->uapi); - intel_plane_color_copy_uapi_to_hw_state(plane_state, from_plane_state, crtc); + intel_plane_color_copy_uapi_to_hw_state(state, plane_state, from_plane_state, crtc); } -void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, - const struct intel_plane_state *from_plane_state) +static void intel_plane_y_copy_hw_state(struct intel_plane_state *y_plane_state, + const struct intel_plane_state *uv_plane_state) { - intel_plane_clear_hw_state(plane_state); + intel_plane_clear_hw_state(y_plane_state); - memcpy(&plane_state->hw, &from_plane_state->hw, - sizeof(plane_state->hw)); + y_plane_state->hw.crtc = uv_plane_state->hw.crtc; + y_plane_state->hw.fb = uv_plane_state->hw.fb; + if (y_plane_state->hw.fb) + drm_framebuffer_get(y_plane_state->hw.fb); - if (plane_state->hw.fb) - drm_framebuffer_get(plane_state->hw.fb); + y_plane_state->hw.alpha = uv_plane_state->hw.alpha; + y_plane_state->hw.pixel_blend_mode = uv_plane_state->hw.pixel_blend_mode; + y_plane_state->hw.rotation = uv_plane_state->hw.rotation; + y_plane_state->hw.color_encoding = uv_plane_state->hw.color_encoding; + y_plane_state->hw.color_range = uv_plane_state->hw.color_range; + y_plane_state->hw.scaling_filter = uv_plane_state->hw.scaling_filter; } static void unlink_nv12_plane(struct intel_crtc_state *crtc_state, @@ -868,7 +877,8 @@ static int plane_atomic_check(struct intel_atomic_state *state, old_primary_crtc_plane_state, new_primary_crtc_plane_state); - intel_plane_copy_uapi_to_hw_state(new_plane_state, + intel_plane_copy_uapi_to_hw_state(state, + new_plane_state, new_primary_crtc_plane_state, crtc); @@ -1607,17 +1617,17 @@ static int intel_get_scanout_buffer(struct drm_plane *plane, if (fb == intel_fbdev_framebuffer(display->fbdev.fbdev)) { intel_fbdev_get_map(display, &sb->map[0]); } else { + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width) = NULL; int ret; /* Can't disable tiling if DPT is in use */ if (intel_fb_uses_dpt(&fb->base)) { if (fb->base.format->cpp[0] != 4) return -EOPNOTSUPP; - fb->panic_tiling = intel_get_tiling_func(fb->base.modifier); - if (!fb->panic_tiling) + tiling = intel_get_tiling_func(fb->base.modifier); + if (!tiling) return -EOPNOTSUPP; } - sb->private = fb; - ret = intel_parent_panic_setup(display, fb->panic, sb); + ret = intel_parent_panic_setup(display, fb->panic, sb, obj, tiling); if (ret) return ret; } @@ -1688,7 +1698,7 @@ static void link_nv12_planes(struct intel_crtc_state *crtc_state, crtc_state->rel_data_rate[y_plane->id] = crtc_state->rel_data_rate_y[uv_plane->id]; /* Copy parameters to Y plane */ - intel_plane_copy_hw_state(y_plane_state, uv_plane_state); + intel_plane_y_copy_hw_state(y_plane_state, uv_plane_state); y_plane_state->uapi.src = uv_plane_state->uapi.src; y_plane_state->uapi.dst = uv_plane_state->uapi.dst; diff --git a/drivers/gpu/drm/i915/display/intel_plane.h b/drivers/gpu/drm/i915/display/intel_plane.h index a6338bba72d9..31a6229aea73 100644 --- a/drivers/gpu/drm/i915/display/intel_plane.h +++ b/drivers/gpu/drm/i915/display/intel_plane.h @@ -35,11 +35,10 @@ unsigned int intel_plane_pixel_rate(const struct intel_crtc_state *crtc_state, unsigned int intel_plane_data_rate(const struct intel_crtc_state *crtc_state, const struct intel_plane_state *plane_state, int color_plane); -void intel_plane_copy_uapi_to_hw_state(struct intel_plane_state *plane_state, +void intel_plane_copy_uapi_to_hw_state(struct intel_atomic_state *state, + struct intel_plane_state *plane_state, const struct intel_plane_state *from_plane_state, struct intel_crtc *crtc); -void intel_plane_copy_hw_state(struct intel_plane_state *plane_state, - const struct intel_plane_state *from_plane_state); void intel_plane_async_flip(struct intel_dsb *dsb, struct intel_plane *plane, const struct intel_crtc_state *crtc_state, diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index e138982dc91f..92af21d823a3 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -88,22 +88,6 @@ * issues the self-refresh re-enable code is done from a work queue, which * must be correctly synchronized/cancelled when shutting down the pipe." * - * DC3CO (DC3 clock off) - * - * On top of PSR2, GEN12 adds a intermediate power savings state that turns - * clock off automatically during PSR2 idle state. - * The smaller overhead of DC3co entry/exit vs. the overhead of PSR2 deep sleep - * entry/exit allows the HW to enter a low-power state even when page flipping - * periodically (for instance a 30fps video playback scenario). - * - * Every time a flips occurs PSR2 will get out of deep sleep state(if it was), - * so DC3CO is enabled and tgl_dc3co_disable_work is schedule to run after 6 - * frames, if no other flip occurs and the function above is executed, DC3CO is - * disabled and PSR2 is configured to enter deep sleep, resetting again in case - * of another flip. - * Front buffer modifications do not trigger DC3CO activation on purpose as it - * would bring a lot of complexity and most of the moderns systems will only - * use page flips. */ /* @@ -1098,10 +1082,11 @@ static void hsw_activate_psr2(struct intel_dp *intel_dp) u32 psr_val = 0; u8 idle_frames; - /* Wa_16025596647 */ - if ((DISPLAY_VER(display) == 20 || - IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && - is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used) + /* DC3CO / Wa_16025596647 */ + if (intel_dp->psr.dc3co_allowed || + ((DISPLAY_VER(display) == 20 || + IS_DISPLAY_VERx100_STEP(display, 3000, STEP_A0, STEP_B0)) && + is_dc5_dc6_blocked(intel_dp) && intel_dp->psr.pkg_c_latency_used)) idle_frames = 0; else idle_frames = psr_compute_idle_frames(intel_dp); @@ -1220,108 +1205,6 @@ static void psr2_program_idle_frames(struct intel_dp *intel_dp, EDP_PSR2_IDLE_FRAMES(idle_frames)); } -static void tgl_psr2_enable_dc3co(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - - psr2_program_idle_frames(intel_dp, 0); - intel_display_power_set_target_dc_state(display, DC_STATE_EN_DC3CO); -} - -static void tgl_psr2_disable_dc3co(struct intel_dp *intel_dp) -{ - struct intel_display *display = to_intel_display(intel_dp); - - intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6); - psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp)); -} - -static void tgl_dc3co_disable_work(struct work_struct *work) -{ - struct intel_dp *intel_dp = - container_of(work, typeof(*intel_dp), psr.dc3co_work.work); - - mutex_lock(&intel_dp->psr.lock); - /* If delayed work is pending, it is not idle */ - if (delayed_work_pending(&intel_dp->psr.dc3co_work)) - goto unlock; - - tgl_psr2_disable_dc3co(intel_dp); -unlock: - mutex_unlock(&intel_dp->psr.lock); -} - -static void tgl_disallow_dc3co_on_psr2_exit(struct intel_dp *intel_dp) -{ - if (!intel_dp->psr.dc3co_exitline) - return; - - cancel_delayed_work(&intel_dp->psr.dc3co_work); - /* Before PSR2 exit disallow dc3co*/ - tgl_psr2_disable_dc3co(intel_dp); -} - -static bool -dc3co_is_pipe_port_compatible(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum pipe pipe = to_intel_crtc(crtc_state->uapi.crtc)->pipe; - enum port port = dig_port->base.port; - - if (display->platform.alderlake_p || DISPLAY_VER(display) >= 14) - return pipe <= PIPE_B && port <= PORT_B; - else - return pipe == PIPE_A && port == PORT_A; -} - -static void -tgl_dc3co_exitline_compute_config(struct intel_dp *intel_dp, - struct intel_crtc_state *crtc_state) -{ - struct intel_display *display = to_intel_display(intel_dp); - const u32 crtc_vdisplay = crtc_state->uapi.adjusted_mode.crtc_vdisplay; - struct i915_power_domains *power_domains = &display->power.domains; - u32 exit_scanlines; - - /* - * FIXME: Due to the changed sequence of activating/deactivating DC3CO, - * disable DC3CO until the changed dc3co activating/deactivating sequence - * is applied. B.Specs:49196 - */ - return; - - /* - * DMC's DC3CO exit mechanism has an issue with Selective Fecth - * TODO: when the issue is addressed, this restriction should be removed. - */ - if (crtc_state->enable_psr2_sel_fetch) - return; - - if (!(power_domains->allowed_dc_mask & DC_STATE_EN_DC3CO)) - return; - - if (!dc3co_is_pipe_port_compatible(intel_dp, crtc_state)) - return; - - /* Wa_16011303918:adl-p */ - if (intel_display_wa(display, INTEL_DISPLAY_WA_16011303918)) - return; - - /* - * DC3CO Exit time 200us B.Spec 49196 - * PSR2 transcoder Early Exit scanlines = ROUNDUP(200 / line time) + 1 - */ - exit_scanlines = - intel_usecs_to_scanlines(&crtc_state->uapi.adjusted_mode, 200) + 1; - - if (drm_WARN_ON(display->drm, exit_scanlines > crtc_vdisplay)) - return; - - crtc_state->dc3co_exitline = crtc_vdisplay - exit_scanlines; -} - static bool intel_psr2_sel_fetch_config_valid(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) { @@ -1522,9 +1405,6 @@ int _intel_psr_min_set_context_latency(const struct intel_crtc_state *crtc_state needs_panel_replay) return 0; - if (intel_vrr_always_use_vrr_tg(display)) - return 0; - return 1; } @@ -1697,8 +1577,6 @@ static bool intel_psr2_config_valid(struct intel_dp *intel_dp, return false; } - tgl_dc3co_exitline_compute_config(intel_dp, crtc_state); - return true; } @@ -1893,6 +1771,50 @@ static bool intel_psr_needs_wa_18037818876(struct intel_dp *intel_dp, !crtc_state->has_sel_update); } +static void psr2_dc3co_disable_locked(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (intel_dp->psr.dc3co_allowed) { + intel_dp->psr.dc3co_allowed = false; + intel_display_power_set_target_dc_state(display, DC_STATE_EN_UPTO_DC6); + psr2_program_idle_frames(intel_dp, psr_compute_idle_frames(intel_dp)); + } +} + +static void psr2_dc3co_disable_work(struct work_struct *work) +{ + struct intel_dp *intel_dp = + container_of(work, typeof(*intel_dp), psr.dc3co_work.work); + + mutex_lock(&intel_dp->psr.lock); + psr2_dc3co_disable_locked(intel_dp); + mutex_unlock(&intel_dp->psr.lock); +} + +static void +psr2_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits) +{ + struct intel_display *display = to_intel_display(intel_dp); + + if (!intel_dp->psr.dc3co_allowed) + return; + + if (!intel_dp->psr.sel_update_enabled || + !intel_dp->psr.active) + return; + /* + * At every frontbuffer flush flip event modified delay of delayed work, + * when delayed work schedules that means display has been idle. + */ + if (!(frontbuffer_bits & + INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe))) + return; + + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); +} + static void intel_psr_set_non_psr_pipes(struct intel_dp *intel_dp, struct intel_crtc_state *crtc_state) @@ -2013,12 +1935,6 @@ void intel_psr_get_config(struct intel_encoder *encoder, } pipe_config->enable_psr2_su_region_et = intel_dp->psr.su_region_et_enabled; - - if (DISPLAY_VER(display) >= 12) { - val = intel_de_read(display, - TRANS_EXITLINE(display, cpu_transcoder)); - pipe_config->dc3co_exitline = REG_FIELD_GET(EXITLINE_MASK, val); - } unlock: mutex_unlock(&intel_dp->psr.lock); } @@ -2146,16 +2062,6 @@ static void intel_psr_enable_source(struct intel_dp *intel_dp, psr_irq_control(intel_dp); - /* - * TODO: if future platforms supports DC3CO in more than one - * transcoder, EXITLINE will need to be unset when disabling PSR - */ - if (intel_dp->psr.dc3co_exitline) - intel_de_rmw(display, - TRANS_EXITLINE(display, cpu_transcoder), - EXITLINE_MASK, - intel_dp->psr.dc3co_exitline << EXITLINE_SHIFT | EXITLINE_ENABLE); - if (HAS_PSR_HW_TRACKING(display) && HAS_PSR2_SEL_FETCH(display)) intel_de_rmw(display, CHICKEN_PAR1_1, IGNORE_PSR2_HW_TRACKING, intel_dp->psr.psr2_sel_fetch_enabled ? @@ -2255,7 +2161,6 @@ static void intel_psr_enable_locked(struct intel_dp *intel_dp, /* DC5/DC6 requires at least 6 idle frames */ val = usecs_to_jiffies(intel_get_frame_time_us(crtc_state) * 6); intel_dp->psr.dc3co_exit_delay = val; - intel_dp->psr.dc3co_exitline = crtc_state->dc3co_exitline; intel_dp->psr.psr2_sel_fetch_enabled = crtc_state->enable_psr2_sel_fetch; intel_dp->psr.su_region_et_enabled = crtc_state->enable_psr2_su_region_et; intel_dp->psr.psr2_sel_fetch_cff_enabled = false; @@ -2334,8 +2239,6 @@ static void intel_psr_exit(struct intel_dp *intel_dp) intel_de_rmw(display, TRANS_DP2_CTL(intel_dp->psr.transcoder), TRANS_DP2_PANEL_REPLAY_ENABLE, 0); } else if (intel_dp->psr.sel_update_enabled) { - tgl_disallow_dc3co_on_psr2_exit(intel_dp); - val = intel_de_rmw(display, EDP_PSR2_CTL(display, cpu_transcoder), EDP_PSR2_ENABLE, 0); @@ -2358,6 +2261,27 @@ static void intel_psr_exit(struct intel_dp *intel_dp) intel_dp->psr.active = false; } +bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp) +{ + struct intel_display *display = to_intel_display(intel_dp); + enum transcoder cpu_transcoder; + bool in_deep_sleep = false; + u32 val; + + mutex_lock(&intel_dp->psr.lock); + + if (!intel_dp->psr.enabled || !intel_dp->psr.sel_update_enabled) + goto out; + + cpu_transcoder = intel_dp->psr.transcoder; + val = intel_de_read(display, EDP_PSR2_STATUS(display, cpu_transcoder)); + in_deep_sleep = (val & EDP_PSR2_STATUS_STATE_MASK) == + EDP_PSR2_STATUS_STATE_DEEP_SLEEP; +out: + mutex_unlock(&intel_dp->psr.lock); + return in_deep_sleep; +} + static void intel_psr_wait_exit_locked(struct intel_dp *intel_dp) { struct intel_display *display = to_intel_display(intel_dp); @@ -2448,6 +2372,8 @@ static void intel_psr_disable_locked(struct intel_dp *intel_dp) intel_dp->psr.psr2_sel_fetch_cff_enabled = false; intel_dp->psr.active_non_psr_pipes = 0; intel_dp->psr.pkg_c_latency_used = 0; + cancel_delayed_work(&intel_dp->psr.dc3co_work); + intel_dp->psr.dc3co_allowed = false; } /** @@ -2535,8 +2461,13 @@ void intel_psr_resume(struct intel_dp *intel_dp) goto out; } - if (--intel_dp->psr.pause_counter == 0) + if (--intel_dp->psr.pause_counter == 0) { intel_psr_activate(intel_dp); + /* re-arm cancelled dc3co work from pause */ + if (intel_dp->psr.dc3co_allowed) + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); + } out: mutex_unlock(&psr->lock); @@ -3240,10 +3171,13 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, const struct intel_crtc_state *crtc_state = intel_atomic_get_new_crtc_state(state, crtc); struct intel_encoder *encoder; + bool dc3co_allowed; if (!crtc_state->has_psr) return; + dc3co_allowed = intel_display_power_dc3co_allowed(display); + verify_panel_replay_dsc_state(crtc_state); for_each_intel_encoder_mask_with_psr(state->base.dev, encoder, @@ -3271,6 +3205,8 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, keep_disabled = true; } + intel_dp->psr.dc3co_allowed = dc3co_allowed; + if (!psr->enabled && !keep_disabled) intel_psr_enable_locked(intel_dp, crtc_state); else if (psr->enabled && !crtc_state->wm_level_disabled) @@ -3287,6 +3223,11 @@ void intel_psr_post_plane_update(struct intel_atomic_state *state, */ intel_dp->psr.busy_frontbuffer_bits = 0; + if (intel_dp->psr.dc3co_allowed) { + mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, + intel_dp->psr.dc3co_exit_delay); + } + mutex_unlock(&psr->lock); } } @@ -3659,34 +3600,6 @@ void intel_psr_invalidate(struct intel_display *display, mutex_unlock(&intel_dp->psr.lock); } } -/* - * When we will be completely rely on PSR2 S/W tracking in future, - * intel_psr_flush() will invalidate and flush the PSR for ORIGIN_FLIP - * event also therefore tgl_dc3co_flush_locked() require to be changed - * accordingly in future. - */ -static void -tgl_dc3co_flush_locked(struct intel_dp *intel_dp, unsigned int frontbuffer_bits, - enum fb_op_origin origin) -{ - struct intel_display *display = to_intel_display(intel_dp); - - if (!intel_dp->psr.dc3co_exitline || !intel_dp->psr.sel_update_enabled || - !intel_dp->psr.active) - return; - - /* - * At every frontbuffer flush flip event modified delay of delayed work, - * when delayed work schedules that means display has been idle. - */ - if (!(frontbuffer_bits & - INTEL_FRONTBUFFER_ALL_MASK(intel_dp->psr.pipe))) - return; - - tgl_psr2_enable_dc3co(intel_dp); - mod_delayed_work(display->wq.unordered, &intel_dp->psr.dc3co_work, - intel_dp->psr.dc3co_exit_delay); -} static void _psr_flush_handle(struct intel_dp *intel_dp) { @@ -3773,7 +3686,7 @@ void intel_psr_flush(struct intel_display *display, if (origin == ORIGIN_FLIP || (origin == ORIGIN_CURSOR_UPDATE && !intel_dp->psr.psr2_sel_fetch_enabled)) { - tgl_dc3co_flush_locked(intel_dp, frontbuffer_bits, origin); + psr2_dc3co_flush_locked(intel_dp, frontbuffer_bits); goto unlock; } @@ -3832,7 +3745,7 @@ void intel_psr_init(struct intel_dp *intel_dp) intel_dp->psr.link_standby = connector->panel.vbt.psr.full_link; INIT_WORK(&intel_dp->psr.work, intel_psr_work); - INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, tgl_dc3co_disable_work); + INIT_DELAYED_WORK(&intel_dp->psr.dc3co_work, psr2_dc3co_disable_work); mutex_init(&intel_dp->psr.lock); } diff --git a/drivers/gpu/drm/i915/display/intel_psr.h b/drivers/gpu/drm/i915/display/intel_psr.h index 29723e63888f..d545fdaa0de7 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.h +++ b/drivers/gpu/drm/i915/display/intel_psr.h @@ -87,5 +87,6 @@ void intel_psr_compute_config_late(struct intel_dp *intel_dp, int intel_psr_min_guardband(struct intel_crtc_state *crtc_state); bool intel_psr_use_trans_push(const struct intel_crtc_state *crtc_state); bool intel_psr_pr_async_video_timing_supported(struct intel_dp *intel_dp); +bool intel_psr2_in_deep_sleep(struct intel_dp *intel_dp); #endif /* __INTEL_PSR_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_psr_regs.h b/drivers/gpu/drm/i915/display/intel_psr_regs.h index 8afbf5a38335..16a9e3af198d 100644 --- a/drivers/gpu/drm/i915/display/intel_psr_regs.h +++ b/drivers/gpu/drm/i915/display/intel_psr_regs.h @@ -268,6 +268,7 @@ #define _PR_ALPM_CTL_A 0x60948 #define PR_ALPM_CTL(dev_priv, tran) _MMIO_TRANS2(dev_priv, tran, _PR_ALPM_CTL_A) +#define PR_ALPM_CTL_USE_DC3CO_IDLE_PROTOCOL BIT(7) #define PR_ALPM_CTL_ALLOW_LINK_OFF_BETWEEN_AS_SDP_AND_SU BIT(6) #define PR_ALPM_CTL_RFB_UPDATE_CONTROL BIT(5) #define PR_ALPM_CTL_AS_SDP_TRANSMISSION_IN_ACTIVE_DISABLE BIT(4) diff --git a/drivers/gpu/drm/i915/display/intel_sdvo.c b/drivers/gpu/drm/i915/display/intel_sdvo.c index d83d350959d8..3075ef04df56 100644 --- a/drivers/gpu/drm/i915/display/intel_sdvo.c +++ b/drivers/gpu/drm/i915/display/intel_sdvo.c @@ -1354,7 +1354,8 @@ static bool intel_sdvo_has_audio(struct intel_encoder *encoder, return intel_conn_state->force_audio == HDMI_AUDIO_ON; } -static int intel_sdvo_compute_config(struct intel_encoder *encoder, +static int intel_sdvo_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -1398,8 +1399,8 @@ static int intel_sdvo_compute_config(struct intel_encoder *encoder, const struct drm_display_mode *fixed_mode; int ret; - ret = intel_panel_compute_config(&intel_sdvo_connector->base, - adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, + &intel_sdvo_connector->base); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/display/intel_tv.c b/drivers/gpu/drm/i915/display/intel_tv.c index 0a926c6f25f4..840e1dcdc2d0 100644 --- a/drivers/gpu/drm/i915/display/intel_tv.c +++ b/drivers/gpu/drm/i915/display/intel_tv.c @@ -1187,13 +1187,12 @@ static bool intel_tv_vert_scaling(const struct drm_display_mode *tv_mode, } static int -intel_tv_compute_config(struct intel_encoder *encoder, +intel_tv_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { struct intel_display *display = to_intel_display(encoder); - struct intel_atomic_state *state = - to_intel_atomic_state(pipe_config->uapi.state); struct intel_crtc *crtc = to_intel_crtc(pipe_config->uapi.crtc); struct intel_tv_connector_state *tv_conn_state = to_intel_tv_connector_state(conn_state); diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index aa587be908f1..bffbdee76ee1 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -7,8 +7,10 @@ #include <drm/drm_print.h> #include "intel_alpm.h" +#include "intel_cmtg.h" #include "intel_crtc.h" #include "intel_de.h" +#include "intel_display_limits.h" #include "intel_display_regs.h" #include "intel_display_types.h" #include "intel_dmc.h" @@ -322,19 +324,19 @@ int intel_vrr_fixed_rr_hw_flipline(const struct intel_crtc_state *crtc_state) return intel_vrr_fixed_rr_hw_vtotal(crtc_state); } -void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state) +void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder) { struct intel_display *display = to_intel_display(crtc_state); - enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; if (!intel_vrr_possible(crtc_state)) return; - intel_de_write(display, TRANS_VRR_VMIN(display, cpu_transcoder), + intel_de_write(display, TRANS_VRR_VMIN(display, transcoder), intel_vrr_fixed_rr_hw_vmin(crtc_state) - 1); - intel_de_write(display, TRANS_VRR_VMAX(display, cpu_transcoder), + intel_de_write(display, TRANS_VRR_VMAX(display, transcoder), intel_vrr_fixed_rr_hw_vmax(crtc_state) - 1); - intel_de_write(display, TRANS_VRR_FLIPLINE(display, cpu_transcoder), + intel_de_write(display, TRANS_VRR_FLIPLINE(display, transcoder), intel_vrr_fixed_rr_hw_flipline(crtc_state) - 1); } @@ -649,7 +651,8 @@ void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) lower_32_bits(crtc_state->cmrr.cmrr_n)); } - intel_vrr_set_fixed_rr_timings(crtc_state); + intel_vrr_set_fixed_rr_timings(crtc_state, cpu_transcoder); + intel_cmtg_set_vrr_timings(crtc_state); if (!intel_vrr_always_use_vrr_tg(display)) intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), @@ -934,6 +937,8 @@ static void intel_vrr_tg_enable(const struct intel_crtc_state *crtc_state, vrr_ctl |= VRR_CTL_CMRR_ENABLE; intel_de_write(display, TRANS_VRR_CTL(display, cpu_transcoder), vrr_ctl); + + intel_cmtg_set_vrr_ctl(crtc_state); } static void intel_vrr_tg_disable(const struct intel_crtc_state *old_crtc_state) @@ -978,7 +983,7 @@ void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) intel_vrr_tg_disable(old_crtc_state); intel_vrr_disable_dc_balancing(old_crtc_state); - intel_vrr_set_fixed_rr_timings(old_crtc_state); + intel_vrr_set_fixed_rr_timings(old_crtc_state, old_crtc_state->cpu_transcoder); } void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state) @@ -1101,16 +1106,6 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) crtc_state->vrr.vmin += intel_vrr_vmin_flipline_offset(display); } - /* - * For platforms that always use VRR Timing Generator, the VTOTAL.Vtotal - * bits are not filled. Since for these platforms TRAN_VMIN is always - * filled with crtc_vtotal, use TRAN_VRR_VMIN to get the vtotal for - * adjusted_mode. - */ - if (intel_vrr_always_use_vrr_tg(display)) - crtc_state->hw.adjusted_mode.crtc_vtotal = - intel_vrr_vmin_vtotal(crtc_state); - if (HAS_AS_SDP(display)) { trans_vrr_vsync = intel_de_read(display, diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h index 4f16ca4af91f..55e9c429f579 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.h +++ b/drivers/gpu/drm/i915/display/intel_vrr.h @@ -15,6 +15,7 @@ struct intel_crtc; struct intel_crtc_state; struct intel_dsb; struct intel_display; +enum transcoder; bool intel_vrr_is_capable(struct intel_connector *connector); bool intel_vrr_is_in_range(struct intel_connector *connector, int vrefresh); @@ -42,7 +43,8 @@ int intel_vrr_vmin_vblank_start(const struct intel_crtc_state *crtc_state); bool intel_vrr_is_fixed_rr(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_enable(const struct intel_crtc_state *crtc_state); void intel_vrr_transcoder_disable(const struct intel_crtc_state *crtc_state); -void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state); +void intel_vrr_set_fixed_rr_timings(const struct intel_crtc_state *crtc_state, + enum transcoder transcoder); void intel_vrr_dcb_reset(const struct intel_crtc_state *old_crtc_state, struct intel_crtc *crtc); bool intel_vrr_always_use_vrr_tg(struct intel_display *display); diff --git a/drivers/gpu/drm/i915/display/vlv_dsi.c b/drivers/gpu/drm/i915/display/vlv_dsi.c index 877eab75f19a..8829f365592e 100644 --- a/drivers/gpu/drm/i915/display/vlv_dsi.c +++ b/drivers/gpu/drm/i915/display/vlv_dsi.c @@ -266,7 +266,8 @@ static void band_gap_reset(struct intel_display *display) vlv_flisdsi_put(display); } -static int intel_dsi_compute_config(struct intel_encoder *encoder, +static int intel_dsi_compute_config(struct intel_atomic_state *state, + struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { @@ -280,7 +281,7 @@ static int intel_dsi_compute_config(struct intel_encoder *encoder, pipe_config->sink_format = INTEL_OUTPUT_FORMAT_RGB; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - ret = intel_panel_compute_config(intel_connector, adjusted_mode); + ret = intel_panel_compute_config(state, pipe_config, intel_connector); if (ret) return ret; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_object.h b/drivers/gpu/drm/i915/gem/i915_gem_object.h index 8878539c10ed..2c5d20e4dbaf 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_object.h +++ b/drivers/gpu/drm/i915/gem/i915_gem_object.h @@ -17,8 +17,6 @@ #include "i915_vma_types.h" enum intel_region_id; -struct drm_scanout_buffer; -struct intel_panic; #define obj_to_i915(obj__) to_i915((obj__)->base.dev) @@ -693,11 +691,6 @@ i915_gem_object_unpin_pages(struct drm_i915_gem_object *obj) int __i915_gem_object_put_pages(struct drm_i915_gem_object *obj); int i915_gem_object_truncate(struct drm_i915_gem_object *obj); -struct intel_panic *i915_gem_object_alloc_panic(void); -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling); -void i915_gem_object_panic_finish(struct intel_panic *panic); - /** * i915_gem_object_pin_map - return a contiguous mapping of the entire object * @obj: the object to map into kernel address space diff --git a/drivers/gpu/drm/i915/gem/i915_gem_pages.c b/drivers/gpu/drm/i915/gem/i915_gem_pages.c index df35bdb755e4..59e3d4de7d3c 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_pages.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_pages.c @@ -6,11 +6,8 @@ #include <linux/vmalloc.h> #include <drm/drm_cache.h> -#include <drm/drm_panic.h> #include <drm/drm_print.h> -#include "display/intel_fb.h" -#include "display/intel_display_types.h" #include "gt/intel_gt.h" #include "gt/intel_tlb.h" @@ -359,131 +356,6 @@ static void *i915_gem_object_map_pfn(struct drm_i915_gem_object *obj, return vaddr ?: ERR_PTR(-ENOMEM); } -struct intel_panic { - struct page **pages; - int page; - void *vaddr; -}; - -static void i915_panic_kunmap(struct intel_panic *panic) -{ - if (panic->vaddr) { - drm_clflush_virt_range(panic->vaddr, PAGE_SIZE); - kunmap_local(panic->vaddr); - panic->vaddr = NULL; - } -} - -static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj) -{ - unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i; - struct page *page; - struct page **pages; - struct sgt_iter iter; - - /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */ - pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC); - if (!pages) - return NULL; - - i = 0; - for_each_sgt_page(page, iter, obj->mm.pages) - pages[i++] = page; - return pages; -} - -static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, - unsigned int y, u32 color) -{ - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - unsigned int offset = fb->panic_tiling(sb->width, x, y); - - iosys_map_wr(&sb->map[0], offset, u32, color); -} - -/* - * The scanout buffer pages are not mapped, so for each pixel, - * use kmap_local_page_try_from_panic() to map the page, and write the pixel. - * Try to keep the map from the previous pixel, to avoid too much map/unmap. - */ -static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, - unsigned int y, u32 color) -{ - unsigned int new_page; - unsigned int offset; - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; - - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); - else - offset = y * sb->pitch[0] + x * sb->format->cpp[0]; - - new_page = offset >> PAGE_SHIFT; - offset = offset % PAGE_SIZE; - if (new_page != panic->page) { - i915_panic_kunmap(panic); - panic->page = new_page; - panic->vaddr = - kmap_local_page_try_from_panic(panic->pages[panic->page]); - } - if (panic->vaddr) { - u32 *pix = panic->vaddr + offset; - *pix = color; - } -} - -struct intel_panic *i915_gem_object_alloc_panic(void) -{ - struct intel_panic *panic; - - panic = kzalloc_obj(*panic); - - return panic; -} - -/* - * Setup the gem framebuffer for drm_panic access. - * Use current vaddr if it exists, or setup a list of pages. - * pfn is not supported yet. - */ -int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, - struct drm_gem_object *_obj, bool panic_tiling) -{ - enum i915_map_type has_type; - struct drm_i915_gem_object *obj = to_intel_bo(_obj); - void *ptr; - - ptr = page_unpack_bits(obj->mm.mapping, &has_type); - if (ptr) { - if (i915_gem_object_has_iomem(obj)) - iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr); - else - iosys_map_set_vaddr(&sb->map[0], ptr); - - if (panic_tiling) - sb->set_pixel = i915_gem_object_panic_map_set_pixel; - return 0; - } - if (i915_gem_object_has_struct_page(obj)) { - panic->pages = i915_gem_object_panic_pages(obj); - if (!panic->pages) - return -ENOMEM; - panic->page = -1; - sb->set_pixel = i915_gem_object_panic_page_set_pixel; - return 0; - } - return -EOPNOTSUPP; -} - -void i915_gem_object_panic_finish(struct intel_panic *panic) -{ - i915_panic_kunmap(panic); - panic->page = -1; - kfree(panic->pages); - panic->pages = NULL; -} - /* get, pin, and map the pages of the object into kernel space */ void *i915_gem_object_pin_map(struct drm_i915_gem_object *obj, enum i915_map_type type) diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.c b/drivers/gpu/drm/i915/gem/i915_gem_panic.c new file mode 100644 index 000000000000..91389d36f101 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.c @@ -0,0 +1,147 @@ +// SPDX-License-Identifier: MIT +/* Copyright © 2026 Intel Corporation */ + +#include <drm/drm_cache.h> +#include <drm/drm_panic.h> +#include <drm/intel/display_parent_interface.h> + +#include "i915_gem_object.h" +#include "i915_gem_panic.h" + +struct intel_panic { + struct page **pages; + int page; + void *vaddr; + + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); +}; + +static void i915_panic_kunmap(struct intel_panic *panic) +{ + if (panic->vaddr) { + drm_clflush_virt_range(panic->vaddr, PAGE_SIZE); + kunmap_local(panic->vaddr); + panic->vaddr = NULL; + } +} + +static struct page **i915_gem_object_panic_pages(struct drm_i915_gem_object *obj) +{ + unsigned long n_pages = obj->base.size >> PAGE_SHIFT, i; + struct page *page; + struct page **pages; + struct sgt_iter iter; + + /* For a 3840x2160 32 bits Framebuffer, this should require ~64K */ + pages = kmalloc_objs(*pages, n_pages, GFP_ATOMIC); + if (!pages) + return NULL; + + i = 0; + for_each_sgt_page(page, iter, obj->mm.pages) + pages[i++] = page; + return pages; +} + +static void i915_gem_object_panic_map_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, + unsigned int y, u32 color) +{ + struct intel_panic *panic = sb->private; + unsigned int offset = panic->tiling(sb->width, x, y); + + iosys_map_wr(&sb->map[0], offset, u32, color); +} + +/* + * The scanout buffer pages are not mapped, so for each pixel, + * use kmap_local_page_try_from_panic() to map the page, and write the pixel. + * Try to keep the map from the previous pixel, to avoid too much map/unmap. + */ +static void i915_gem_object_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, + unsigned int y, u32 color) +{ + struct intel_panic *panic = sb->private; + unsigned int new_page; + unsigned int offset; + + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); + else + offset = y * sb->pitch[0] + x * sb->format->cpp[0]; + + new_page = offset >> PAGE_SHIFT; + offset = offset % PAGE_SIZE; + if (new_page != panic->page) { + i915_panic_kunmap(panic); + panic->page = new_page; + panic->vaddr = + kmap_local_page_try_from_panic(panic->pages[panic->page]); + } + if (panic->vaddr) { + u32 *pix = panic->vaddr + offset; + *pix = color; + } +} + +static struct intel_panic *i915_gem_object_alloc_panic(void) +{ + struct intel_panic *panic; + + panic = kzalloc_obj(*panic); + + return panic; +} + +/* + * Setup the gem framebuffer for drm_panic access. + * Use current vaddr if it exists, or setup a list of pages. + * pfn is not supported yet. + */ +static int i915_gem_object_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *_obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) +{ + enum i915_map_type has_type; + struct drm_i915_gem_object *obj = to_intel_bo(_obj); + void *ptr; + + sb->private = panic; + + ptr = page_unpack_bits(obj->mm.mapping, &has_type); + if (ptr) { + if (i915_gem_object_has_iomem(obj)) + iosys_map_set_vaddr_iomem(&sb->map[0], (void __iomem *)ptr); + else + iosys_map_set_vaddr(&sb->map[0], ptr); + + if (tiling) { + panic->tiling = tiling; + sb->set_pixel = i915_gem_object_panic_map_set_pixel; + } + return 0; + } + if (i915_gem_object_has_struct_page(obj)) { + panic->pages = i915_gem_object_panic_pages(obj); + if (!panic->pages) + return -ENOMEM; + panic->page = -1; + panic->tiling = tiling; + sb->set_pixel = i915_gem_object_panic_page_set_pixel; + return 0; + } + return -EOPNOTSUPP; +} + +static void i915_gem_object_panic_finish(struct intel_panic *panic) +{ + i915_panic_kunmap(panic); + panic->page = -1; + kfree(panic->pages); + panic->pages = NULL; +} + +const struct intel_display_panic_interface i915_display_panic_interface = { + .alloc = i915_gem_object_alloc_panic, + .setup = i915_gem_object_panic_setup, + .finish = i915_gem_object_panic_finish, +}; diff --git a/drivers/gpu/drm/i915/gem/i915_gem_panic.h b/drivers/gpu/drm/i915/gem/i915_gem_panic.h new file mode 100644 index 000000000000..82c3aca6f1f3 --- /dev/null +++ b/drivers/gpu/drm/i915/gem/i915_gem_panic.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: MIT */ +/* Copyright © 2026 Intel Corporation */ + +#ifndef __I915_GEM_PANIC_H__ +#define __I915_GEM_PANIC_H__ + +#include <linux/types.h> + +extern const struct intel_display_panic_interface i915_display_panic_interface; + +#endif /* __I915_GEM_PANIC_H__ */ diff --git a/drivers/gpu/drm/i915/gt/intel_gsc.c b/drivers/gpu/drm/i915/gt/intel_gsc.c index 050d909fb4f8..1c06bf76568a 100644 --- a/drivers/gpu/drm/i915/gt/intel_gsc.c +++ b/drivers/gpu/drm/i915/gt/intel_gsc.c @@ -286,7 +286,7 @@ static void gsc_irq_handler(struct intel_gt *gt, unsigned int intf_id) ret = generic_handle_irq_safe(gt->gsc.intf[intf_id].irq); if (ret) - gt_err_ratelimited(gt, "error handling GSC irq: %d\n", ret); + gt_err_ratelimited(gt, "GSC: irq handling failed (%pe)\n", ERR_PTR(ret)); } void intel_gsc_irq_handler(struct intel_gt *gt, u32 iir) diff --git a/drivers/gpu/drm/i915/gvt/edid.c b/drivers/gpu/drm/i915/gvt/edid.c index ca5b54466a65..dc9ef98ff51b 100644 --- a/drivers/gpu/drm/i915/gvt/edid.c +++ b/drivers/gpu/drm/i915/gvt/edid.c @@ -90,13 +90,13 @@ static inline int cnp_get_port_from_gmbus0(u32 gmbus0) int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; int port = -EINVAL; - if (port_select == GMBUS_PIN_1_BXT) + if (port_select == GMBUS_PIN_1) port = PORT_B; - else if (port_select == GMBUS_PIN_2_BXT) + else if (port_select == GMBUS_PIN_2) port = PORT_C; - else if (port_select == GMBUS_PIN_3_BXT) + else if (port_select == GMBUS_PIN_3) port = PORT_D; - else if (port_select == GMBUS_PIN_4_CNP) + else if (port_select == GMBUS_PIN_4) port = PORT_E; return port; } @@ -106,11 +106,11 @@ static inline int bxt_get_port_from_gmbus0(u32 gmbus0) int port_select = gmbus0 & _GMBUS_PIN_SEL_MASK; int port = -EINVAL; - if (port_select == GMBUS_PIN_1_BXT) + if (port_select == GMBUS_PIN_1) port = PORT_B; - else if (port_select == GMBUS_PIN_2_BXT) + else if (port_select == GMBUS_PIN_2) port = PORT_C; - else if (port_select == GMBUS_PIN_3_BXT) + else if (port_select == GMBUS_PIN_3) port = PORT_D; return port; } diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index 58081b52461a..45f2dcc5130e 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -51,7 +51,6 @@ #include <drm/intel/intel_pcode_regs.h> #include "display/i9xx_display_sr.h" -#include "display/intel_bw.h" #include "display/intel_cdclk.h" #include "display/intel_crtc.h" #include "display/intel_display_device.h" @@ -60,8 +59,6 @@ #include "display/intel_dmc.h" #include "display/intel_dp.h" #include "display/intel_dpt.h" -#include "display/intel_dram.h" -#include "display/intel_encoder.h" #include "display/intel_fbdev.h" #include "display/intel_gmbus.h" #include "display/intel_hotplug.h" @@ -79,6 +76,7 @@ #include "gem/i915_gem_ioctls.h" #include "gem/i915_gem_mman.h" #include "gem/i915_gem_object_frontbuffer.h" +#include "gem/i915_gem_panic.h" #include "gem/i915_gem_pm.h" #include "gt/intel_gt.h" #include "gt/intel_gt_pm.h" @@ -111,7 +109,6 @@ #include "i915_irq.h" #include "i915_memcpy.h" #include "i915_overlay.h" -#include "i915_panic.h" #include "i915_perf.h" #include "i915_query.h" #include "i915_reg.h" @@ -470,7 +467,6 @@ static int i915_pcode_init(struct drm_i915_private *i915) */ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) { - struct intel_display *display = dev_priv->display; struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); int ret; @@ -564,28 +560,14 @@ static int i915_driver_hw_probe(struct drm_i915_private *dev_priv) drm_dbg(&dev_priv->drm, "can't enable MSI"); } - intel_opregion_setup(display); - ret = i915_pcode_init(dev_priv); if (ret) - goto err_opregion; - - /* - * Fill the dram structure to get the system dram info. This will be - * used for memory latency calculation. - */ - ret = intel_dram_detect(display); - if (ret) - goto err_opregion; - - intel_bw_init_hw(display); + goto err_msi; return 0; -err_opregion: - intel_opregion_cleanup(display); - if (pdev->msi_enabled) - pci_disable_msi(pdev); +err_msi: + pci_disable_msi(pdev); err_mem_regions: intel_memory_regions_driver_release(dev_priv); err_ggtt: @@ -604,15 +586,11 @@ ALLOW_ERROR_INJECTION(i915_driver_hw_probe, ERRNO); */ static void i915_driver_hw_remove(struct drm_i915_private *dev_priv) { - struct intel_display *display = dev_priv->display; struct pci_dev *pdev = to_pci_dev(dev_priv->drm.dev); i915_perf_fini(dev_priv); - intel_opregion_cleanup(display); - - if (pdev->msi_enabled) - pci_disable_msi(pdev); + pci_disable_msi(pdev); } /** @@ -803,7 +781,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) const struct intel_device_info *match_info = (struct intel_device_info *)ent->driver_data; struct drm_i915_private *i915; - struct intel_display *display; i915 = devm_drm_dev_alloc(&pdev->dev, &i915_drm_driver, struct drm_i915_private, drm); @@ -818,12 +795,6 @@ i915_driver_create(struct pci_dev *pdev, const struct pci_device_id *ent) /* Set up device info and initial runtime info. */ intel_device_info_driver_create(i915, pdev->device, match_info); - display = intel_display_device_probe(pdev, &parent); - if (IS_ERR(display)) - return ERR_CAST(display); - - i915->display = display; - return i915; } @@ -856,7 +827,13 @@ int i915_driver_probe(struct pci_dev *pdev, const struct pci_device_id *ent) return PTR_ERR(i915); } - display = i915->display; + display = intel_display_device_probe(pdev, &parent); + if (IS_ERR(display)) { + ret = PTR_ERR(display); + goto out_pci_disable; + } + + i915->display = display; ret = i915_driver_early_probe(i915); if (ret < 0) @@ -971,7 +948,6 @@ void i915_driver_remove(struct drm_i915_private *i915) intel_display_driver_remove(display); intel_irq_uninstall(i915); - intel_hpd_cancel_work(display); intel_display_driver_remove_noirq(display); @@ -1043,43 +1019,24 @@ void i915_driver_shutdown(struct drm_i915_private *i915) disable_rpm_wakeref_asserts(&i915->runtime_pm); intel_runtime_pm_disable(&i915->runtime_pm); - intel_display_power_disable(display); - drm_client_dev_suspend(&i915->drm); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&i915->drm); - intel_display_driver_disable_user_access(display); - - drm_atomic_helper_shutdown(&i915->drm); - } - - intel_dp_mst_suspend(display); + intel_display_driver_shutdown(display); intel_irq_suspend(i915); - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); - intel_encoder_shutdown_all(display); intel_dmc_suspend(display); i915_gem_suspend(i915); /* - * The only requirement is to reboot with display DC states disabled, - * for now leaving all display power wells in the INIT power domain - * enabled. - * * TODO: * - unify the pci_driver::shutdown sequence here with the * pci_driver.driver.pm.poweroff,poweroff_late sequence. * - unify the driver remove and system/runtime suspend sequences with * the above unified shutdown/poweroff sequence. */ - intel_display_power_driver_remove(display); + + intel_display_driver_shutdown_late(display); enable_rpm_wakeref_asserts(&i915->runtime_pm); intel_runtime_pm_driver_last_release(&i915->runtime_pm); @@ -1108,10 +1065,10 @@ static int i915_drm_prepare(struct drm_device *dev) intel_pxp_suspend_prepare(i915->pxp); /* - * NB intel_display_driver_suspend() may issue new requests after we've - * ostensibly marked the GPU as ready-to-sleep here. We need to - * split out that work and pull it forward so that after point, - * the GPU is not woken again. + * NB intel_display_driver_pm_suspend() may issue new requests after + * we've ostensibly marked the GPU as ready-to-sleep here. We need to + * split out that work and pull it forward so that after point, the GPU + * is not woken again. */ return i915_gem_backup_suspend(i915); } @@ -1124,24 +1081,9 @@ static int i915_drm_suspend(struct drm_device *dev) disable_rpm_wakeref_asserts(&dev_priv->runtime_pm); - /* We do a lot of poking in a lot of registers, make sure they work - * properly. */ - intel_display_power_disable(display); - drm_client_dev_suspend(dev); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(dev); - intel_display_driver_disable_user_access(display); - } - - intel_display_driver_suspend(display); + intel_display_driver_pm_suspend(display); intel_irq_suspend(dev_priv); - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); - - intel_encoder_suspend_all(display); /* Must be called before GGTT is suspended. */ intel_dpt_suspend(display); @@ -1181,12 +1123,12 @@ static int i915_drm_suspend_late(struct drm_device *dev, bool hibernation) for_each_gt(gt, dev_priv, i) intel_uncore_suspend(gt->uncore); - intel_display_power_suspend_late(display, s2idle); + intel_display_driver_pm_suspend_late(display, s2idle); ret = vlv_suspend_complete(dev_priv); if (ret) { drm_err(&dev_priv->drm, "Suspend complete failed: %d\n", ret); - intel_display_power_resume_early(display); + intel_display_driver_pm_resume_early(display); } enable_rpm_wakeref_asserts(rpm); @@ -1309,24 +1251,7 @@ static int i915_drm_resume(struct drm_device *dev) intel_clock_gating_init(&dev_priv->drm); - if (intel_display_device_present(display)) - intel_display_driver_resume_access(display); - - intel_hpd_init(display); - - intel_display_driver_resume(display); - - if (intel_display_device_present(display)) { - intel_display_driver_enable_user_access(display); - drm_kms_helper_poll_enable(dev); - } - intel_hpd_poll_disable(display); - - intel_opregion_resume(display); - - drm_client_dev_resume(dev); - - intel_display_power_enable(display); + intel_display_driver_pm_resume(display); intel_gvt_resume(dev_priv); @@ -1362,7 +1287,7 @@ static int i915_drm_resume_early(struct drm_device *dev) for_each_gt(gt, dev_priv, i) intel_gt_resume_early(gt); - intel_display_power_resume_early(display); + intel_display_driver_pm_resume_early(display); enable_rpm_wakeref_asserts(&dev_priv->runtime_pm); diff --git a/drivers/gpu/drm/i915/i915_panic.c b/drivers/gpu/drm/i915/i915_panic.c deleted file mode 100644 index 728be077e8e8..000000000000 --- a/drivers/gpu/drm/i915/i915_panic.c +++ /dev/null @@ -1,35 +0,0 @@ -// SPDX-License-Identifier: MIT -/* Copyright © 2025 Intel Corporation */ - -#include <drm/drm_panic.h> -#include <drm/intel/display_parent_interface.h> - -#include "display/intel_display_types.h" -#include "display/intel_fb.h" -#include "gem/i915_gem_object.h" - -#include "i915_panic.h" - -static struct intel_panic *intel_panic_alloc(void) -{ - return i915_gem_object_alloc_panic(); -} - -static int intel_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) -{ - struct intel_framebuffer *fb = sb->private; - struct drm_gem_object *obj = intel_fb_bo(&fb->base); - - return i915_gem_object_panic_setup(panic, sb, obj, fb->panic_tiling); -} - -static void intel_panic_finish(struct intel_panic *panic) -{ - return i915_gem_object_panic_finish(panic); -} - -const struct intel_display_panic_interface i915_display_panic_interface = { - .alloc = intel_panic_alloc, - .setup = intel_panic_setup, - .finish = intel_panic_finish, -}; diff --git a/drivers/gpu/drm/i915/i915_panic.h b/drivers/gpu/drm/i915/i915_panic.h deleted file mode 100644 index 743d8c861c42..000000000000 --- a/drivers/gpu/drm/i915/i915_panic.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: MIT */ -/* Copyright © 2025 Intel Corporation */ - -#ifndef __I915_PANIC_H__ -#define __I915_PANIC_H__ - -extern const struct intel_display_panic_interface i915_display_panic_interface; - -#endif /* __I915_PANIC_H__ */ diff --git a/drivers/gpu/drm/xe/Makefile b/drivers/gpu/drm/xe/Makefile index 8e7b146880f4..e5a04253e73b 100644 --- a/drivers/gpu/drm/xe/Makefile +++ b/drivers/gpu/drm/xe/Makefile @@ -278,6 +278,7 @@ xe-$(CONFIG_DRM_XE_DISPLAY) += \ i915-display/intel_dp_aux.o \ i915-display/intel_dp_aux_backlight.o \ i915-display/intel_dp_hdcp.o \ + i915-display/intel_dp_link_caps.o \ i915-display/intel_dp_link_training.o \ i915-display/intel_dp_mst.o \ i915-display/intel_dp_test.o \ diff --git a/drivers/gpu/drm/xe/display/xe_display.c b/drivers/gpu/drm/xe/display/xe_display.c index 810d93fefcbc..92a4573db28a 100644 --- a/drivers/gpu/drm/xe/display/xe_display.c +++ b/drivers/gpu/drm/xe/display/xe_display.c @@ -19,7 +19,6 @@ #include "intel_acpi.h" #include "intel_audio.h" -#include "intel_bw.h" #include "intel_display.h" #include "intel_display_core.h" #include "intel_display_device.h" @@ -29,8 +28,6 @@ #include "intel_dmc.h" #include "intel_dmc_wl.h" #include "intel_dp.h" -#include "intel_dram.h" -#include "intel_encoder.h" #include "intel_fbdev.h" #include "intel_hdcp.h" #include "intel_hotplug.h" @@ -84,10 +81,8 @@ static void xe_display_fini_early(void *arg) if (!xe->info.probe_display) return; - intel_hpd_cancel_work(display); - intel_display_driver_remove_nogem(display); intel_display_driver_remove_noirq(display); - intel_opregion_cleanup(display); + intel_display_driver_remove_nogem(display); intel_display_power_cleanup(display); } @@ -113,22 +108,9 @@ int xe_display_init_early(struct xe_device *xe) return 0; } - /* Early display init.. */ - intel_opregion_setup(display); - - /* - * Fill the dram structure to get the system dram info. This will be - * used for memory latency calculation. - */ - err = intel_dram_detect(display); - if (err) - goto err_opregion; - - intel_bw_init_hw(display); - err = intel_display_driver_probe_noirq(display); if (err) - goto err_opregion; + return err; err = intel_display_driver_probe_nogem(display); if (err) @@ -138,8 +120,7 @@ int xe_display_init_early(struct xe_device *xe) err_noirq: intel_display_driver_remove_noirq(display); intel_display_power_cleanup(display); -err_opregion: - intel_opregion_cleanup(display); + return err; } @@ -191,6 +172,30 @@ void xe_display_unregister(struct xe_device *xe) intel_display_driver_unregister(display); } +void xe_display_shutdown(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + intel_display_driver_shutdown(display); + + intel_opregion_suspend(display, PCI_D3cold); + + intel_dmc_suspend(display); +} + +void xe_display_shutdown_late(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + + if (!xe->info.probe_display) + return; + + intel_display_driver_shutdown_late(display); +} + /* IRQ-related functions */ void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) @@ -244,57 +249,62 @@ static bool suspend_to_idle(void) return false; } -static void xe_display_enable_d3cold(struct xe_device *xe) +void xe_display_pm_suspend(struct xe_device *xe) { struct intel_display *display = xe->display; + bool s2idle = suspend_to_idle(); if (!xe->info.probe_display) return; - /* - * We do a lot of poking in a lot of registers, make sure they work - * properly. - */ - intel_display_power_disable(display); - - intel_display_flush_cleanup_work(display); + intel_display_driver_pm_suspend(display); - intel_opregion_suspend(display, PCI_D3cold); + intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold); intel_dmc_suspend(display); +} - if (intel_display_device_present(display)) - intel_hpd_poll_enable(display); +void xe_display_pm_suspend_late(struct xe_device *xe) +{ + struct intel_display *display = xe->display; + bool s2idle = suspend_to_idle(); + + if (!xe->info.probe_display) + return; + + intel_display_driver_pm_suspend_late(display, s2idle); } -static void xe_display_disable_d3cold(struct xe_device *xe) +void xe_display_pm_resume_early(struct xe_device *xe) { struct intel_display *display = xe->display; if (!xe->info.probe_display) return; - intel_dmc_resume(display); + intel_display_driver_pm_resume_early(display); +} - if (intel_display_device_present(display)) - drm_mode_config_reset(&xe->drm); +void xe_display_pm_resume(struct xe_device *xe) +{ + struct intel_display *display = xe->display; - intel_display_driver_init_hw(display); + if (!xe->info.probe_display) + return; - intel_hpd_init(display); + intel_dmc_resume(display); if (intel_display_device_present(display)) - intel_hpd_poll_disable(display); + drm_mode_config_reset(&xe->drm); - intel_opregion_resume(display); + intel_display_driver_init_hw(display); - intel_display_power_enable(display); + intel_display_driver_pm_resume(display); } -void xe_display_pm_suspend(struct xe_device *xe) +static void xe_display_enable_d3cold(struct xe_device *xe) { struct intel_display *display = xe->display; - bool s2idle = suspend_to_idle(); if (!xe->info.probe_display) return; @@ -304,60 +314,39 @@ void xe_display_pm_suspend(struct xe_device *xe) * properly. */ intel_display_power_disable(display); - drm_client_dev_suspend(&xe->drm); - - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&xe->drm); - intel_display_driver_disable_user_access(display); - intel_display_driver_suspend(display); - } intel_display_flush_cleanup_work(display); - intel_encoder_block_all_hpds(display); - - intel_hpd_cancel_work(display); - - if (intel_display_device_present(display)) { - intel_display_driver_suspend_access(display); - intel_encoder_suspend_all(display); - } - - intel_opregion_suspend(display, s2idle ? PCI_D1 : PCI_D3cold); + intel_opregion_suspend(display, PCI_D3cold); intel_dmc_suspend(display); + + if (intel_display_device_present(display)) + intel_hpd_poll_enable(display); } -void xe_display_pm_shutdown(struct xe_device *xe) +static void xe_display_disable_d3cold(struct xe_device *xe) { struct intel_display *display = xe->display; if (!xe->info.probe_display) return; - intel_display_power_disable(display); - drm_client_dev_suspend(&xe->drm); + intel_dmc_resume(display); - if (intel_display_device_present(display)) { - drm_kms_helper_poll_disable(&xe->drm); - intel_display_driver_disable_user_access(display); - intel_display_driver_suspend(display); - } + if (intel_display_device_present(display)) + drm_mode_config_reset(&xe->drm); - intel_display_flush_cleanup_work(display); - intel_dp_mst_suspend(display); - intel_encoder_block_all_hpds(display); - intel_hpd_cancel_work(display); + intel_display_driver_init_hw(display); - if (intel_display_device_present(display)) - intel_display_driver_suspend_access(display); + intel_hpd_init(display); - intel_encoder_suspend_all(display); - intel_encoder_shutdown_all(display); + if (intel_display_device_present(display)) + intel_hpd_poll_disable(display); - intel_opregion_suspend(display, PCI_D3cold); + intel_opregion_resume(display); - intel_dmc_suspend(display); + intel_display_power_enable(display); } void xe_display_pm_runtime_suspend(struct xe_device *xe) @@ -375,17 +364,6 @@ void xe_display_pm_runtime_suspend(struct xe_device *xe) intel_hpd_poll_enable(display); } -void xe_display_pm_suspend_late(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - bool s2idle = suspend_to_idle(); - - if (!xe->info.probe_display) - return; - - intel_display_power_suspend_late(display, s2idle); -} - void xe_display_pm_runtime_suspend_late(struct xe_device *xe) { struct intel_display *display = xe->display; @@ -404,68 +382,6 @@ void xe_display_pm_runtime_suspend_late(struct xe_device *xe) intel_dmc_wl_flush_release_work(display); } -void xe_display_pm_shutdown_late(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - /* - * The only requirement is to reboot with display DC states disabled, - * for now leaving all display power wells in the INIT power domain - * enabled. - */ - intel_display_power_driver_remove(display); -} - -void xe_display_pm_resume_early(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - intel_display_power_resume_early(display); -} - -void xe_display_pm_resume(struct xe_device *xe) -{ - struct intel_display *display = xe->display; - - if (!xe->info.probe_display) - return; - - intel_dmc_resume(display); - - if (intel_display_device_present(display)) - drm_mode_config_reset(&xe->drm); - - intel_display_driver_init_hw(display); - - if (intel_display_device_present(display)) - intel_display_driver_resume_access(display); - - intel_hpd_init(display); - - intel_encoder_unblock_all_hpds(display); - - if (intel_display_device_present(display)) { - intel_display_driver_resume(display); - drm_kms_helper_poll_enable(&xe->drm); - intel_display_driver_enable_user_access(display); - } - - if (intel_display_device_present(display)) - intel_hpd_poll_disable(display); - - intel_opregion_resume(display); - - drm_client_dev_resume(&xe->drm); - - intel_display_power_enable(display); -} - void xe_display_pm_runtime_resume(struct xe_device *xe) { struct intel_display *display = xe->display; diff --git a/drivers/gpu/drm/xe/display/xe_display.h b/drivers/gpu/drm/xe/display/xe_display.h index 60291cb154df..e5f9aed93206 100644 --- a/drivers/gpu/drm/xe/display/xe_display.h +++ b/drivers/gpu/drm/xe/display/xe_display.h @@ -29,15 +29,16 @@ int xe_display_init(struct xe_device *xe); void xe_display_register(struct xe_device *xe); void xe_display_unregister(struct xe_device *xe); +void xe_display_shutdown(struct xe_device *xe); +void xe_display_shutdown_late(struct xe_device *xe); + void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl); void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir); void xe_display_irq_reset(struct xe_device *xe); void xe_display_irq_postinstall(struct xe_device *xe); void xe_display_pm_suspend(struct xe_device *xe); -void xe_display_pm_shutdown(struct xe_device *xe); void xe_display_pm_suspend_late(struct xe_device *xe); -void xe_display_pm_shutdown_late(struct xe_device *xe); void xe_display_pm_resume_early(struct xe_device *xe); void xe_display_pm_resume(struct xe_device *xe); void xe_display_pm_runtime_suspend(struct xe_device *xe); @@ -65,15 +66,16 @@ static inline int xe_display_init(struct xe_device *xe) { return 0; } static inline void xe_display_register(struct xe_device *xe) {} static inline void xe_display_unregister(struct xe_device *xe) {} +static inline void xe_display_shutdown(struct xe_device *xe) {} +static inline void xe_display_shutdown_late(struct xe_device *xe) {} + static inline void xe_display_irq_handler(struct xe_device *xe, u32 master_ctl) {} static inline void xe_display_irq_enable(struct xe_device *xe, u32 gu_misc_iir) {} static inline void xe_display_irq_reset(struct xe_device *xe) {} static inline void xe_display_irq_postinstall(struct xe_device *xe) {} static inline void xe_display_pm_suspend(struct xe_device *xe) {} -static inline void xe_display_pm_shutdown(struct xe_device *xe) {} static inline void xe_display_pm_suspend_late(struct xe_device *xe) {} -static inline void xe_display_pm_shutdown_late(struct xe_device *xe) {} static inline void xe_display_pm_resume_early(struct xe_device *xe) {} static inline void xe_display_pm_resume(struct xe_device *xe) {} static inline void xe_display_pm_runtime_suspend(struct xe_device *xe) {} diff --git a/drivers/gpu/drm/xe/display/xe_panic.c b/drivers/gpu/drm/xe/display/xe_panic.c index bebb21d617f0..12c6fb99015d 100644 --- a/drivers/gpu/drm/xe/display/xe_panic.c +++ b/drivers/gpu/drm/xe/display/xe_panic.c @@ -5,8 +5,6 @@ #include <drm/drm_panic.h> #include <drm/intel/display_parent_interface.h> -#include "intel_display_types.h" -#include "intel_fb.h" #include "xe_bo.h" #include "xe_panic.h" #include "xe_res_cursor.h" @@ -16,6 +14,9 @@ struct intel_panic { struct iosys_map vmap; int page; + + struct xe_bo *bo; + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width); }; static void xe_panic_kunmap(struct intel_panic *panic) @@ -36,14 +37,13 @@ static void xe_panic_kunmap(struct intel_panic *panic) static void xe_panic_page_set_pixel(struct drm_scanout_buffer *sb, unsigned int x, unsigned int y, u32 color) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct intel_panic *panic = fb->panic; - struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); + struct intel_panic *panic = sb->private; + struct xe_bo *bo = panic->bo; unsigned int new_page; unsigned int offset; - if (fb->panic_tiling) - offset = fb->panic_tiling(sb->width, x, y); + if (panic->tiling) + offset = panic->tiling(sb->width, x, y); else offset = y * sb->pitch[0] + x * sb->format->cpp[0]; @@ -84,16 +84,22 @@ static struct intel_panic *xe_panic_alloc(void) return panic; } -static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb) +static int xe_panic_setup(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)) { - struct intel_framebuffer *fb = (struct intel_framebuffer *)sb->private; - struct xe_bo *bo = gem_to_xe_bo(intel_fb_bo(&fb->base)); + struct xe_bo *bo = gem_to_xe_bo(obj); if (xe_bo_is_vram(bo) && !xe_bo_is_visible_vram(bo)) return -ENODEV; panic->page = -1; + panic->bo = bo; + panic->tiling = tiling; + + sb->private = panic; sb->set_pixel = xe_panic_page_set_pixel; + return 0; } diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c index 4efd2bc8c02e..b60a651a3c9b 100644 --- a/drivers/gpu/drm/xe/xe_device.c +++ b/drivers/gpu/drm/xe/xe_device.c @@ -1129,14 +1129,14 @@ void xe_device_shutdown(struct xe_device *xe) drm_dbg(&xe->drm, "Shutting down device\n"); - xe_display_pm_shutdown(xe); + xe_display_shutdown(xe); xe_irq_suspend(xe); for_each_gt(gt, xe, id) xe_gt_shutdown(gt); - xe_display_pm_shutdown_late(xe); + xe_display_shutdown_late(xe); if (!xe_driver_flr_disabled(xe)) { /* BOOM! */ diff --git a/include/drm/drm_modes.h b/include/drm/drm_modes.h index b9bb92e4b029..6e3eccc3c349 100644 --- a/include/drm/drm_modes.h +++ b/include/drm/drm_modes.h @@ -193,6 +193,7 @@ enum drm_mode_status { #define DRM_MODE_MATCH_FLAGS (1 << 2) #define DRM_MODE_MATCH_3D_FLAGS (1 << 3) #define DRM_MODE_MATCH_ASPECT_RATIO (1 << 4) +#define DRM_MODE_MATCH_TIMINGS_VRR (1 << 5) /** * struct drm_display_mode - DRM kernel-internal display mode structure diff --git a/include/drm/intel/display_parent_interface.h b/include/drm/intel/display_parent_interface.h index 39991afeb173..de395df9ca30 100644 --- a/include/drm/intel/display_parent_interface.h +++ b/include/drm/intel/display_parent_interface.h @@ -167,7 +167,9 @@ struct intel_display_overlay_interface { struct intel_display_panic_interface { struct intel_panic *(*alloc)(void); - int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb); + int (*setup)(struct intel_panic *panic, struct drm_scanout_buffer *sb, + struct drm_gem_object *obj, + unsigned int (*tiling)(unsigned int x, unsigned int y, unsigned int width)); void (*finish)(struct intel_panic *panic); }; diff --git a/include/drm/intel/mchbar_regs.h b/include/drm/intel/mchbar_regs.h index ca0d421be16c..66498ca5e40b 100644 --- a/include/drm/intel/mchbar_regs.h +++ b/include/drm/intel/mchbar_regs.h @@ -6,8 +6,6 @@ #ifndef __INTEL_MCHBAR_REGS__ #define __INTEL_MCHBAR_REGS__ -#include "i915_reg_defs.h" - /* * MCHBAR mirror. * |
