From b25e07419fee6e3be07e58cc64f50e11228987d3 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Mon, 20 Mar 2023 22:33:48 +0200 Subject: drm/i915/vrr: Eliminate redundant function arguments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the VRR functions take redundant arguments. Get rid of them to make life simpler. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230320203352.19515-3-ville.syrjala@linux.intel.com Reviewed-by: Mitul Golani --- drivers/gpu/drm/i915/display/intel_ddi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index dc294717bcdf..1f453362d079 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2963,7 +2963,7 @@ static void intel_enable_ddi(struct intel_atomic_state *state, intel_enable_transcoder(crtc_state); - intel_vrr_enable(encoder, crtc_state); + intel_vrr_enable(crtc_state); intel_crtc_vblank_on(crtc_state); -- cgit v1.2.3 From 99cfbed19d06dfe9c9929c436b5a768231c05b70 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 21 Mar 2023 15:56:15 +0200 Subject: drm/i915/vrr: Relocate VRR enable/disable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move VRR enabling/disabling into a place where it also works for fastsets. With this we always start the transcoder up in non-VRR mode. Granted we already did that but for a very short period of time. But now that we might end up doing a bit more with the transcoder in non-VRR mode it seems prudent to also update the active timings as the transcoder changes its operating mode. crtc_state->vrr.enable still tracks whether VRR is actually enabled or not, but now we configure all the other VRR timing registers whenever VRR is possible (whether we actually enable it or not). crtc_state->vrr.flipline can now serve as our "is VRR possible" bit of state. I decided to leave the MSA timing ignore bit set all the time whether VRR is actually enabled or not. If the sink can figure out the timings with that information when VRR is active then surely it can also do it when VRR is inactive. v2: Protect intel_vrr_set_transcoder_timings() with HAS_VRR() Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230321135615.27338-1-ville.syrjala@linux.intel.com Reviewed-by: Mitul Golani --- drivers/gpu/drm/i915/display/intel_ddi.c | 5 --- drivers/gpu/drm/i915/display/intel_display.c | 29 ++++++++++++- .../gpu/drm/i915/display/intel_dp_link_training.c | 2 +- drivers/gpu/drm/i915/display/intel_vrr.c | 48 +++++++++++++--------- drivers/gpu/drm/i915/display/intel_vrr.h | 1 + 5 files changed, 58 insertions(+), 27 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 1f453362d079..d5652e3b2fdb 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -68,7 +68,6 @@ #include "intel_tc.h" #include "intel_vdsc.h" #include "intel_vdsc_regs.h" -#include "intel_vrr.h" #include "skl_scaler.h" #include "skl_universal_plane.h" @@ -2725,8 +2724,6 @@ static void intel_ddi_post_disable(struct intel_atomic_state *state, if (!intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST)) { intel_crtc_vblank_off(old_crtc_state); - intel_vrr_disable(old_crtc_state); - intel_disable_transcoder(old_crtc_state); intel_ddi_disable_transcoder_func(old_crtc_state); @@ -2963,8 +2960,6 @@ static void intel_enable_ddi(struct intel_atomic_state *state, intel_enable_transcoder(crtc_state); - intel_vrr_enable(crtc_state); - intel_crtc_vblank_on(crtc_state); if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 921cf469682f..25ffa114a380 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -1088,6 +1088,18 @@ static bool planes_disabling(const struct intel_crtc_state *old_crtc_state, return is_disabling(active_planes, old_crtc_state, new_crtc_state); } +static bool vrr_enabling(const struct intel_crtc_state *old_crtc_state, + const struct intel_crtc_state *new_crtc_state) +{ + return is_enabling(vrr.enable, old_crtc_state, new_crtc_state); +} + +static bool vrr_disabling(const struct intel_crtc_state *old_crtc_state, + const struct intel_crtc_state *new_crtc_state) +{ + return is_disabling(vrr.enable, old_crtc_state, new_crtc_state); +} + #undef is_disabling #undef is_enabling @@ -1204,6 +1216,11 @@ static void intel_pre_plane_update(struct intel_atomic_state *state, intel_atomic_get_new_crtc_state(state, crtc); enum pipe pipe = crtc->pipe; + if (vrr_disabling(old_crtc_state, new_crtc_state)) { + intel_vrr_disable(old_crtc_state); + intel_crtc_update_active_timings(old_crtc_state, false); + } + intel_drrs_deactivate(old_crtc_state); intel_psr_pre_plane_update(state, crtc); @@ -1684,6 +1701,8 @@ static void hsw_configure_cpu_transcoder(const struct intel_crtc_state *crtc_sta } intel_set_transcoder_timings(crtc_state); + if (HAS_VRR(dev_priv)) + intel_vrr_set_transcoder_timings(crtc_state); if (cpu_transcoder != TRANSCODER_EDP) intel_de_write(dev_priv, TRANS_MULT(cpu_transcoder), @@ -6910,8 +6929,8 @@ static void intel_enable_crtc(struct intel_atomic_state *state, if (!intel_crtc_needs_modeset(new_crtc_state)) return; - intel_crtc_update_active_timings(new_crtc_state, - new_crtc_state->vrr.enable); + /* VRR will be enable later, if required */ + intel_crtc_update_active_timings(new_crtc_state, false); dev_priv->display.funcs.display->crtc_enable(state, crtc); @@ -6938,6 +6957,12 @@ static void intel_update_crtc(struct intel_atomic_state *state, intel_dpt_configure(crtc); } + if (vrr_enabling(old_crtc_state, new_crtc_state)) { + intel_vrr_enable(new_crtc_state); + intel_crtc_update_active_timings(new_crtc_state, + new_crtc_state->vrr.enable); + } + if (!modeset) { if (new_crtc_state->preload_luts && intel_crtc_needs_color_update(new_crtc_state)) 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 d638054c74ac..6aa4ae5e7ebe 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_link_training.c +++ b/drivers/gpu/drm/i915/display/intel_dp_link_training.c @@ -699,7 +699,7 @@ intel_dp_prepare_link_train(struct intel_dp *intel_dp, drm_dp_dpcd_write(&intel_dp->aux, DP_LINK_RATE_SET, &rate_select, 1); - link_config[0] = crtc_state->vrr.enable ? DP_MSA_TIMING_PAR_IGNORE_EN : 0; + link_config[0] = crtc_state->vrr.flipline ? DP_MSA_TIMING_PAR_IGNORE_EN : 0; link_config[1] = intel_dp_is_uhbr(crtc_state) ? DP_SET_ANSI_128B132B : DP_SET_ANSI_8B10B; drm_dp_dpcd_write(&intel_dp->aux, DP_DOWNSPREAD_CTRL, link_config, 2); diff --git a/drivers/gpu/drm/i915/display/intel_vrr.c b/drivers/gpu/drm/i915/display/intel_vrr.c index 348a7cc8e620..88e4759b538b 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.c +++ b/drivers/gpu/drm/i915/display/intel_vrr.c @@ -114,9 +114,6 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state, if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) return; - if (!crtc_state->uapi.vrr_enabled) - return; - vmin = DIV_ROUND_UP(adjusted_mode->crtc_clock * 1000, adjusted_mode->crtc_htotal * info->monitor_range.max_vfreq); vmax = adjusted_mode->crtc_clock * 1000 / @@ -135,7 +132,6 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state, */ crtc_state->vrr.vmin = vmin - 1; crtc_state->vrr.vmax = vmax; - crtc_state->vrr.enable = true; crtc_state->vrr.flipline = crtc_state->vrr.vmin + 1; @@ -152,7 +148,10 @@ intel_vrr_compute_config(struct intel_crtc_state *crtc_state, crtc_state->framestart_delay - 1); } - crtc_state->mode_flags |= I915_MODE_FLAG_VRR; + if (crtc_state->uapi.vrr_enabled) { + crtc_state->vrr.enable = true; + crtc_state->mode_flags |= I915_MODE_FLAG_VRR; + } } static u32 trans_vrr_ctl(const struct intel_crtc_state *crtc_state) @@ -168,7 +167,7 @@ static u32 trans_vrr_ctl(const struct intel_crtc_state *crtc_state) VRR_CTL_PIPELINE_FULL_OVERRIDE; } -void intel_vrr_enable(const struct intel_crtc_state *crtc_state) +void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; @@ -181,17 +180,15 @@ void intel_vrr_enable(const struct intel_crtc_state *crtc_state) intel_de_rmw(dev_priv, CHICKEN_TRANS(cpu_transcoder), 0, PIPE_VBLANK_WITH_DELAY); - if (!crtc_state->vrr.enable) + if (!crtc_state->vrr.flipline) { + intel_de_write(dev_priv, TRANS_VRR_CTL(cpu_transcoder), 0); return; + } intel_de_write(dev_priv, TRANS_VRR_VMIN(cpu_transcoder), crtc_state->vrr.vmin - 1); intel_de_write(dev_priv, TRANS_VRR_VMAX(cpu_transcoder), crtc_state->vrr.vmax - 1); intel_de_write(dev_priv, TRANS_VRR_CTL(cpu_transcoder), trans_vrr_ctl(crtc_state)); intel_de_write(dev_priv, TRANS_VRR_FLIPLINE(cpu_transcoder), crtc_state->vrr.flipline - 1); - intel_de_write(dev_priv, TRANS_PUSH(cpu_transcoder), TRANS_PUSH_EN); - - intel_de_write(dev_priv, TRANS_VRR_CTL(cpu_transcoder), - VRR_CTL_VRR_ENABLE | trans_vrr_ctl(crtc_state)); } void intel_vrr_send_push(const struct intel_crtc_state *crtc_state) @@ -219,6 +216,19 @@ bool intel_vrr_is_push_sent(const struct intel_crtc_state *crtc_state) return intel_de_read(dev_priv, TRANS_PUSH(cpu_transcoder)) & TRANS_PUSH_SEND; } +void intel_vrr_enable(const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(crtc_state->uapi.crtc->dev); + enum transcoder cpu_transcoder = crtc_state->cpu_transcoder; + + if (!crtc_state->vrr.enable) + return; + + intel_de_write(dev_priv, TRANS_PUSH(cpu_transcoder), TRANS_PUSH_EN); + intel_de_write(dev_priv, TRANS_VRR_CTL(cpu_transcoder), + VRR_CTL_VRR_ENABLE | trans_vrr_ctl(crtc_state)); +} + void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) { struct intel_crtc *crtc = to_intel_crtc(old_crtc_state->uapi.crtc); @@ -232,9 +242,7 @@ void intel_vrr_disable(const struct intel_crtc_state *old_crtc_state) trans_vrr_ctl(old_crtc_state)); intel_de_wait_for_clear(dev_priv, TRANS_VRR_STATUS(cpu_transcoder), VRR_STATUS_VRR_EN_LIVE, 1000); - intel_de_write(dev_priv, TRANS_PUSH(cpu_transcoder), 0); - intel_de_write(dev_priv, TRANS_VRR_CTL(cpu_transcoder), 0); } void intel_vrr_get_config(struct intel_crtc_state *crtc_state) @@ -244,9 +252,8 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) u32 trans_vrr_ctl; trans_vrr_ctl = intel_de_read(dev_priv, TRANS_VRR_CTL(cpu_transcoder)); + crtc_state->vrr.enable = trans_vrr_ctl & VRR_CTL_VRR_ENABLE; - if (!crtc_state->vrr.enable) - return; if (DISPLAY_VER(dev_priv) >= 13) crtc_state->vrr.guardband = @@ -255,10 +262,13 @@ void intel_vrr_get_config(struct intel_crtc_state *crtc_state) if (trans_vrr_ctl & VRR_CTL_PIPELINE_FULL_OVERRIDE) crtc_state->vrr.pipeline_full = REG_FIELD_GET(VRR_CTL_PIPELINE_FULL_MASK, trans_vrr_ctl); - if (trans_vrr_ctl & VRR_CTL_FLIP_LINE_EN) + + if (trans_vrr_ctl & VRR_CTL_FLIP_LINE_EN) { crtc_state->vrr.flipline = intel_de_read(dev_priv, TRANS_VRR_FLIPLINE(cpu_transcoder)) + 1; - crtc_state->vrr.vmax = intel_de_read(dev_priv, TRANS_VRR_VMAX(cpu_transcoder)) + 1; - crtc_state->vrr.vmin = intel_de_read(dev_priv, TRANS_VRR_VMIN(cpu_transcoder)) + 1; + crtc_state->vrr.vmax = intel_de_read(dev_priv, TRANS_VRR_VMAX(cpu_transcoder)) + 1; + crtc_state->vrr.vmin = intel_de_read(dev_priv, TRANS_VRR_VMIN(cpu_transcoder)) + 1; + } - crtc_state->mode_flags |= I915_MODE_FLAG_VRR; + if (crtc_state->vrr.enable) + crtc_state->mode_flags |= I915_MODE_FLAG_VRR; } diff --git a/drivers/gpu/drm/i915/display/intel_vrr.h b/drivers/gpu/drm/i915/display/intel_vrr.h index ee636a5367c8..de16960c4929 100644 --- a/drivers/gpu/drm/i915/display/intel_vrr.h +++ b/drivers/gpu/drm/i915/display/intel_vrr.h @@ -17,6 +17,7 @@ bool intel_vrr_is_capable(struct intel_connector *connector); void intel_vrr_check_modeset(struct intel_atomic_state *state); void intel_vrr_compute_config(struct intel_crtc_state *crtc_state, struct drm_connector_state *conn_state); +void intel_vrr_set_transcoder_timings(const struct intel_crtc_state *crtc_state); void intel_vrr_enable(const struct intel_crtc_state *crtc_state); void intel_vrr_send_push(const struct intel_crtc_state *crtc_state); bool intel_vrr_is_push_sent(const struct intel_crtc_state *crtc_state); -- cgit v1.2.3 From 76ec69272195317080c16b970d23aebdaf192883 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Tue, 28 Mar 2023 15:23:57 +0300 Subject: drm/i915: Flag purely internal commits to not clear crtc_state->inherited MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we have to force the hardware to go through a full modeset due to eg. cdclk reprogramming, we need to preserve crtc_state->inherited for all crtcs that have not otherwise gone through the whole compute_config() stuff after connectors have been detected. Otherwise eg. cdclk induced modeset glk_force_audio_cdclk() will clear the inherited flag, and thus the first real commit coming from userspace later on will not be forced through the full .compute_config() path and so eg. audio state may not get properly recomputed. But instead of adding all kinds of ad-hoc crtc_state->inherited preservation hacks all over, let's change things so that we only clear it for the crtcs directly included in userspace/client initiated commits. Should be far less fragile since now we just need to remember to flag the internal commits, and not worry about where new crtcs might get pulled in. Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/5260 Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230328122357.1697-1-ville.syrjala@linux.intel.com Reviewed-by: Luca Coelho --- drivers/gpu/drm/i915/display/i9xx_wm.c | 3 ++- drivers/gpu/drm/i915/display/intel_atomic.c | 3 ++- drivers/gpu/drm/i915/display/intel_audio.c | 1 + drivers/gpu/drm/i915/display/intel_ddi.c | 1 + drivers/gpu/drm/i915/display/intel_display.c | 22 ++++++++++++---------- drivers/gpu/drm/i915/display/intel_display_types.h | 3 +++ drivers/gpu/drm/i915/display/intel_modeset_setup.c | 1 + drivers/gpu/drm/i915/display/intel_pipe_crc.c | 1 + drivers/gpu/drm/i915/display/intel_psr.c | 3 ++- drivers/gpu/drm/i915/display/intel_sprite_uapi.c | 1 + 10 files changed, 26 insertions(+), 13 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/i9xx_wm.c b/drivers/gpu/drm/i915/display/i9xx_wm.c index caef72d38798..6288826a9497 100644 --- a/drivers/gpu/drm/i915/display/i9xx_wm.c +++ b/drivers/gpu/drm/i915/display/i9xx_wm.c @@ -3447,9 +3447,10 @@ void ilk_wm_sanitize(struct drm_i915_private *dev_priv) drm_modeset_acquire_init(&ctx, 0); -retry: state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; +retry: /* * Hardware readout is the only time we don't want to calculate * intermediate watermarks (since we don't trust the current diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c index a9a3f3715279..61011641f6ab 100644 --- a/drivers/gpu/drm/i915/display/intel_atomic.c +++ b/drivers/gpu/drm/i915/display/intel_atomic.c @@ -265,7 +265,6 @@ intel_crtc_duplicate_state(struct drm_crtc *crtc) crtc_state->update_wm_post = false; crtc_state->fifo_changed = false; crtc_state->preload_luts = false; - crtc_state->inherited = false; crtc_state->wm.need_postvbl_update = false; crtc_state->do_async_flip = false; crtc_state->fb_bits = 0; @@ -599,6 +598,8 @@ void intel_atomic_state_clear(struct drm_atomic_state *s) drm_atomic_state_default_clear(&state->base); intel_atomic_clear_global_state(state); + /* state->internal not reset on purpose */ + state->dpll_set = state->modeset = false; } diff --git a/drivers/gpu/drm/i915/display/intel_audio.c b/drivers/gpu/drm/i915/display/intel_audio.c index 3d5a9bbc6fde..3d9c9b4f27f8 100644 --- a/drivers/gpu/drm/i915/display/intel_audio.c +++ b/drivers/gpu/drm/i915/display/intel_audio.c @@ -1039,6 +1039,7 @@ static void glk_force_audio_cdclk(struct drm_i915_private *i915, return; state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; retry: ret = glk_force_audio_cdclk_commit(to_intel_atomic_state(state), crtc, diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index d5652e3b2fdb..d6fb07821cff 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3903,6 +3903,7 @@ static int modeset_pipe(struct drm_crtc *crtc, return -ENOMEM; state->acquire_ctx = ctx; + to_intel_atomic_state(state)->internal = true; crtc_state = drm_atomic_get_crtc_state(state, crtc); if (IS_ERR(crtc_state)) { diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index 27b47680573a..0334565cec82 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -4137,7 +4137,10 @@ found: } state->acquire_ctx = ctx; + to_intel_atomic_state(state)->internal = true; + restore_state->acquire_ctx = ctx; + to_intel_atomic_state(restore_state)->internal = true; connector_state = drm_atomic_get_connector_state(state, connector); if (IS_ERR(connector_state)) { @@ -6585,6 +6588,13 @@ int intel_atomic_check(struct drm_device *dev, for_each_oldnew_intel_crtc_in_state(state, crtc, old_crtc_state, new_crtc_state, i) { + /* + * crtc's state no longer considered to be inherited + * after the first userspace/client initiated commit. + */ + if (!state->internal) + new_crtc_state->inherited = false; + if (new_crtc_state->inherited != old_crtc_state->inherited) new_crtc_state->uapi.mode_changed = true; @@ -8285,9 +8295,10 @@ static int intel_initial_commit(struct drm_device *dev) drm_modeset_acquire_init(&ctx, 0); -retry: state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; +retry: for_each_intel_crtc(dev, crtc) { struct intel_crtc_state *crtc_state = intel_atomic_get_crtc_state(state, crtc); @@ -8300,15 +8311,6 @@ retry: if (crtc_state->hw.active) { struct intel_encoder *encoder; - /* - * We've not yet detected sink capabilities - * (audio,infoframes,etc.) and thus we don't want to - * force a full state recomputation yet. We want that to - * happen only for the first real commit from userspace. - * So preserve the inherited flag for the time being. - */ - crtc_state->inherited = true; - ret = drm_atomic_add_affected_planes(state, &crtc->base); if (ret) goto out; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index ed02399ee41d..b28c8dc48e24 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -643,6 +643,9 @@ struct intel_atomic_state { struct __intel_global_objs_state *global_objs; int num_global_objs; + /* Internal commit, as opposed to userspace/client initiated one */ + bool internal; + bool dpll_set, modeset; struct intel_shared_dpll_state shared_dpll[I915_NUM_PLLS]; diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c index bc0695c5bb6f..eefa4018dc0c 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c @@ -69,6 +69,7 @@ static void intel_crtc_disable_noatomic(struct intel_crtc *crtc, } state->acquire_ctx = ctx; + to_intel_atomic_state(state)->internal = true; /* Everything's already locked, -EDEADLK can't happen. */ temp_crtc_state = intel_atomic_get_crtc_state(state, crtc); diff --git a/drivers/gpu/drm/i915/display/intel_pipe_crc.c b/drivers/gpu/drm/i915/display/intel_pipe_crc.c index 8d3ea8d7b737..5a468ed6e26c 100644 --- a/drivers/gpu/drm/i915/display/intel_pipe_crc.c +++ b/drivers/gpu/drm/i915/display/intel_pipe_crc.c @@ -293,6 +293,7 @@ intel_crtc_crc_setup_workarounds(struct intel_crtc *crtc, bool enable) } state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; retry: pipe_config = intel_atomic_get_crtc_state(state, crtc); diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index 6badfff2b4a2..3e2d9998566c 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -2149,10 +2149,11 @@ static int intel_psr_fastset_force(struct drm_i915_private *dev_priv) return -ENOMEM; drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); + state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; retry: - drm_connector_list_iter_begin(&dev_priv->drm, &conn_iter); drm_for_each_connector_iter(conn, &conn_iter) { struct drm_connector_state *conn_state; diff --git a/drivers/gpu/drm/i915/display/intel_sprite_uapi.c b/drivers/gpu/drm/i915/display/intel_sprite_uapi.c index 70a391083751..a76b48ebc2d3 100644 --- a/drivers/gpu/drm/i915/display/intel_sprite_uapi.c +++ b/drivers/gpu/drm/i915/display/intel_sprite_uapi.c @@ -86,6 +86,7 @@ int intel_sprite_set_colorkey_ioctl(struct drm_device *dev, void *data, goto out; } state->acquire_ctx = &ctx; + to_intel_atomic_state(state)->internal = true; while (1) { plane_state = drm_atomic_get_plane_state(state, plane); -- cgit v1.2.3 From 51390cc0e00a378b7c152bb6f63efc0a01b59d20 Mon Sep 17 00:00:00 2001 From: Radhakrishna Sripada Date: Thu, 13 Apr 2023 14:24:37 -0700 Subject: drm/i915/mtl: Add Support for C10 PHY message bus and pll programming XELPDP has C10 and C20 phys from Synopsys to drive displays. Each phy has a dedicated PIPE 5.2 Message bus for configuration. This message bus is used to configure the phy internal registers. XELPDP has C10 phys to drive output to the EDP and the native output from the display engine. Add structures, programming hardware state readout logic. Port clock calculations are similar to DG2. Use the DG2 formulae to calculate the port clock but use the relevant pll signals. Note: PHY lane 0 is always used for PLL programming. Add sequences for C10 phy enable/disable phy lane reset, powerdown change sequence and phy lane programming. Bspec: 64539, 64568, 64599, 65100, 65101, 65450, 65451, 67610, 67636 v2: Squash patches related to C10 phy message bus and pll programming support (Jani) Move register definitions to a new file i.e. intel_cx0_reg_defs.h (Jani) Move macro definitions (Jani) DP rates as separate patch (Jani) Spin out xelpdp register definitions into a separate file (Jani) Replace macro to select registers based on phy lane with function calls (Jani) Fix styling issues (Jani) Call XELPDP_PORT_P2M_MSGBUS_STATUS() with port instead of phy (Lucas) v3: Move clear request flag into try-loop v4: On PHY idle change drm_err_once() as drm_dbg_kms() (Jani) use __intel_de_wait_for_register() instead of __intel_wait_for_register and uncomment intel_uncore.h (Jani) Add DP-alt support for PHY lane programming (Khaled) v4: Add tx and cmn on c10mpllb_state (Imre) Add missing waits for pending transactions between two message bus writes (Imre) General cleanups and simplifications (Imre) v5: Few nit cleanups from rev4 (imre) s/dev_priv/i915/ , s/c10mpllb/c10pll/ (RK) Rebase v6: Move the mtl code from intel_c10pll_calc_port_clock to mtl function Fix typo in comment for REG_FIELD_PREP8 definition(Imre) Cc: Mika Kahola Cc: Imre Deak Cc: Uma Shankar Cc: Gustavo Sousa Signed-off-by: Radhakrishna Sripada Signed-off-by: Mika Kahola Reviewed-by: Imre Deak (v4) Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-4-radhakrishna.sripada@intel.com --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/display/intel_cx0_phy.c | 1207 ++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cx0_phy.h | 34 + drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h | 47 +- drivers/gpu/drm/i915/display/intel_ddi.c | 22 +- drivers/gpu/drm/i915/display/intel_display_types.h | 13 + drivers/gpu/drm/i915/display/intel_dpll.c | 33 +- drivers/gpu/drm/i915/display/intel_dpll_mgr.c | 2 +- .../gpu/drm/i915/display/intel_modeset_verify.c | 2 + drivers/gpu/drm/i915/i915_reg.h | 5 + drivers/gpu/drm/i915/i915_reg_defs.h | 57 + 11 files changed, 1411 insertions(+), 12 deletions(-) create mode 100644 drivers/gpu/drm/i915/display/intel_cx0_phy.c create mode 100644 drivers/gpu/drm/i915/display/intel_cx0_phy.h (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 97b0d4ae221a..4ee3b5850dd0 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -298,6 +298,7 @@ i915-y += \ display/icl_dsi.o \ display/intel_backlight.o \ display/intel_crt.o \ + display/intel_cx0_phy.o \ display/intel_ddi.o \ display/intel_ddi_buf_trans.o \ display/intel_display_trace.o \ diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c new file mode 100644 index 000000000000..9ab1e686a40b --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -0,0 +1,1207 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include "i915_reg.h" +#include "intel_cx0_phy.h" +#include "intel_cx0_phy_regs.h" +#include "intel_de.h" +#include "intel_display_types.h" +#include "intel_dp.h" +#include "intel_panel.h" +#include "intel_psr.h" +#include "intel_tc.h" + +#define MB_WRITE_COMMITTED true +#define MB_WRITE_UNCOMMITTED false + +#define for_each_cx0_lane_in_mask(__lane_mask, __lane) \ + for ((__lane) = 0; (__lane) < 2; (__lane)++) \ + for_each_if((__lane_mask) & BIT(__lane)) + +#define INTEL_CX0_LANE0 BIT(0) +#define INTEL_CX0_LANE1 BIT(1) +#define INTEL_CX0_BOTH_LANES (INTEL_CX0_LANE1 | INTEL_CX0_LANE0) + +bool intel_is_c10phy(struct drm_i915_private *i915, enum phy phy) +{ + if (IS_METEORLAKE(i915) && (phy < PHY_C)) + return true; + + return false; +} + +static int lane_mask_to_lane(u8 lane_mask) +{ + if (WARN_ON((lane_mask & ~INTEL_CX0_BOTH_LANES) || + hweight8(lane_mask) != 1)) + return 0; + + return ilog2(lane_mask); +} + +static void +assert_dc_off(struct drm_i915_private *i915) +{ + bool enabled; + + enabled = intel_display_power_is_enabled(i915, POWER_DOMAIN_DC_OFF); + drm_WARN_ON(&i915->drm, !enabled); +} + +/* + * Prepare HW for CX0 phy transactions. + * + * It is required that PSR and DC5/6 are disabled before any CX0 message + * bus transaction is executed. + */ +static intel_wakeref_t intel_cx0_phy_transaction_begin(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + intel_psr_pause(intel_dp); + return intel_display_power_get(i915, POWER_DOMAIN_DC_OFF); +} + +static void intel_cx0_phy_transaction_end(struct intel_encoder *encoder, intel_wakeref_t wakeref) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + intel_psr_resume(intel_dp); + intel_display_power_put(i915, POWER_DOMAIN_DC_OFF, wakeref); +} + +static void intel_clear_response_ready_flag(struct drm_i915_private *i915, + enum port port, int lane) +{ + intel_de_rmw(i915, XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane), + 0, XELPDP_PORT_P2M_RESPONSE_READY | XELPDP_PORT_P2M_ERROR_SET); +} + +static void intel_cx0_bus_reset(struct drm_i915_private *i915, enum port port, int lane) +{ + enum phy phy = intel_port_to_phy(i915, port); + + intel_de_write(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_RESET); + + if (intel_de_wait_for_clear(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_RESET, + XELPDP_MSGBUS_TIMEOUT_SLOW)) { + drm_err_once(&i915->drm, "Failed to bring PHY %c to idle.\n", phy_name(phy)); + return; + } + + intel_clear_response_ready_flag(i915, port, lane); +} + +static int intel_cx0_wait_for_ack(struct drm_i915_private *i915, enum port port, + int command, int lane, u32 *val) +{ + enum phy phy = intel_port_to_phy(i915, port); + + if (__intel_de_wait_for_register(i915, + XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane), + XELPDP_PORT_P2M_RESPONSE_READY, + XELPDP_PORT_P2M_RESPONSE_READY, + XELPDP_MSGBUS_TIMEOUT_FAST_US, + XELPDP_MSGBUS_TIMEOUT_SLOW, val)) { + drm_dbg_kms(&i915->drm, "PHY %c Timeout waiting for message ACK. Status: 0x%x\n", + phy_name(phy), *val); + return -ETIMEDOUT; + } + + if (*val & XELPDP_PORT_P2M_ERROR_SET) { + drm_dbg_kms(&i915->drm, "PHY %c Error occurred during %s command. Status: 0x%x\n", phy_name(phy), + command == XELPDP_PORT_P2M_COMMAND_READ_ACK ? "read" : "write", *val); + intel_cx0_bus_reset(i915, port, lane); + return -EINVAL; + } + + if (REG_FIELD_GET(XELPDP_PORT_P2M_COMMAND_TYPE_MASK, *val) != command) { + drm_dbg_kms(&i915->drm, "PHY %c Not a %s response. MSGBUS Status: 0x%x.\n", phy_name(phy), + command == XELPDP_PORT_P2M_COMMAND_READ_ACK ? "read" : "write", *val); + intel_cx0_bus_reset(i915, port, lane); + return -EINVAL; + } + + return 0; +} + +static int __intel_cx0_read_once(struct drm_i915_private *i915, enum port port, + int lane, u16 addr) +{ + enum phy phy = intel_port_to_phy(i915, port); + int ack; + u32 val; + + if (intel_de_wait_for_clear(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_PENDING, + XELPDP_MSGBUS_TIMEOUT_SLOW)) { + drm_dbg_kms(&i915->drm, + "PHY %c Timeout waiting for previous transaction to complete. Reset the bus and retry.\n", phy_name(phy)); + intel_cx0_bus_reset(i915, port, lane); + return -ETIMEDOUT; + } + + intel_de_write(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_PENDING | + XELPDP_PORT_M2P_COMMAND_READ | + XELPDP_PORT_M2P_ADDRESS(addr)); + + ack = intel_cx0_wait_for_ack(i915, port, XELPDP_PORT_P2M_COMMAND_READ_ACK, lane, &val); + if (ack < 0) { + intel_cx0_bus_reset(i915, port, lane); + return ack; + } + + intel_clear_response_ready_flag(i915, port, lane); + + return REG_FIELD_GET(XELPDP_PORT_P2M_DATA_MASK, val); +} + +static u8 __intel_cx0_read(struct drm_i915_private *i915, enum port port, + int lane, u16 addr) +{ + enum phy phy = intel_port_to_phy(i915, port); + int i, status; + + assert_dc_off(i915); + + /* 3 tries is assumed to be enough to read successfully */ + for (i = 0; i < 3; i++) { + status = __intel_cx0_read_once(i915, port, lane, addr); + + if (status >= 0) + return status; + } + + drm_err_once(&i915->drm, "PHY %c Read %04x failed after %d retries.\n", + phy_name(phy), addr, i); + + return 0; +} + +static u8 intel_cx0_read(struct drm_i915_private *i915, enum port port, + u8 lane_mask, u16 addr) +{ + int lane = lane_mask_to_lane(lane_mask); + + return __intel_cx0_read(i915, port, lane, addr); +} + +static int __intel_cx0_write_once(struct drm_i915_private *i915, enum port port, + int lane, u16 addr, u8 data, bool committed) +{ + enum phy phy = intel_port_to_phy(i915, port); + u32 val; + + if (intel_de_wait_for_clear(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_PENDING, + XELPDP_MSGBUS_TIMEOUT_SLOW)) { + drm_dbg_kms(&i915->drm, + "PHY %c Timeout waiting for previous transaction to complete. Resetting the bus.\n", phy_name(phy)); + intel_cx0_bus_reset(i915, port, lane); + return -ETIMEDOUT; + } + + intel_de_write(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_PENDING | + (committed ? XELPDP_PORT_M2P_COMMAND_WRITE_COMMITTED : + XELPDP_PORT_M2P_COMMAND_WRITE_UNCOMMITTED) | + XELPDP_PORT_M2P_DATA(data) | + XELPDP_PORT_M2P_ADDRESS(addr)); + + if (intel_de_wait_for_clear(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_PENDING, + XELPDP_MSGBUS_TIMEOUT_SLOW)) { + drm_dbg_kms(&i915->drm, + "PHY %c Timeout waiting for write to complete. Resetting the bus.\n", phy_name(phy)); + intel_cx0_bus_reset(i915, port, lane); + return -ETIMEDOUT; + } + + if (committed) { + if (intel_cx0_wait_for_ack(i915, port, XELPDP_PORT_P2M_COMMAND_WRITE_ACK, lane, &val) < 0) { + intel_cx0_bus_reset(i915, port, lane); + return -EINVAL; + } + } else if ((intel_de_read(i915, XELPDP_PORT_P2M_MSGBUS_STATUS(port, lane)) & + XELPDP_PORT_P2M_ERROR_SET)) { + drm_dbg_kms(&i915->drm, + "PHY %c Error occurred during write command.\n", phy_name(phy)); + intel_cx0_bus_reset(i915, port, lane); + return -EINVAL; + } + + intel_clear_response_ready_flag(i915, port, lane); + + return 0; +} + +static void __intel_cx0_write(struct drm_i915_private *i915, enum port port, + int lane, u16 addr, u8 data, bool committed) +{ + enum phy phy = intel_port_to_phy(i915, port); + int i, status; + + assert_dc_off(i915); + + /* 3 tries is assumed to be enough to write successfully */ + for (i = 0; i < 3; i++) { + status = __intel_cx0_write_once(i915, port, lane, addr, data, committed); + + if (status == 0) + return; + } + + drm_err_once(&i915->drm, + "PHY %c Write %04x failed after %d retries.\n", phy_name(phy), addr, i); +} + +static void intel_cx0_write(struct drm_i915_private *i915, enum port port, + u8 lane_mask, u16 addr, u8 data, bool committed) +{ + int lane; + + for_each_cx0_lane_in_mask(lane_mask, lane) + __intel_cx0_write(i915, port, lane, addr, data, committed); +} + +static void __intel_cx0_rmw(struct drm_i915_private *i915, enum port port, + int lane, u16 addr, u8 clear, u8 set, bool committed) +{ + u8 old, val; + + old = __intel_cx0_read(i915, port, lane, addr); + val = (old & ~clear) | set; + + if (val != old) + __intel_cx0_write(i915, port, lane, addr, val, committed); +} + +static void intel_cx0_rmw(struct drm_i915_private *i915, enum port port, + u8 lane_mask, u16 addr, u8 clear, u8 set, bool committed) +{ + u8 lane; + + for_each_cx0_lane_in_mask(lane_mask, lane) + __intel_cx0_rmw(i915, port, lane, addr, clear, set, committed); +} + +/* + * Basic DP link rates with 38.4 MHz reference clock. + * Note: The tables below are with SSC. In non-ssc + * registers 0xC04 to 0xC08(pll[4] to pll[8]) will be + * programmed 0. + */ + +static const struct intel_c10pll_state mtl_c10_dp_rbr = { + .clock = 162000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0xB4, + .pll[1] = 0, + .pll[2] = 0x30, + .pll[3] = 0x1, + .pll[4] = 0x26, + .pll[5] = 0x0C, + .pll[6] = 0x98, + .pll[7] = 0x46, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0xC0, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0x2, + .pll[16] = 0x84, + .pll[17] = 0x4F, + .pll[18] = 0xE5, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_edp_r216 = { + .clock = 216000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0x4, + .pll[1] = 0, + .pll[2] = 0xA2, + .pll[3] = 0x1, + .pll[4] = 0x33, + .pll[5] = 0x10, + .pll[6] = 0x75, + .pll[7] = 0xB3, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0x2, + .pll[16] = 0x85, + .pll[17] = 0x0F, + .pll[18] = 0xE6, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_edp_r243 = { + .clock = 243000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0x34, + .pll[1] = 0, + .pll[2] = 0xDA, + .pll[3] = 0x1, + .pll[4] = 0x39, + .pll[5] = 0x12, + .pll[6] = 0xE3, + .pll[7] = 0xE9, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0x20, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0x2, + .pll[16] = 0x85, + .pll[17] = 0x8F, + .pll[18] = 0xE6, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_dp_hbr1 = { + .clock = 270000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0xF4, + .pll[1] = 0, + .pll[2] = 0xF8, + .pll[3] = 0x0, + .pll[4] = 0x20, + .pll[5] = 0x0A, + .pll[6] = 0x29, + .pll[7] = 0x10, + .pll[8] = 0x1, /* Verify */ + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0xA0, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0x1, + .pll[16] = 0x84, + .pll[17] = 0x4F, + .pll[18] = 0xE5, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_edp_r324 = { + .clock = 324000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0xB4, + .pll[1] = 0, + .pll[2] = 0x30, + .pll[3] = 0x1, + .pll[4] = 0x26, + .pll[5] = 0x0C, + .pll[6] = 0x98, + .pll[7] = 0x46, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0xC0, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0x1, + .pll[16] = 0x85, + .pll[17] = 0x4F, + .pll[18] = 0xE6, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_edp_r432 = { + .clock = 432000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0x4, + .pll[1] = 0, + .pll[2] = 0xA2, + .pll[3] = 0x1, + .pll[4] = 0x33, + .pll[5] = 0x10, + .pll[6] = 0x75, + .pll[7] = 0xB3, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0x1, + .pll[16] = 0x85, + .pll[17] = 0x0F, + .pll[18] = 0xE6, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_dp_hbr2 = { + .clock = 540000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0xF4, + .pll[1] = 0, + .pll[2] = 0xF8, + .pll[3] = 0, + .pll[4] = 0x20, + .pll[5] = 0x0A, + .pll[6] = 0x29, + .pll[7] = 0x10, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0xA0, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0, + .pll[16] = 0x84, + .pll[17] = 0x4F, + .pll[18] = 0xE5, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_edp_r675 = { + .clock = 675000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0xB4, + .pll[1] = 0, + .pll[2] = 0x3E, + .pll[3] = 0x1, + .pll[4] = 0xA8, + .pll[5] = 0x0C, + .pll[6] = 0x33, + .pll[7] = 0x54, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0xC8, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0, + .pll[16] = 0x85, + .pll[17] = 0x8F, + .pll[18] = 0xE6, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state mtl_c10_dp_hbr3 = { + .clock = 810000, + .tx = 0x10, + .cmn = 0x21, + .pll[0] = 0x34, + .pll[1] = 0, + .pll[2] = 0x84, + .pll[3] = 0x1, + .pll[4] = 0x30, + .pll[5] = 0x0F, + .pll[6] = 0x3D, + .pll[7] = 0x98, + .pll[8] = 0x1, + .pll[9] = 0x1, + .pll[10] = 0, + .pll[11] = 0, + .pll[12] = 0xF0, + .pll[13] = 0, + .pll[14] = 0, + .pll[15] = 0, + .pll[16] = 0x84, + .pll[17] = 0x0F, + .pll[18] = 0xE5, + .pll[19] = 0x23, +}; + +static const struct intel_c10pll_state * const mtl_c10_dp_tables[] = { + &mtl_c10_dp_rbr, + &mtl_c10_dp_hbr1, + &mtl_c10_dp_hbr2, + &mtl_c10_dp_hbr3, + NULL, +}; + +static const struct intel_c10pll_state * const mtl_c10_edp_tables[] = { + &mtl_c10_dp_rbr, + &mtl_c10_edp_r216, + &mtl_c10_edp_r243, + &mtl_c10_dp_hbr1, + &mtl_c10_edp_r324, + &mtl_c10_edp_r432, + &mtl_c10_dp_hbr2, + &mtl_c10_edp_r675, + &mtl_c10_dp_hbr3, + NULL, +}; + +static const struct intel_c10pll_state * const * +intel_c10pll_tables_get(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + if (intel_crtc_has_dp_encoder(crtc_state)) { + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP)) + return mtl_c10_edp_tables; + else + return mtl_c10_dp_tables; + } + + /* TODO: Add HDMI Support */ + MISSING_CASE(encoder->type); + return NULL; +} + +static void intel_c10pll_update_pll(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_cx0pll_state *pll_state = &crtc_state->cx0pll_state; + int i; + + if (intel_crtc_has_dp_encoder(crtc_state)) { + if (intel_panel_use_ssc(i915)) { + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + + pll_state->ssc_enabled = + (intel_dp->dpcd[DP_MAX_DOWNSPREAD] & DP_MAX_DOWNSPREAD_0_5); + } + } + + if (pll_state->ssc_enabled) + return; + + drm_WARN_ON(&i915->drm, ARRAY_SIZE(pll_state->c10.pll) < 9); + for (i = 4; i < 9; i++) + pll_state->c10.pll[i] = 0; +} + +static int intel_c10pll_calc_state(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + const struct intel_c10pll_state * const *tables; + int i; + + tables = intel_c10pll_tables_get(crtc_state, encoder); + if (!tables) + return -EINVAL; + + for (i = 0; tables[i]; i++) { + if (crtc_state->port_clock == tables[i]->clock) { + crtc_state->cx0pll_state.c10 = *tables[i]; + intel_c10pll_update_pll(crtc_state, encoder); + + return 0; + } + } + + return -EINVAL; +} + +int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); + + return intel_c10pll_calc_state(crtc_state, encoder); +} + +void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, + struct intel_c10pll_state *pll_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + u8 lane = INTEL_CX0_LANE0; + intel_wakeref_t wakeref; + int i; + + wakeref = intel_cx0_phy_transaction_begin(encoder); + + /* + * According to C10 VDR Register programming Sequence we need + * to do this to read PHY internal registers from MsgBus. + */ + intel_cx0_rmw(i915, encoder->port, lane, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_MSGBUS_ACCESS, + MB_WRITE_COMMITTED); + + for (i = 0; i < ARRAY_SIZE(pll_state->pll); i++) + pll_state->pll[i] = intel_cx0_read(i915, encoder->port, lane, + PHY_C10_VDR_PLL(i)); + + pll_state->cmn = intel_cx0_read(i915, encoder->port, lane, PHY_C10_VDR_CMN(0)); + pll_state->tx = intel_cx0_read(i915, encoder->port, lane, PHY_C10_VDR_TX(0)); + + intel_cx0_phy_transaction_end(encoder, wakeref); +} + +static void intel_c10_pll_program(struct drm_i915_private *i915, + const struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + const struct intel_c10pll_state *pll_state = &crtc_state->cx0pll_state.c10; + int i; + + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_MSGBUS_ACCESS, + MB_WRITE_COMMITTED); + /* Custom width needs to be programmed to 0 for both the phy lanes */ + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CUSTOM_WIDTH, + C10_VDR_CUSTOM_WIDTH_MASK, C10_VDR_CUSTOM_WIDTH_8_10, + MB_WRITE_COMMITTED); + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_UPDATE_CFG, + MB_WRITE_COMMITTED); + + /* Program the pll values only for the master lane */ + for (i = 0; i < ARRAY_SIZE(pll_state->pll); i++) + intel_cx0_write(i915, encoder->port, INTEL_CX0_LANE0, PHY_C10_VDR_PLL(i), + pll_state->pll[i], + (i % 4) ? MB_WRITE_UNCOMMITTED : MB_WRITE_COMMITTED); + + intel_cx0_write(i915, encoder->port, INTEL_CX0_LANE0, PHY_C10_VDR_CMN(0), pll_state->cmn, MB_WRITE_COMMITTED); + intel_cx0_write(i915, encoder->port, INTEL_CX0_LANE0, PHY_C10_VDR_TX(0), pll_state->tx, MB_WRITE_COMMITTED); + + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_LANE0, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_MASTER_LANE | C10_VDR_CTRL_UPDATE_CFG, + MB_WRITE_COMMITTED); +} + +void intel_c10pll_dump_hw_state(struct drm_i915_private *i915, + const struct intel_c10pll_state *hw_state) +{ + bool fracen; + int i; + unsigned int frac_quot = 0, frac_rem = 0, frac_den = 1; + unsigned int multiplier, tx_clk_div; + + fracen = hw_state->pll[0] & C10_PLL0_FRACEN; + drm_dbg_kms(&i915->drm, "c10pll_hw_state: fracen: %s, ", + str_yes_no(fracen)); + + if (fracen) { + frac_quot = hw_state->pll[12] << 8 | hw_state->pll[11]; + frac_rem = hw_state->pll[14] << 8 | hw_state->pll[13]; + frac_den = hw_state->pll[10] << 8 | hw_state->pll[9]; + drm_dbg_kms(&i915->drm, "quot: %u, rem: %u, den: %u,\n", + frac_quot, frac_rem, frac_den); + } + + multiplier = (REG_FIELD_GET8(C10_PLL3_MULTIPLIERH_MASK, hw_state->pll[3]) << 8 | + hw_state->pll[2]) / 2 + 16; + tx_clk_div = REG_FIELD_GET8(C10_PLL15_TXCLKDIV_MASK, hw_state->pll[15]); + drm_dbg_kms(&i915->drm, + "multiplier: %u, tx_clk_div: %u.\n", multiplier, tx_clk_div); + + drm_dbg_kms(&i915->drm, "c10pll_rawhw_state:"); + drm_dbg_kms(&i915->drm, "tx: 0x%x, cmn: 0x%x\n", hw_state->tx, hw_state->cmn); + + BUILD_BUG_ON(ARRAY_SIZE(hw_state->pll) % 4); + for (i = 0; i < ARRAY_SIZE(hw_state->pll); i = i + 4) + drm_dbg_kms(&i915->drm, "pll[%d] = 0x%x, pll[%d] = 0x%x, pll[%d] = 0x%x, pll[%d] = 0x%x\n", + i, hw_state->pll[i], i + 1, hw_state->pll[i + 1], + i + 2, hw_state->pll[i + 2], i + 3, hw_state->pll[i + 3]); +} + +int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c10pll_state *pll_state) +{ + unsigned int frac_quot = 0, frac_rem = 0, frac_den = 1; + unsigned int multiplier, tx_clk_div, refclk = 38400; + + if (pll_state->pll[0] & C10_PLL0_FRACEN) { + frac_quot = pll_state->pll[12] << 8 | pll_state->pll[11]; + frac_rem = pll_state->pll[14] << 8 | pll_state->pll[13]; + frac_den = pll_state->pll[10] << 8 | pll_state->pll[9]; + } + + multiplier = (REG_FIELD_GET8(C10_PLL3_MULTIPLIERH_MASK, pll_state->pll[3]) << 8 | + pll_state->pll[2]) / 2 + 16; + + tx_clk_div = REG_FIELD_GET8(C10_PLL15_TXCLKDIV_MASK, pll_state->pll[15]); + + return DIV_ROUND_CLOSEST_ULL(mul_u32_u32(refclk, (multiplier << 16) + frac_quot) + + DIV_ROUND_CLOSEST(refclk * frac_rem, frac_den), + 10 << (tx_clk_div + 16)); +} + +static void intel_program_port_clock_ctl(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + bool lane_reversal) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + u32 val = 0; + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL1(encoder->port), XELPDP_PORT_REVERSAL, + lane_reversal ? XELPDP_PORT_REVERSAL : 0); + + if (lane_reversal) + val |= XELPDP_LANE1_PHY_CLOCK_SELECT; + + val |= XELPDP_FORWARD_CLOCK_UNGATE; + val |= XELPDP_DDI_CLOCK_SELECT(XELPDP_DDI_CLOCK_SELECT_MAXPCLK); + + /* TODO: HDMI FRL */ + /* TODO: DP2.0 10G and 20G rates enable MPLLA*/ + val |= crtc_state->cx0pll_state.ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; + + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_LANE1_PHY_CLOCK_SELECT | + XELPDP_FORWARD_CLOCK_UNGATE | + XELPDP_DDI_CLOCK_SELECT_MASK | + XELPDP_SSC_ENABLE_PLLB, val); +} + +static u32 intel_cx0_get_powerdown_update(u8 lane_mask) +{ + u32 val = 0; + int lane = 0; + + for_each_cx0_lane_in_mask(lane_mask, lane) + val |= XELPDP_LANE_POWERDOWN_UPDATE(lane); + + return val; +} + +static u32 intel_cx0_get_powerdown_state(u8 lane_mask, u8 state) +{ + u32 val = 0; + int lane = 0; + + for_each_cx0_lane_in_mask(lane_mask, lane) + val |= XELPDP_LANE_POWERDOWN_NEW_STATE(lane, state); + + return val; +} + +static void intel_cx0_powerdown_change_sequence(struct drm_i915_private *i915, + enum port port, + u8 lane_mask, u8 state) +{ + enum phy phy = intel_port_to_phy(i915, port); + int lane; + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + intel_cx0_get_powerdown_state(INTEL_CX0_BOTH_LANES, XELPDP_LANE_POWERDOWN_NEW_STATE_MASK), + intel_cx0_get_powerdown_state(lane_mask, state)); + + /* Wait for pending transactions.*/ + for_each_cx0_lane_in_mask(lane_mask, lane) + if (intel_de_wait_for_clear(i915, XELPDP_PORT_M2P_MSGBUS_CTL(port, lane), + XELPDP_PORT_M2P_TRANSACTION_PENDING, + XELPDP_MSGBUS_TIMEOUT_SLOW)) { + drm_dbg_kms(&i915->drm, + "PHY %c Timeout waiting for previous transaction to complete. Reset the bus.\n", + phy_name(phy)); + intel_cx0_bus_reset(i915, port, lane); + } + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + intel_cx0_get_powerdown_update(INTEL_CX0_BOTH_LANES), + intel_cx0_get_powerdown_update(lane_mask)); + + /* Update Timeout Value */ + if (__intel_de_wait_for_register(i915, XELPDP_PORT_BUF_CTL2(port), + intel_cx0_get_powerdown_update(lane_mask), 0, + XELPDP_PORT_POWERDOWN_UPDATE_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dus.\n", + phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US); +} + +static void intel_cx0_setup_powerdown(struct drm_i915_private *i915, enum port port) +{ + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + XELPDP_POWER_STATE_READY_MASK, + XELPDP_POWER_STATE_READY(CX0_P2_STATE_READY)); + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL3(port), + XELPDP_POWER_STATE_ACTIVE_MASK | + XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, + XELPDP_POWER_STATE_ACTIVE(CX0_P0_STATE_ACTIVE) | + XELPDP_PLL_LANE_STAGGERING_DELAY(0)); +} + +static u32 intel_cx0_get_pclk_refclk_request(u8 lane_mask) +{ + u32 val = 0; + int lane = 0; + + for_each_cx0_lane_in_mask(lane_mask, lane) + val |= XELPDP_LANE_PCLK_REFCLK_REQUEST(lane); + + return val; +} + +static u32 intel_cx0_get_pclk_refclk_ack(u8 lane_mask) +{ + u32 val = 0; + int lane = 0; + + for_each_cx0_lane_in_mask(lane_mask, lane) + val |= XELPDP_LANE_PCLK_REFCLK_ACK(lane); + + return val; +} + +/* FIXME: Some Type-C cases need not reset both the lanes. Handle those cases. */ +static void intel_cx0_phy_lane_reset(struct drm_i915_private *i915, enum port port, + bool lane_reversal) +{ + enum phy phy = intel_port_to_phy(i915, port); + u8 lane_mask = lane_reversal ? INTEL_CX0_LANE1 : + INTEL_CX0_LANE0; + + if (__intel_de_wait_for_register(i915, XELPDP_PORT_BUF_CTL1(port), + XELPDP_PORT_BUF_SOC_PHY_READY, + XELPDP_PORT_BUF_SOC_PHY_READY, + XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to bring out of SOC reset after %dus.\n", + phy_name(phy), XELPDP_PORT_BUF_SOC_READY_TIMEOUT_US); + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + XELPDP_LANE_PIPE_RESET(0) | XELPDP_LANE_PIPE_RESET(1), + XELPDP_LANE_PIPE_RESET(0) | XELPDP_LANE_PIPE_RESET(1)); + + if (__intel_de_wait_for_register(i915, XELPDP_PORT_BUF_CTL2(port), + XELPDP_LANE_PHY_CURRENT_STATUS(0) | + XELPDP_LANE_PHY_CURRENT_STATUS(1), + XELPDP_LANE_PHY_CURRENT_STATUS(0) | + XELPDP_LANE_PHY_CURRENT_STATUS(1), + XELPDP_PORT_RESET_START_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dus.\n", + phy_name(phy), XELPDP_PORT_RESET_START_TIMEOUT_US); + + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(port), + intel_cx0_get_pclk_refclk_request(INTEL_CX0_BOTH_LANES), + intel_cx0_get_pclk_refclk_request(lane_mask)); + + if (__intel_de_wait_for_register(i915, XELPDP_PORT_CLOCK_CTL(port), + intel_cx0_get_pclk_refclk_ack(INTEL_CX0_BOTH_LANES), + intel_cx0_get_pclk_refclk_ack(lane_mask), + XELPDP_REFCLK_ENABLE_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "PHY %c failed to request refclk after %dus.\n", + phy_name(phy), XELPDP_REFCLK_ENABLE_TIMEOUT_US); + + intel_cx0_powerdown_change_sequence(i915, port, INTEL_CX0_BOTH_LANES, + CX0_P2_STATE_RESET); + intel_cx0_setup_powerdown(i915, port); + + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL2(port), + XELPDP_LANE_PIPE_RESET(0) | XELPDP_LANE_PIPE_RESET(1), + 0); + + if (intel_de_wait_for_clear(i915, XELPDP_PORT_BUF_CTL2(port), + XELPDP_LANE_PHY_CURRENT_STATUS(0) | + XELPDP_LANE_PHY_CURRENT_STATUS(1), + XELPDP_PORT_RESET_END_TIMEOUT)) + drm_warn(&i915->drm, "PHY %c failed to bring out of Lane reset after %dms.\n", + phy_name(phy), XELPDP_PORT_RESET_END_TIMEOUT); +} + +static void intel_c10_program_phy_lane(struct drm_i915_private *i915, + struct intel_encoder *encoder, int lane_count, + bool lane_reversal) +{ + u8 l0t1, l0t2, l1t1, l1t2; + bool dp_alt_mode = intel_tc_port_in_dp_alt_mode(enc_to_dig_port(encoder)); + enum port port = encoder->port; + + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_MSGBUS_ACCESS, + MB_WRITE_COMMITTED); + + /* TODO: DP-alt MFD case where only one PHY lane should be programmed. */ + l0t1 = intel_cx0_read(i915, port, INTEL_CX0_LANE0, PHY_CX0_TX_CONTROL(1, 2)); + l0t2 = intel_cx0_read(i915, port, INTEL_CX0_LANE0, PHY_CX0_TX_CONTROL(2, 2)); + l1t1 = intel_cx0_read(i915, port, INTEL_CX0_LANE1, PHY_CX0_TX_CONTROL(1, 2)); + l1t2 = intel_cx0_read(i915, port, INTEL_CX0_LANE1, PHY_CX0_TX_CONTROL(2, 2)); + + l0t1 |= CONTROL2_DISABLE_SINGLE_TX; + l0t2 |= CONTROL2_DISABLE_SINGLE_TX; + l1t1 |= CONTROL2_DISABLE_SINGLE_TX; + l1t2 |= CONTROL2_DISABLE_SINGLE_TX; + + if (lane_reversal) { + switch (lane_count) { + case 4: + l0t1 &= ~CONTROL2_DISABLE_SINGLE_TX; + fallthrough; + case 3: + l0t2 &= ~CONTROL2_DISABLE_SINGLE_TX; + fallthrough; + case 2: + l1t1 &= ~CONTROL2_DISABLE_SINGLE_TX; + fallthrough; + case 1: + l1t2 &= ~CONTROL2_DISABLE_SINGLE_TX; + break; + default: + MISSING_CASE(lane_count); + } + } else { + switch (lane_count) { + case 4: + l1t2 &= ~CONTROL2_DISABLE_SINGLE_TX; + fallthrough; + case 3: + l1t1 &= ~CONTROL2_DISABLE_SINGLE_TX; + fallthrough; + case 2: + l0t2 &= ~CONTROL2_DISABLE_SINGLE_TX; + l0t1 &= ~CONTROL2_DISABLE_SINGLE_TX; + break; + case 1: + if (dp_alt_mode) + l0t2 &= ~CONTROL2_DISABLE_SINGLE_TX; + else + l0t1 &= ~CONTROL2_DISABLE_SINGLE_TX; + break; + default: + MISSING_CASE(lane_count); + } + } + + /* disable MLs */ + intel_cx0_write(i915, port, INTEL_CX0_LANE0, PHY_CX0_TX_CONTROL(1, 2), + l0t1, MB_WRITE_COMMITTED); + intel_cx0_write(i915, port, INTEL_CX0_LANE0, PHY_CX0_TX_CONTROL(2, 2), + l0t2, MB_WRITE_COMMITTED); + intel_cx0_write(i915, port, INTEL_CX0_LANE1, PHY_CX0_TX_CONTROL(1, 2), + l1t1, MB_WRITE_COMMITTED); + intel_cx0_write(i915, port, INTEL_CX0_LANE1, PHY_CX0_TX_CONTROL(2, 2), + l1t2, MB_WRITE_COMMITTED); + + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_UPDATE_CFG, + MB_WRITE_COMMITTED); +} + +static u32 intel_cx0_get_pclk_pll_request(u8 lane_mask) +{ + u32 val = 0; + int lane = 0; + + for_each_cx0_lane_in_mask(lane_mask, lane) + val |= XELPDP_LANE_PCLK_PLL_REQUEST(lane); + + return val; +} + +static u32 intel_cx0_get_pclk_pll_ack(u8 lane_mask) +{ + u32 val = 0; + int lane = 0; + + for_each_cx0_lane_in_mask(lane_mask, lane) + val |= XELPDP_LANE_PCLK_PLL_ACK(lane); + + return val; +} + +static void intel_c10pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL; + u8 maxpclk_lane = lane_reversal ? INTEL_CX0_LANE1 : + INTEL_CX0_LANE0; + + /* + * 1. Program PORT_CLOCK_CTL REGISTER to configure + * clock muxes, gating and SSC + */ + intel_program_port_clock_ctl(encoder, crtc_state, lane_reversal); + + /* 2. Bring PHY out of reset. */ + intel_cx0_phy_lane_reset(i915, encoder->port, lane_reversal); + + /* + * 3. Change Phy power state to Ready. + * TODO: For DP alt mode use only one lane. + */ + intel_cx0_powerdown_change_sequence(i915, encoder->port, INTEL_CX0_BOTH_LANES, + CX0_P2_STATE_READY); + + /* 4. Program PHY internal PLL internal registers. */ + intel_c10_pll_program(i915, crtc_state, encoder); + + /* + * 5. Program the enabled and disabled owned PHY lane + * transmitters over message bus + */ + intel_c10_program_phy_lane(i915, encoder, crtc_state->lane_count, lane_reversal); + + /* + * 6. Follow the Display Voltage Frequency Switching - Sequence + * Before Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 7. Program DDI_CLK_VALFREQ to match intended DDI + * clock frequency. + */ + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), + crtc_state->port_clock); + + /* + * 8. Set PORT_CLOCK_CTL register PCLK PLL Request + * LN to "1" to enable PLL. + */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + intel_cx0_get_pclk_pll_request(INTEL_CX0_BOTH_LANES), + intel_cx0_get_pclk_pll_request(maxpclk_lane)); + + /* 9. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN == "1". */ + if (__intel_de_wait_for_register(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + intel_cx0_get_pclk_pll_ack(INTEL_CX0_BOTH_LANES), + intel_cx0_get_pclk_pll_ack(maxpclk_lane), + XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "Port %c PLL not locked after %dus.\n", + phy_name(phy), XELPDP_PCLK_PLL_ENABLE_TIMEOUT_US); + + /* + * 10. Follow the Display Voltage Frequency Switching Sequence After + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ +} + +void intel_cx0pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + intel_wakeref_t wakeref; + + wakeref = intel_cx0_phy_transaction_begin(encoder); + + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); + intel_c10pll_enable(encoder, crtc_state); + + /* TODO: enable TBT-ALT mode */ + intel_cx0_phy_transaction_end(encoder, wakeref); +} + +static void intel_c10pll_disable(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + /* 1. Change owned PHY lane power to Disable state. */ + intel_cx0_powerdown_change_sequence(i915, encoder->port, INTEL_CX0_BOTH_LANES, + CX0_P2PG_STATE_DISABLE); + + /* + * 2. Follow the Display Voltage Frequency Switching Sequence Before + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 3. Set PORT_CLOCK_CTL register PCLK PLL Request LN + * to "0" to disable PLL. + */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + intel_cx0_get_pclk_pll_request(INTEL_CX0_BOTH_LANES) | + intel_cx0_get_pclk_refclk_request(INTEL_CX0_BOTH_LANES), 0); + + /* 4. Program DDI_CLK_VALFREQ to 0. */ + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), 0); + + /* + * 5. Poll on PORT_CLOCK_CTL PCLK PLL Ack LN == "0". + */ + if (__intel_de_wait_for_register(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + intel_cx0_get_pclk_pll_ack(INTEL_CX0_BOTH_LANES) | + intel_cx0_get_pclk_refclk_ack(INTEL_CX0_BOTH_LANES), 0, + XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US, 0, NULL)) + drm_warn(&i915->drm, "Port %c PLL not unlocked after %dus.\n", + phy_name(phy), XELPDP_PCLK_PLL_DISABLE_TIMEOUT_US); + + /* + * 6. Follow the Display Voltage Frequency Switching Sequence After + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* 7. Program PORT_CLOCK_CTL register to disable and gate clocks. */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_DDI_CLOCK_SELECT_MASK | + XELPDP_FORWARD_CLOCK_UNGATE, 0); +} + +void intel_cx0pll_disable(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + intel_wakeref_t wakeref; + + wakeref = intel_cx0_phy_transaction_begin(encoder); + + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); + intel_c10pll_disable(encoder); + intel_cx0_phy_transaction_end(encoder, wakeref); +} + +void intel_c10pll_state_verify(struct intel_atomic_state *state, + struct intel_crtc_state *new_crtc_state) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_c10pll_state mpllb_hw_state = { 0 }; + struct intel_c10pll_state *mpllb_sw_state = &new_crtc_state->cx0pll_state.c10; + struct intel_crtc *crtc = to_intel_crtc(new_crtc_state->uapi.crtc); + struct intel_encoder *encoder; + enum phy phy; + int i; + + if (DISPLAY_VER(i915) < 14) + return; + + if (!new_crtc_state->hw.active) + return; + + encoder = intel_get_crtc_new_encoder(state, new_crtc_state); + phy = intel_port_to_phy(i915, encoder->port); + + if (!intel_is_c10phy(i915, phy)) + return; + + intel_c10pll_readout_hw_state(encoder, &mpllb_hw_state); + + for (i = 0; i < ARRAY_SIZE(mpllb_sw_state->pll); i++) { + u8 expected = mpllb_sw_state->pll[i]; + + I915_STATE_WARN(mpllb_hw_state.pll[i] != expected, + "[CRTC:%d:%s] mismatch in C10MPLLB: Register[%d] (expected 0x%02x, found 0x%02x)", + crtc->base.base.id, crtc->base.name, + i, expected, mpllb_hw_state.pll[i]); + } + + I915_STATE_WARN(mpllb_hw_state.tx != mpllb_sw_state->tx, + "[CRTC:%d:%s] mismatch in C10MPLLB: Register TX0 (expected 0x%02x, found 0x%02x)", + crtc->base.base.id, crtc->base.name, + mpllb_sw_state->tx, mpllb_hw_state.tx); + + I915_STATE_WARN(mpllb_hw_state.cmn != mpllb_sw_state->cmn, + "[CRTC:%d:%s] mismatch in C10MPLLB: Register CMN0 (expected 0x%02x, found 0x%02x)", + crtc->base.base.id, crtc->base.name, + mpllb_sw_state->cmn, mpllb_hw_state.cmn); +} diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h new file mode 100644 index 000000000000..46fa0576ef0d --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __INTEL_CX0_PHY_H__ +#define __INTEL_CX0_PHY_H__ + +#include +#include +#include + +#include "i915_drv.h" +#include "intel_display_types.h" + +struct drm_i915_private; +struct intel_encoder; +struct intel_crtc_state; +enum phy; + +bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy phy); +void intel_cx0pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state); +void intel_cx0pll_disable(struct intel_encoder *encoder); +void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c10pll_state *pll_state); +int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder); +void intel_c10pll_dump_hw_state(struct drm_i915_private *dev_priv, + const struct intel_c10pll_state *hw_state); +int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c10pll_state *pll_state); +void intel_c10pll_state_verify(struct intel_atomic_state *state, + struct intel_crtc_state *new_crtc_state); + +#endif /* __INTEL_CX0_PHY_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h index 916002aad746..21a699c678a1 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h @@ -96,6 +96,11 @@ #define XELPDP_PLL_LANE_STAGGERING_DELAY(val) REG_FIELD_PREP(XELPDP_PLL_LANE_STAGGERING_DELAY_MASK, val) #define XELPDP_POWER_STATE_ACTIVE_MASK REG_GENMASK(3, 0) #define XELPDP_POWER_STATE_ACTIVE(val) REG_FIELD_PREP(XELPDP_POWER_STATE_ACTIVE_MASK, val) +#define CX0_P0_STATE_ACTIVE 0x0 +#define CX0_P2_STATE_READY 0x2 +#define CX0_P2PG_STATE_DISABLE 0x9 +#define CX0_P4PG_STATE_DISABLE 0xC +#define CX0_P2_STATE_RESET 0x2 #define _XELPDP_PORT_CLOCK_CTL_A 0x640E0 #define _XELPDP_PORT_CLOCK_CTL_B 0x641E0 @@ -106,14 +111,11 @@ _XELPDP_PORT_CLOCK_CTL_B, \ _XELPDP_PORT_CLOCK_CTL_USBC1, \ _XELPDP_PORT_CLOCK_CTL_USBC2)) -#define XELPDP_LANE0_PCLK_PLL_REQUEST REG_BIT(31) -#define XELPDP_LANE0_PCLK_PLL_ACK REG_BIT(30) -#define XELPDP_LANE0_PCLK_REFCLK_REQUEST REG_BIT(29) -#define XELPDP_LANE0_PCLK_REFCLK_ACK REG_BIT(28) -#define XELPDP_LANE1_PCLK_PLL_REQUEST REG_BIT(27) -#define XELPDP_LANE1_PCLK_PLL_ACK REG_BIT(26) -#define XELPDP_LANE1_PCLK_REFCLK_REQUEST REG_BIT(25) -#define XELPDP_LANE1_PCLK_REFCLK_ACK REG_BIT(24) +#define XELPDP_LANE_PCLK_PLL_REQUEST(lane) REG_BIT(31 - ((lane) * 4)) +#define XELPDP_LANE_PCLK_PLL_ACK(lane) REG_BIT(30 - ((lane) * 4)) +#define XELPDP_LANE_PCLK_REFCLK_REQUEST(lane) REG_BIT(29 - ((lane) * 4)) +#define XELPDP_LANE_PCLK_REFCLK_ACK(lane) REG_BIT(28 - ((lane) * 4)) + #define XELPDP_TBT_CLOCK_REQUEST REG_BIT(19) #define XELPDP_TBT_CLOCK_ACK REG_BIT(18) #define XELPDP_DDI_CLOCK_SELECT_MASK REG_GENMASK(15, 12) @@ -130,4 +132,31 @@ #define XELPDP_SSC_ENABLE_PLLA REG_BIT(1) #define XELPDP_SSC_ENABLE_PLLB REG_BIT(0) -#endif /* __INTEL_CX0_PHY_REGS_H__ */ +/* C10 Vendor Registers */ +#define PHY_C10_VDR_PLL(idx) (0xC00 + (idx)) +#define C10_PLL0_FRACEN REG_BIT8(4) +#define C10_PLL3_MULTIPLIERH_MASK REG_GENMASK8(3, 0) +#define C10_PLL15_TXCLKDIV_MASK REG_GENMASK8(2, 0) +#define PHY_C10_VDR_CMN(idx) (0xC20 + (idx)) +#define C10_CMN0_REF_RANGE REG_FIELD_PREP(REG_GENMASK(4, 0), 1) +#define C10_CMN0_REF_CLK_MPLLB_DIV REG_FIELD_PREP(REG_GENMASK(7, 5), 1) +#define C10_CMN3_TXVBOOST_MASK REG_GENMASK8(7, 5) +#define C10_CMN3_TXVBOOST(val) REG_FIELD_PREP8(C10_CMN3_TXVBOOST_MASK, val) +#define PHY_C10_VDR_TX(idx) (0xC30 + (idx)) +#define C10_TX0_TX_MPLLB_SEL REG_BIT(4) +#define PHY_C10_VDR_CONTROL(idx) (0xC70 + (idx) - 1) +#define C10_VDR_CTRL_MSGBUS_ACCESS REG_BIT8(2) +#define C10_VDR_CTRL_MASTER_LANE REG_BIT8(1) +#define C10_VDR_CTRL_UPDATE_CFG REG_BIT8(0) +#define PHY_C10_VDR_CUSTOM_WIDTH 0xD02 +#define C10_VDR_CUSTOM_WIDTH_MASK REG_GENMASK(1, 0) +#define C10_VDR_CUSTOM_WIDTH_8_10 REG_FIELD_PREP(C10_VDR_CUSTOM_WIDTH_MASK, 0) + +/* PHY_C10_VDR_PLL0 */ +#define PLL_C10_MPLL_SSC_EN REG_BIT8(0) + +/* PIPE SPEC Defined Registers */ +#define PHY_CX0_TX_CONTROL(tx, control) (0x400 + ((tx) - 1) * 0x200 + (control)) +#define CONTROL2_DISABLE_SINGLE_TX REG_BIT(6) + +#endif /* __INTEL_CX0_REG_DEFS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index d6fb07821cff..de7cf773440d 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -39,6 +39,7 @@ #include "intel_combo_phy_regs.h" #include "intel_connector.h" #include "intel_crtc.h" +#include "intel_cx0_phy.h" #include "intel_ddi.h" #include "intel_ddi_buf_trans.h" #include "intel_de.h" @@ -3488,6 +3489,21 @@ void intel_ddi_get_clock(struct intel_encoder *encoder, &crtc_state->dpll_hw_state); } +static void mtl_ddi_get_config(struct intel_encoder *encoder, + struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); + + intel_c10pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c10); + intel_c10pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c10); + crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); + + intel_ddi_get_config(encoder, crtc_state); +} + static void dg2_ddi_get_config(struct intel_encoder *encoder, struct intel_crtc_state *crtc_state) { @@ -4396,7 +4412,11 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) encoder->cloneable = 0; encoder->pipe_mask = ~0; - if (IS_DG2(dev_priv)) { + if (DISPLAY_VER(dev_priv) >= 14) { + encoder->enable_clock = intel_cx0pll_enable; + encoder->disable_clock = intel_cx0pll_disable; + encoder->get_config = mtl_ddi_get_config; + } else if (IS_DG2(dev_priv)) { encoder->enable_clock = intel_mpllb_enable; encoder->disable_clock = intel_mpllb_disable; encoder->get_config = dg2_ddi_get_config; diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index b28c8dc48e24..64b6a5ec4b81 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -989,6 +989,18 @@ struct intel_csc_matrix { u16 postoff[3]; }; +struct intel_c10pll_state { + u32 clock; /* in KHz */ + u8 tx; + u8 cmn; + u8 pll[20]; +}; + +struct intel_cx0pll_state { + struct intel_c10pll_state c10; + bool ssc_enabled; +}; + struct intel_crtc_state { /* * uapi (drm) state. This is the software state shown to userspace. @@ -1134,6 +1146,7 @@ struct intel_crtc_state { union { struct intel_dpll_hw_state dpll_hw_state; struct intel_mpllb_state mpllb_state; + struct intel_cx0pll_state cx0pll_state; }; /* diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index 4e9c18be7e1f..a9fbef0fa817 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -8,6 +8,7 @@ #include "i915_reg.h" #include "intel_crtc.h" +#include "intel_cx0_phy.h" #include "intel_de.h" #include "intel_display.h" #include "intel_display_types.h" @@ -995,6 +996,30 @@ static int dg2_crtc_compute_clock(struct intel_atomic_state *state, return 0; } +static int mtl_crtc_compute_clock(struct intel_atomic_state *state, + struct intel_crtc *crtc) +{ + struct drm_i915_private *i915 = to_i915(state->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct intel_encoder *encoder = + intel_get_crtc_new_encoder(state, crtc_state); + enum phy phy = intel_port_to_phy(i915, encoder->port); + int ret; + + ret = intel_cx0pll_calc_state(crtc_state, encoder); + if (ret) + return ret; + + /* TODO: Do the readback via intel_compute_shared_dplls() */ + if (intel_is_c10phy(i915, phy)) + crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); + + crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state); + + return 0; +} + static bool ilk_needs_fb_cb_tune(const struct dpll *dpll, int factor) { return dpll->m < factor * dpll->n; @@ -1423,6 +1448,10 @@ static int i8xx_crtc_compute_clock(struct intel_atomic_state *state, return 0; } +static const struct intel_dpll_funcs mtl_dpll_funcs = { + .crtc_compute_clock = mtl_crtc_compute_clock, +}; + static const struct intel_dpll_funcs dg2_dpll_funcs = { .crtc_compute_clock = dg2_crtc_compute_clock, }; @@ -1517,7 +1546,9 @@ int intel_dpll_crtc_get_shared_dpll(struct intel_atomic_state *state, void intel_dpll_init_clock_hook(struct drm_i915_private *dev_priv) { - if (IS_DG2(dev_priv)) + if (DISPLAY_VER(dev_priv) >= 14) + dev_priv->display.funcs.dpll = &mtl_dpll_funcs; + else if (IS_DG2(dev_priv)) dev_priv->display.funcs.dpll = &dg2_dpll_funcs; else if (DISPLAY_VER(dev_priv) >= 9 || HAS_DDI(dev_priv)) dev_priv->display.funcs.dpll = &hsw_dpll_funcs; diff --git a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c index 22fc908b7e5d..ed372d227aa7 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll_mgr.c +++ b/drivers/gpu/drm/i915/display/intel_dpll_mgr.c @@ -4104,7 +4104,7 @@ void intel_shared_dpll_init(struct drm_i915_private *dev_priv) mutex_init(&dev_priv->display.dpll.lock); - if (IS_DG2(dev_priv)) + if (DISPLAY_VER(dev_priv) >= 14 || IS_DG2(dev_priv)) /* No shared DPLLs on DG2; port PLLs are part of the PHY */ dpll_mgr = NULL; else if (IS_ALDERLAKE_P(dev_priv)) diff --git a/drivers/gpu/drm/i915/display/intel_modeset_verify.c b/drivers/gpu/drm/i915/display/intel_modeset_verify.c index 842d70f0dfd2..5e0ec15d9fd5 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_verify.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_verify.c @@ -11,6 +11,7 @@ #include "intel_atomic.h" #include "intel_crtc.h" #include "intel_crtc_state_dump.h" +#include "intel_cx0_phy.h" #include "intel_display.h" #include "intel_display_types.h" #include "intel_fdi.h" @@ -236,6 +237,7 @@ void intel_modeset_verify_crtc(struct intel_crtc *crtc, verify_crtc_state(crtc, old_crtc_state, new_crtc_state); intel_shared_dpll_state_verify(crtc, old_crtc_state, new_crtc_state); intel_mpllb_state_verify(state, new_crtc_state); + intel_c10pll_state_verify(state, new_crtc_state); } void intel_modeset_verify_disabled(struct drm_i915_private *dev_priv, diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 4511f01ca705..924557294960 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -1799,6 +1799,11 @@ #define CLKGATE_DIS_PSL_EXT(pipe) \ _MMIO_PIPE(pipe, _CLKGATE_DIS_PSL_EXT_A, _CLKGATE_DIS_PSL_EXT_B) +/* DDI Buffer Control */ +#define _DDI_CLK_VALFREQ_A 0x64030 +#define _DDI_CLK_VALFREQ_B 0x64130 +#define DDI_CLK_VALFREQ(port) _MMIO_PORT(port, _DDI_CLK_VALFREQ_A, _DDI_CLK_VALFREQ_B) + /* * Display engine regs */ diff --git a/drivers/gpu/drm/i915/i915_reg_defs.h b/drivers/gpu/drm/i915/i915_reg_defs.h index db26de6b57bc..622d603080f9 100644 --- a/drivers/gpu/drm/i915/i915_reg_defs.h +++ b/drivers/gpu/drm/i915/i915_reg_defs.h @@ -22,6 +22,19 @@ BUILD_BUG_ON_ZERO(__is_constexpr(__n) && \ ((__n) < 0 || (__n) > 31)))) +/** + * REG_BIT8() - Prepare a u8 bit value + * @__n: 0-based bit number + * + * Local wrapper for BIT() to force u8, with compile time checks. + * + * @return: Value with bit @__n set. + */ +#define REG_BIT8(__n) \ + ((u8)(BIT(__n) + \ + BUILD_BUG_ON_ZERO(__is_constexpr(__n) && \ + ((__n) < 0 || (__n) > 7)))) + /** * REG_GENMASK() - Prepare a continuous u32 bitmask * @__high: 0-based high bit @@ -52,6 +65,21 @@ __is_constexpr(__low) && \ ((__low) < 0 || (__high) > 63 || (__low) > (__high))))) +/** + * REG_GENMASK8() - Prepare a continuous u8 bitmask + * @__high: 0-based high bit + * @__low: 0-based low bit + * + * Local wrapper for GENMASK() to force u8, with compile time checks. + * + * @return: Continuous bitmask from @__high to @__low, inclusive. + */ +#define REG_GENMASK8(__high, __low) \ + ((u8)(GENMASK(__high, __low) + \ + BUILD_BUG_ON_ZERO(__is_constexpr(__high) && \ + __is_constexpr(__low) && \ + ((__low) < 0 || (__high) > 7 || (__low) > (__high))))) + /* * Local integer constant expression version of is_power_of_2(). */ @@ -74,6 +102,23 @@ BUILD_BUG_ON_ZERO(!IS_POWER_OF_2((__mask) + (1ULL << __bf_shf(__mask)))) + \ BUILD_BUG_ON_ZERO(__builtin_choose_expr(__is_constexpr(__val), (~((__mask) >> __bf_shf(__mask)) & (__val)), 0)))) +/** + * REG_FIELD_PREP8() - Prepare a u8 bitfield value + * @__mask: shifted mask defining the field's length and position + * @__val: value to put in the field + * + * Local copy of FIELD_PREP() to generate an integer constant expression, force + * u8 and for consistency with REG_FIELD_GET8(), REG_BIT8() and REG_GENMASK8(). + * + * @return: @__val masked and shifted into the field defined by @__mask. + */ +#define REG_FIELD_PREP8(__mask, __val) \ + ((u8)((((typeof(__mask))(__val) << __bf_shf(__mask)) & (__mask)) + \ + BUILD_BUG_ON_ZERO(!__is_constexpr(__mask)) + \ + BUILD_BUG_ON_ZERO((__mask) == 0 || (__mask) > U8_MAX) + \ + BUILD_BUG_ON_ZERO(!IS_POWER_OF_2((__mask) + (1ULL << __bf_shf(__mask)))) + \ + BUILD_BUG_ON_ZERO(__builtin_choose_expr(__is_constexpr(__val), (~((__mask) >> __bf_shf(__mask)) & (__val)), 0)))) + /** * REG_FIELD_GET() - Extract a u32 bitfield value * @__mask: shifted mask defining the field's length and position @@ -155,6 +200,18 @@ */ #define _PICK(__index, ...) (((const u32 []){ __VA_ARGS__ })[__index]) +/** + * REG_FIELD_GET8() - Extract a u8 bitfield value + * @__mask: shifted mask defining the field's length and position + * @__val: value to extract the bitfield value from + * + * Local wrapper for FIELD_GET() to force u8 and for consistency with + * REG_FIELD_PREP(), REG_BIT() and REG_GENMASK(). + * + * @return: Masked and shifted value of the field defined by @__mask in @__val. + */ +#define REG_FIELD_GET8(__mask, __val) ((u8)FIELD_GET(__mask, __val)) + typedef struct { u32 reg; } i915_reg_t; -- cgit v1.2.3 From ea8af87ae6be578b3b633ad6aa9188b0ce4cd7ee Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Thu, 13 Apr 2023 14:24:38 -0700 Subject: drm/i915/mtl: Add vswing programming for C10 phys C10 phys uses direct mapping internally for voltage and pre-emphasis levels. Program the levels directly to the fields in the VDR Registers. Bspec: 65449 v2: From table "C10: Tx EQ settings for DP 1.4x" it shows level 1 and preemphasis 1 instead of two times of level 1 preemphasis 0. Fix this in the driver code as well. v3: VSwing update (Clint) v4: Add vboost termination ctl programming(Imre) Fix tx llogic and other nits Restrict C10 vdr ctl register access for C10 phy(RK) v5: Program vboots, termination ctl for both lanes(Imre) Cc: Imre Deak Cc: Uma Shankar Signed-off-by: Clint Taylor Signed-off-by: Radhakrishna Sripada Signed-off-by: Mika Kahola Reviewed-by: Imre Deak (v3) Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-5-radhakrishna.sripada@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 102 ++++++++++++++++++++- drivers/gpu/drm/i915/display/intel_cx0_phy.h | 2 + drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h | 14 ++- drivers/gpu/drm/i915/display/intel_ddi.c | 4 +- drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c | 31 ++++++- 5 files changed, 143 insertions(+), 10 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 9ab1e686a40b..5ffd661fa507 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -6,6 +6,8 @@ #include "i915_reg.h" #include "intel_cx0_phy.h" #include "intel_cx0_phy_regs.h" +#include "intel_ddi.h" +#include "intel_ddi_buf_trans.h" #include "intel_de.h" #include "intel_display_types.h" #include "intel_dp.h" @@ -292,6 +294,97 @@ static void intel_cx0_rmw(struct drm_i915_private *i915, enum port port, __intel_cx0_rmw(i915, port, lane, addr, clear, set, committed); } +static u8 intel_c10_get_tx_vboost_lvl(const struct intel_crtc_state *crtc_state) +{ + if (intel_crtc_has_dp_encoder(crtc_state)) { + if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP) && + (crtc_state->port_clock == 540000 || + crtc_state->port_clock == 810000)) + return 5; + else + return 4; + } else { + return 5; + } +} + +static u8 intel_c10_get_tx_term_ctl(const struct intel_crtc_state *crtc_state) +{ + if (intel_crtc_has_dp_encoder(crtc_state)) { + if (!intel_crtc_has_type(crtc_state, INTEL_OUTPUT_EDP) && + (crtc_state->port_clock == 540000 || + crtc_state->port_clock == 810000)) + return 5; + else + return 2; + } else { + return 6; + } +} + +void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + const struct intel_ddi_buf_trans *trans; + enum phy phy = intel_port_to_phy(i915, encoder->port); + intel_wakeref_t wakeref; + int n_entries, ln; + + wakeref = intel_cx0_phy_transaction_begin(encoder); + + trans = encoder->get_buf_trans(encoder, crtc_state, &n_entries); + if (drm_WARN_ON_ONCE(&i915->drm, !trans)) { + intel_cx0_phy_transaction_end(encoder, wakeref); + return; + } + + if (intel_is_c10phy(i915, phy)) { + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_MSGBUS_ACCESS, MB_WRITE_COMMITTED); + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CMN(3), + C10_CMN3_TXVBOOST_MASK, + C10_CMN3_TXVBOOST(intel_c10_get_tx_vboost_lvl(crtc_state)), + MB_WRITE_UNCOMMITTED); + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_TX(1), + C10_TX1_TERMCTL_MASK, + C10_TX1_TERMCTL(intel_c10_get_tx_term_ctl(crtc_state)), + MB_WRITE_COMMITTED); + } + + for (ln = 0; ln < crtc_state->lane_count; ln++) { + int level = intel_ddi_level(encoder, crtc_state, ln); + int lane, tx; + + lane = ln / 2; + tx = ln % 2; + + intel_cx0_rmw(i915, encoder->port, BIT(lane), PHY_CX0_VDROVRD_CTL(lane, tx, 0), + C10_PHY_OVRD_LEVEL_MASK, + C10_PHY_OVRD_LEVEL(trans->entries[level].snps.pre_cursor), + MB_WRITE_COMMITTED); + intel_cx0_rmw(i915, encoder->port, BIT(lane), PHY_CX0_VDROVRD_CTL(lane, tx, 1), + C10_PHY_OVRD_LEVEL_MASK, + C10_PHY_OVRD_LEVEL(trans->entries[level].snps.vswing), + MB_WRITE_COMMITTED); + intel_cx0_rmw(i915, encoder->port, BIT(lane), PHY_CX0_VDROVRD_CTL(lane, tx, 2), + C10_PHY_OVRD_LEVEL_MASK, + C10_PHY_OVRD_LEVEL(trans->entries[level].snps.post_cursor), + MB_WRITE_COMMITTED); + } + + /* Write Override enables in 0xD71 */ + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_OVRD, + 0, PHY_C10_VDR_OVRD_TX1 | PHY_C10_VDR_OVRD_TX2, + MB_WRITE_COMMITTED); + + if (intel_is_c10phy(i915, phy)) + intel_cx0_rmw(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), + 0, C10_VDR_CTRL_UPDATE_CFG, MB_WRITE_COMMITTED); + + intel_cx0_phy_transaction_end(encoder, wakeref); +} + /* * Basic DP link rates with 38.4 MHz reference clock. * Note: The tables below are with SSC. In non-ssc @@ -766,10 +859,8 @@ static void intel_program_port_clock_ctl(struct intel_encoder *encoder, val |= crtc_state->cx0pll_state.ssc_enabled ? XELPDP_SSC_ENABLE_PLLB : 0; intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), - XELPDP_LANE1_PHY_CLOCK_SELECT | - XELPDP_FORWARD_CLOCK_UNGATE | - XELPDP_DDI_CLOCK_SELECT_MASK | - XELPDP_SSC_ENABLE_PLLB, val); + XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | + XELPDP_DDI_CLOCK_SELECT_MASK | XELPDP_SSC_ENABLE_PLLB, val); } static u32 intel_cx0_get_powerdown_update(u8 lane_mask) @@ -1144,7 +1235,8 @@ static void intel_c10pll_disable(struct intel_encoder *encoder) /* 7. Program PORT_CLOCK_CTL register to disable and gate clocks. */ intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), - XELPDP_DDI_CLOCK_SELECT_MASK | + XELPDP_DDI_CLOCK_SELECT_MASK, 0); + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), XELPDP_FORWARD_CLOCK_UNGATE, 0); } diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 46fa0576ef0d..6b736acb83e0 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -30,5 +30,7 @@ int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, const struct intel_c10pll_state *pll_state); void intel_c10pll_state_verify(struct intel_atomic_state *state, struct intel_crtc_state *new_crtc_state); +void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state); #endif /* __INTEL_CX0_PHY_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h index 21a699c678a1..9cfa7f508c90 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h @@ -144,6 +144,8 @@ #define C10_CMN3_TXVBOOST(val) REG_FIELD_PREP8(C10_CMN3_TXVBOOST_MASK, val) #define PHY_C10_VDR_TX(idx) (0xC30 + (idx)) #define C10_TX0_TX_MPLLB_SEL REG_BIT(4) +#define C10_TX1_TERMCTL_MASK REG_GENMASK8(7, 5) +#define C10_TX1_TERMCTL(val) REG_FIELD_PREP8(C10_TX1_TERMCTL_MASK, val) #define PHY_C10_VDR_CONTROL(idx) (0xC70 + (idx) - 1) #define C10_VDR_CTRL_MSGBUS_ACCESS REG_BIT8(2) #define C10_VDR_CTRL_MASTER_LANE REG_BIT8(1) @@ -151,9 +153,15 @@ #define PHY_C10_VDR_CUSTOM_WIDTH 0xD02 #define C10_VDR_CUSTOM_WIDTH_MASK REG_GENMASK(1, 0) #define C10_VDR_CUSTOM_WIDTH_8_10 REG_FIELD_PREP(C10_VDR_CUSTOM_WIDTH_MASK, 0) - -/* PHY_C10_VDR_PLL0 */ -#define PLL_C10_MPLL_SSC_EN REG_BIT8(0) +#define PHY_C10_VDR_OVRD 0xD71 +#define PHY_C10_VDR_OVRD_TX1 REG_BIT8(0) +#define PHY_C10_VDR_OVRD_TX2 REG_BIT8(2) +#define PHY_C10_VDR_PRE_OVRD_TX1 0xD80 +#define C10_PHY_OVRD_LEVEL_MASK REG_GENMASK8(5, 0) +#define C10_PHY_OVRD_LEVEL(val) REG_FIELD_PREP8(C10_PHY_OVRD_LEVEL_MASK, val) +#define PHY_CX0_VDROVRD_CTL(lane, tx, control) \ + (PHY_C10_VDR_PRE_OVRD_TX1 + \ + ((lane) ^ (tx)) * 0x10 + (control)) /* PIPE SPEC Defined Registers */ #define PHY_CX0_TX_CONTROL(tx, control) (0x400 + ((tx) - 1) * 0x200 + (control)) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index de7cf773440d..0a057624ae7c 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4476,7 +4476,9 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) encoder->get_config = hsw_ddi_get_config; } - if (IS_DG2(dev_priv)) { + if (DISPLAY_VER(dev_priv) >= 14) { + encoder->set_signal_levels = intel_cx0_phy_set_signal_levels; + } else if (IS_DG2(dev_priv)) { encoder->set_signal_levels = intel_snps_phy_set_signal_levels; } else if (DISPLAY_VER(dev_priv) >= 12) { if (intel_phy_is_combo(dev_priv, phy)) diff --git a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c index 006a2e979000..cd4becbae098 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c +++ b/drivers/gpu/drm/i915/display/intel_ddi_buf_trans.c @@ -1035,6 +1035,25 @@ static const struct intel_ddi_buf_trans dg2_snps_trans_uhbr = { .num_entries = ARRAY_SIZE(_dg2_snps_trans_uhbr), }; +static const union intel_ddi_buf_trans_entry _mtl_c10_trans_dp14[] = { + { .snps = { 26, 0, 0 } }, /* preset 0 */ + { .snps = { 33, 0, 6 } }, /* preset 1 */ + { .snps = { 38, 0, 11 } }, /* preset 2 */ + { .snps = { 43, 0, 19 } }, /* preset 3 */ + { .snps = { 39, 0, 0 } }, /* preset 4 */ + { .snps = { 45, 0, 7 } }, /* preset 5 */ + { .snps = { 46, 0, 13 } }, /* preset 6 */ + { .snps = { 46, 0, 0 } }, /* preset 7 */ + { .snps = { 55, 0, 7 } }, /* preset 8 */ + { .snps = { 62, 0, 0 } }, /* preset 9 */ +}; + +static const struct intel_ddi_buf_trans mtl_cx0c10_trans = { + .entries = _mtl_c10_trans_dp14, + .num_entries = ARRAY_SIZE(_mtl_c10_trans_dp14), + .hdmi_default_entry = ARRAY_SIZE(_mtl_c10_trans_dp14) - 1, +}; + bool is_hobl_buf_trans(const struct intel_ddi_buf_trans *table) { return table == &tgl_combo_phy_trans_edp_hbr2_hobl; @@ -1606,12 +1625,22 @@ dg2_get_snps_buf_trans(struct intel_encoder *encoder, return intel_get_buf_trans(&dg2_snps_trans, n_entries); } +static const struct intel_ddi_buf_trans * +mtl_get_cx0_buf_trans(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + int *n_entries) +{ + return intel_get_buf_trans(&mtl_cx0c10_trans, n_entries); +} + void intel_ddi_buf_trans_init(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); enum phy phy = intel_port_to_phy(i915, encoder->port); - if (IS_DG2(i915)) { + if (DISPLAY_VER(i915) >= 14) { + encoder->get_buf_trans = mtl_get_cx0_buf_trans; + } else if (IS_DG2(i915)) { encoder->get_buf_trans = dg2_get_snps_buf_trans; } else if (IS_ALDERLAKE_P(i915)) { if (intel_phy_is_combo(i915, phy)) -- cgit v1.2.3 From 23ef61946374a9ba52ae051cbc95e82f054ea16b Mon Sep 17 00:00:00 2001 From: José Roberto de Souza Date: Thu, 13 Apr 2023 14:24:40 -0700 Subject: drm/i915/mtl/display: Implement DisplayPort sequences MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The differences between MTL and TGL DP sequences are big enough to MTL have its own functions. Also it is much easier to follow MTL sequences against spec with its own functions. One change worthy to mention is the move of 'intel_display_power_get(dev_priv, dig_port->ddi_io_power_domain)'. This call is not necessary for MTL but we have _put() counter part in intel_ddi_post_disable_dp() that needs to balanced. We could add a display version check on it but instead here it is moving it to intel_ddi_pre_enable_dp() so it is executed for all platforms in a single place and this will not cause any harm in MTL and newer platforms. v2: - Fix logic to wait for buf idle. - Use the right register to wait for ddi active.(RK) v3: - Increase wait timeout for ddi buf active (Mika) v4: - Increase idle timeout for ddi buf idle (Mika) v5: use rmw in mtl_disable_ddi_buf. Donot clear link training mask(Imre) BSpec: 65448 65505 Cc: Matt Roper Cc: Satyeshwar Singh Cc: Clint Taylor Cc: Ankit Nautiyal Cc: Imre Deak Signed-off-by: Radhakrishna Sripada Signed-off-by: José Roberto de Souza Signed-off-by: Mika Kahola Reviewed-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-7-radhakrishna.sripada@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h | 8 + drivers/gpu/drm/i915/display/intel_ddi.c | 344 +++++++++++++++++++++- drivers/gpu/drm/i915/i915_reg.h | 5 + 3 files changed, 345 insertions(+), 12 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h index 9cfa7f508c90..fe2e3edef69b 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h @@ -59,8 +59,16 @@ _XELPDP_PORT_BUF_CTL1_LN0_B, \ _XELPDP_PORT_BUF_CTL1_LN0_USBC1, \ _XELPDP_PORT_BUF_CTL1_LN0_USBC2)) +#define XELPDP_PORT_BUF_D2D_LINK_ENABLE REG_BIT(29) +#define XELPDP_PORT_BUF_D2D_LINK_STATE REG_BIT(28) #define XELPDP_PORT_BUF_SOC_PHY_READY REG_BIT(24) +#define XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK REG_GENMASK(19, 18) +#define XELPDP_PORT_BUF_PORT_DATA_10BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 0) +#define XELPDP_PORT_BUF_PORT_DATA_20BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 1) +#define XELPDP_PORT_BUF_PORT_DATA_40BIT REG_FIELD_PREP(XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK, 2) #define XELPDP_PORT_REVERSAL REG_BIT(16) +#define XELPDP_PORT_BUF_IO_SELECT_TBT REG_BIT(11) +#define XELPDP_PORT_BUF_PHY_IDLE REG_BIT(7) #define XELPDP_TC_PHY_OWNERSHIP REG_BIT(6) #define XELPDP_TCSS_POWER_REQUEST REG_BIT(5) #define XELPDP_TCSS_POWER_STATE REG_BIT(4) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 0a057624ae7c..842095ae3e0b 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -40,6 +40,7 @@ #include "intel_connector.h" #include "intel_crtc.h" #include "intel_cx0_phy.h" +#include "intel_cx0_phy_regs.h" #include "intel_ddi.h" #include "intel_ddi_buf_trans.h" #include "intel_de.h" @@ -169,6 +170,18 @@ static void hsw_prepare_hdmi_ddi_buffers(struct intel_encoder *encoder, trans->entries[level].hsw.trans2); } +static void mtl_wait_ddi_buf_idle(struct drm_i915_private *i915, enum port port) +{ + int ret; + + /* FIXME: find out why Bspec's 100us timeout is too short */ + ret = wait_for_us((intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)) & + XELPDP_PORT_BUF_PHY_IDLE), 10000); + if (ret) + drm_err(&i915->drm, "Timeout waiting for DDI BUF %c to get idle\n", + port_name(port)); +} + void intel_wait_ddi_buf_idle(struct drm_i915_private *dev_priv, enum port port) { @@ -196,7 +209,9 @@ static void intel_wait_ddi_buf_active(struct drm_i915_private *dev_priv, return; } - if (IS_DG2(dev_priv)) { + if (DISPLAY_VER(dev_priv) >= 14) { + timeout_us = 10000; + } else if (IS_DG2(dev_priv)) { timeout_us = 1200; } else if (DISPLAY_VER(dev_priv) >= 12) { if (intel_phy_is_tc(dev_priv, phy)) @@ -207,8 +222,12 @@ static void intel_wait_ddi_buf_active(struct drm_i915_private *dev_priv, timeout_us = 500; } - ret = _wait_for(!(intel_de_read(dev_priv, DDI_BUF_CTL(port)) & - DDI_BUF_IS_IDLE), timeout_us, 10, 10); + if (DISPLAY_VER(dev_priv) >= 14) + ret = _wait_for(!(intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & XELPDP_PORT_BUF_PHY_IDLE), + timeout_us, 10, 10); + else + ret = _wait_for(!(intel_de_read(dev_priv, DDI_BUF_CTL(port)) & DDI_BUF_IS_IDLE), + timeout_us, 10, 10); if (ret) drm_err(&dev_priv->drm, "Timeout waiting for DDI BUF %c to get active\n", @@ -313,6 +332,13 @@ static void intel_ddi_init_dp_buf_reg(struct intel_encoder *encoder, DDI_PORT_WIDTH(crtc_state->lane_count) | DDI_BUF_TRANS_SELECT(0); + if (DISPLAY_VER(i915) >= 14) { + if (intel_dp_is_uhbr(crtc_state)) + intel_dp->DP |= DDI_BUF_PORT_DATA_40BIT; + else + intel_dp->DP |= DDI_BUF_PORT_DATA_10BIT; + } + if (IS_ALDERLAKE_P(i915) && intel_phy_is_tc(i915, phy)) { intel_dp->DP |= ddi_buf_phy_link_rate(crtc_state->port_clock); if (!intel_tc_port_in_tbt_alt_mode(dig_port)) @@ -2309,6 +2335,179 @@ static void intel_ddi_mso_configure(const struct intel_crtc_state *crtc_state) OVERLAP_PIXELS_MASK, dss1); } +static u8 mtl_get_port_width(u8 lane_count) +{ + switch (lane_count) { + case 1: + return 0; + case 2: + return 1; + case 3: + return 4; + case 4: + return 3; + default: + MISSING_CASE(lane_count); + return 4; + } +} + +static void +mtl_ddi_enable_d2d(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), 0, + XELPDP_PORT_BUF_D2D_LINK_ENABLE); + + if (wait_for_us((intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & + XELPDP_PORT_BUF_D2D_LINK_STATE), 100)) { + drm_err(&dev_priv->drm, "Timeout waiting for D2D Link enable for PORT_BUF_CTL %c\n", + port_name(port)); + } +} + +static void mtl_port_buf_ctl_program(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + enum port port = encoder->port; + u32 val; + + val = intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)); + val &= ~XELPDP_PORT_WIDTH_MASK; + val |= XELPDP_PORT_WIDTH(mtl_get_port_width(crtc_state->lane_count)); + + val &= ~XELPDP_PORT_BUF_PORT_DATA_WIDTH_MASK; + if (intel_dp_is_uhbr(crtc_state)) + val |= XELPDP_PORT_BUF_PORT_DATA_40BIT; + else + val |= XELPDP_PORT_BUF_PORT_DATA_10BIT; + + if (dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL) + val |= XELPDP_PORT_REVERSAL; + + intel_de_write(i915, XELPDP_PORT_BUF_CTL1(port), val); +} + +static void mtl_port_buf_ctl_io_selection(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + u32 val; + + val = intel_tc_port_in_tbt_alt_mode(dig_port) ? + XELPDP_PORT_BUF_IO_SELECT_TBT : 0; + intel_de_rmw(i915, XELPDP_PORT_BUF_CTL1(encoder->port), + XELPDP_PORT_BUF_IO_SELECT_TBT, val); +} + +static void mtl_ddi_pre_enable_dp(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + bool is_mst = intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST); + + intel_dp_set_link_params(intel_dp, + crtc_state->port_clock, + crtc_state->lane_count); + + /* + * We only configure what the register value will be here. Actual + * enabling happens during link training farther down. + */ + intel_ddi_init_dp_buf_reg(encoder, crtc_state); + + /* + * 1. Enable Power Wells + * + * This was handled at the beginning of intel_atomic_commit_tail(), + * before we called down into this function. + */ + + /* 2. PMdemand was already set */ + + /* 3. Select Thunderbolt */ + mtl_port_buf_ctl_io_selection(encoder); + + /* 4. Enable Panel Power if PPS is required */ + intel_pps_on(intel_dp); + + /* 5. Enable the port PLL */ + intel_ddi_enable_clock(encoder, crtc_state); + + /* + * 6.a Configure Transcoder Clock Select to direct the Port clock to the + * Transcoder. + */ + intel_ddi_enable_transcoder_clock(encoder, crtc_state); + + /* + * 6.b If DP v2.0/128b mode - Configure TRANS_DP2_CTL register settings. + */ + intel_ddi_config_transcoder_dp2(encoder, crtc_state); + + /* + * 6.c Configure TRANS_DDI_FUNC_CTL DDI Select, DDI Mode Select & MST + * Transport Select + */ + intel_ddi_config_transcoder_func(encoder, crtc_state); + + /* + * 6.e Program CoG/MSO configuration bits in DSS_CTL1 if selected. + */ + intel_ddi_mso_configure(crtc_state); + + if (!is_mst) + intel_dp_set_power(intel_dp, DP_SET_POWER_D0); + + intel_dp_configure_protocol_converter(intel_dp, crtc_state); + intel_dp_sink_set_decompression_state(intel_dp, crtc_state, true); + /* + * DDI FEC: "anticipates enabling FEC encoding sets the FEC_READY bit + * in the FEC_CONFIGURATION register to 1 before initiating link + * training + */ + intel_dp_sink_set_fec_ready(intel_dp, crtc_state); + + intel_dp_check_frl_training(intel_dp); + intel_dp_pcon_dsc_configure(intel_dp, crtc_state); + + /* + * 6. The rest of the below are substeps under the bspec's "Enable and + * Train Display Port" step. Note that steps that are specific to + * MST will be handled by intel_mst_pre_enable_dp() before/after it + * calls into this function. Also intel_mst_pre_enable_dp() only calls + * us when active_mst_links==0, so any steps designated for "single + * stream or multi-stream master transcoder" can just be performed + * unconditionally here. + * + * mtl_ddi_prepare_link_retrain() that is called by + * intel_dp_start_link_train() will execute steps: 6.d, 6.f, 6.g, 6.h, + * 6.i and 6.j + * + * 6.k Follow DisplayPort specification training sequence (see notes for + * failure handling) + * 6.m If DisplayPort multi-stream - Set DP_TP_CTL link training to Idle + * Pattern, wait for 5 idle patterns (DP_TP_STATUS Min_Idles_Sent) + * (timeout after 800 us) + */ + intel_dp_start_link_train(intel_dp, crtc_state); + + /* 6.n Set DP_TP_CTL link training to Normal */ + if (!is_trans_port_sync_mode(crtc_state)) + intel_dp_stop_link_train(intel_dp, crtc_state); + + /* 6.o Configure and enable FEC if needed */ + intel_ddi_enable_fec(encoder, crtc_state); + + intel_dsc_dp_pps_write(encoder, crtc_state); +} + static void tgl_ddi_pre_enable_dp(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, @@ -2523,7 +2722,9 @@ static void intel_ddi_pre_enable_dp(struct intel_atomic_state *state, intel_dp_128b132b_sdp_crc16(enc_to_intel_dp(encoder), crtc_state); - if (DISPLAY_VER(dev_priv) >= 12) + if (DISPLAY_VER(dev_priv) >= 14) + mtl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); + else if (DISPLAY_VER(dev_priv) >= 12) tgl_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); else hsw_ddi_pre_enable_dp(state, encoder, crtc_state, conn_state); @@ -2604,8 +2805,50 @@ static void intel_ddi_pre_enable(struct intel_atomic_state *state, } } -static void intel_disable_ddi_buf(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +static void +mtl_ddi_disable_d2d_link(struct intel_encoder *encoder) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), + XELPDP_PORT_BUF_D2D_LINK_ENABLE, 0); + + if (wait_for_us(!(intel_de_read(dev_priv, XELPDP_PORT_BUF_CTL1(port)) & + XELPDP_PORT_BUF_D2D_LINK_STATE), 100)) + drm_err(&dev_priv->drm, "Timeout waiting for D2D Link disable for PORT_BUF_CTL %c\n", + port_name(port)); +} + +static void mtl_disable_ddi_buf(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + u32 val; + + /* 3.b Clear DDI_CTL_DE Enable to 0. */ + val = intel_de_read(dev_priv, DDI_BUF_CTL(port)); + if (val & DDI_BUF_CTL_ENABLE) { + val &= ~DDI_BUF_CTL_ENABLE; + intel_de_write(dev_priv, DDI_BUF_CTL(port), val); + + /* 3.c Poll for PORT_BUF_CTL Idle Status == 1, timeout after 100us */ + mtl_wait_ddi_buf_idle(dev_priv, port); + } + + /* 3.d Disable D2D Link */ + mtl_ddi_disable_d2d_link(encoder); + + /* 3.e Disable DP_TP_CTL */ + if (intel_crtc_has_dp_encoder(crtc_state)) { + intel_de_rmw(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), + DP_TP_CTL_ENABLE, 0); + } +} + +static void disable_ddi_buf(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); enum port port = encoder->port; @@ -2630,6 +2873,21 @@ static void intel_disable_ddi_buf(struct intel_encoder *encoder, intel_wait_ddi_buf_idle(dev_priv, port); } +static void intel_disable_ddi_buf(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + + if (DISPLAY_VER(dev_priv) >= 14) { + mtl_disable_ddi_buf(encoder, crtc_state); + + /* 3.f Disable DP_TP_CTL FEC Enable if it is needed */ + intel_ddi_disable_fec_state(encoder, crtc_state); + } else { + disable_ddi_buf(encoder, crtc_state); + } +} + static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, struct intel_encoder *encoder, const struct intel_crtc_state *old_crtc_state, @@ -2638,6 +2896,7 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); struct intel_dp *intel_dp = &dig_port->dp; + intel_wakeref_t wakeref; bool is_mst = intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_DP_MST); @@ -2677,12 +2936,19 @@ static void intel_ddi_post_disable_dp(struct intel_atomic_state *state, intel_pps_vdd_on(intel_dp); intel_pps_off(intel_dp); - if (!intel_tc_port_in_tbt_alt_mode(dig_port)) + wakeref = fetch_and_zero(&dig_port->ddi_io_wakeref); + + if (wakeref) intel_display_power_put(dev_priv, dig_port->ddi_io_power_domain, - fetch_and_zero(&dig_port->ddi_io_wakeref)); + wakeref); intel_ddi_disable_clock(encoder); + + /* De-select Thunderbolt */ + if (DISPLAY_VER(dev_priv) >= 14) + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(encoder->port), + XELPDP_PORT_BUF_IO_SELECT_TBT, 0); } static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, @@ -2693,6 +2959,7 @@ static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct intel_digital_port *dig_port = enc_to_dig_port(encoder); struct intel_hdmi *intel_hdmi = &dig_port->hdmi; + intel_wakeref_t wakeref; dig_port->set_infoframes(encoder, false, old_crtc_state, old_conn_state); @@ -2705,9 +2972,11 @@ static void intel_ddi_post_disable_hdmi(struct intel_atomic_state *state, if (DISPLAY_VER(dev_priv) >= 12) intel_ddi_disable_transcoder_clock(old_crtc_state); - intel_display_power_put(dev_priv, - dig_port->ddi_io_power_domain, - fetch_and_zero(&dig_port->ddi_io_wakeref)); + wakeref = fetch_and_zero(&dig_port->ddi_io_wakeref); + if (wakeref) + intel_display_power_put(dev_priv, + dig_port->ddi_io_power_domain, + wakeref); intel_ddi_disable_clock(encoder); @@ -3104,6 +3373,53 @@ static void adlp_tbt_to_dp_alt_switch_wa(struct intel_encoder *encoder) intel_dkl_phy_rmw(i915, DKL_PCS_DW5(tc_port, ln), DKL_PCS_DW5_CORE_SOFTRESET, 0); } +static void mtl_ddi_prepare_link_retrain(struct intel_dp *intel_dp, + const struct intel_crtc_state *crtc_state) +{ + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct intel_encoder *encoder = &dig_port->base; + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + enum port port = encoder->port; + u32 dp_tp_ctl; + + /* + * TODO: To train with only a different voltage swing entry is not + * necessary disable and enable port + */ + dp_tp_ctl = intel_de_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); + if (dp_tp_ctl & DP_TP_CTL_ENABLE) + mtl_disable_ddi_buf(encoder, crtc_state); + + /* 6.d Configure and enable DP_TP_CTL with link training pattern 1 selected */ + dp_tp_ctl = DP_TP_CTL_ENABLE | DP_TP_CTL_LINK_TRAIN_PAT1; + if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_DP_MST)) { + dp_tp_ctl |= DP_TP_CTL_MODE_MST; + } else { + dp_tp_ctl |= DP_TP_CTL_MODE_SST; + if (drm_dp_enhanced_frame_cap(intel_dp->dpcd)) + dp_tp_ctl |= DP_TP_CTL_ENHANCED_FRAME_ENABLE; + } + intel_de_write(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), dp_tp_ctl); + intel_de_posting_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); + + /* 6.f Enable D2D Link */ + mtl_ddi_enable_d2d(encoder); + + /* 6.g Configure voltage swing and related IO settings */ + encoder->set_signal_levels(encoder, crtc_state); + + /* 6.h Configure PORT_BUF_CTL1 */ + mtl_port_buf_ctl_program(encoder, crtc_state); + + /* 6.i Configure and enable DDI_CTL_DE to start sending valid data to port slice */ + intel_dp->DP |= DDI_BUF_CTL_ENABLE; + intel_de_write(dev_priv, DDI_BUF_CTL(port), intel_dp->DP); + intel_de_posting_read(dev_priv, DDI_BUF_CTL(port)); + + /* 6.j Poll for PORT_BUF_CTL Idle Status == 0, timeout after 100 us */ + intel_wait_ddi_buf_active(dev_priv, port); +} + static void intel_ddi_prepare_link_retrain(struct intel_dp *intel_dp, const struct intel_crtc_state *crtc_state) { @@ -3871,6 +4187,7 @@ static const struct drm_encoder_funcs intel_ddi_funcs = { static struct intel_connector * intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) { + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); struct intel_connector *connector; enum port port = dig_port->base.port; @@ -3879,7 +4196,10 @@ intel_ddi_init_dp_connector(struct intel_digital_port *dig_port) return NULL; dig_port->dp.output_reg = DDI_BUF_CTL(port); - dig_port->dp.prepare_link_retrain = intel_ddi_prepare_link_retrain; + if (DISPLAY_VER(i915) >= 14) + dig_port->dp.prepare_link_retrain = mtl_ddi_prepare_link_retrain; + else + dig_port->dp.prepare_link_retrain = intel_ddi_prepare_link_retrain; dig_port->dp.set_link_train = intel_ddi_set_link_train; dig_port->dp.set_idle_link_train = intel_ddi_set_idle_link_train; diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index de95e81007c2..33bc06011235 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5645,11 +5645,16 @@ enum skl_power_gate { /* DDI Buffer Control */ #define _DDI_BUF_CTL_A 0x64000 #define _DDI_BUF_CTL_B 0x64100 +/* Known as DDI_CTL_DE in MTL+ */ #define DDI_BUF_CTL(port) _MMIO_PORT(port, _DDI_BUF_CTL_A, _DDI_BUF_CTL_B) #define DDI_BUF_CTL_ENABLE (1 << 31) #define DDI_BUF_TRANS_SELECT(n) ((n) << 24) #define DDI_BUF_EMP_MASK (0xf << 24) #define DDI_BUF_PHY_LINK_RATE(r) ((r) << 20) +#define DDI_BUF_PORT_DATA_MASK REG_GENMASK(19, 18) +#define DDI_BUF_PORT_DATA_10BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 0) +#define DDI_BUF_PORT_DATA_20BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 1) +#define DDI_BUF_PORT_DATA_40BIT REG_FIELD_PREP(DDI_BUF_PORT_DATA_MASK, 2) #define DDI_BUF_PORT_REVERSAL (1 << 16) #define DDI_BUF_IS_IDLE (1 << 7) #define DDI_BUF_CTL_TC_PHY_OWNERSHIP REG_BIT(6) -- cgit v1.2.3 From b66a8abaa48accd3d4b93c1820bbd995fa26ed78 Mon Sep 17 00:00:00 2001 From: Ankit Nautiyal Date: Thu, 13 Apr 2023 14:24:42 -0700 Subject: drm/i915/display/mtl: Fill port width in DDI_BUF_/TRANS_DDI_FUNC_/PORT_BUF_CTL for HDMI MTL requires the PORT_CTL_WIDTH, TRANS_DDI_FUNC_CTL and DDI_BUF_CTL to be filled with 4 lanes for TMDS mode. This patch enables D2D link and fills PORT_WIDTH in appropriate registers. v2: - Added fixes from Clint's Add HDMI implementation changes. - Modified commit message. v3: - Use TRANS_DDI_PORT_WIDTH() instead of DDI_PORT_WIDTH() for the value of TRANS_DDI_FUNC_CTL_*. (Gustavo) Cc: Taylor, Clinton A Signed-off-by: Ankit Nautiyal Signed-off-by: Radhakrishna Sripada Signed-off-by: Mika Kahola Reviewed-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230413212443.1504245-9-radhakrishna.sripada@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 32 ++++++++++++++++++++++++++++++-- drivers/gpu/drm/i915/i915_reg.h | 2 ++ 2 files changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 842095ae3e0b..2ef1af921a0b 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -541,6 +541,8 @@ intel_ddi_transcoder_func_reg_val_get(struct intel_encoder *encoder, temp |= TRANS_DDI_HDMI_SCRAMBLING; if (crtc_state->hdmi_high_tmds_clock_ratio) temp |= TRANS_DDI_HIGH_TMDS_CHAR_RATE; + if (DISPLAY_VER(dev_priv) >= 14) + temp |= TRANS_DDI_PORT_WIDTH(crtc_state->lane_count); } else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_ANALOG)) { temp |= TRANS_DDI_MODE_SELECT_FDI_OR_128B132B; temp |= (crtc_state->fdi_lanes - 1) << 1; @@ -3157,6 +3159,10 @@ static void intel_enable_ddi_hdmi(struct intel_atomic_state *state, if (has_buf_trans_select(dev_priv)) hsw_prepare_hdmi_ddi_buffers(encoder, crtc_state); + /* e. Enable D2D Link for C10/C20 Phy */ + if (DISPLAY_VER(dev_priv) >= 14) + mtl_ddi_enable_d2d(encoder); + encoder->set_signal_levels(encoder, crtc_state); /* Display WA #1143: skl,kbl,cfl */ @@ -3202,12 +3208,30 @@ static void intel_enable_ddi_hdmi(struct intel_atomic_state *state, * * On ADL_P the PHY link rate and lane count must be programmed but * these are both 0 for HDMI. + * + * But MTL onwards HDMI2.1 is supported and in TMDS mode this + * is filled with lane count, already set in the crtc_state. + * The same is required to be filled in PORT_BUF_CTL for C10/20 Phy. */ buf_ctl = dig_port->saved_port_bits | DDI_BUF_CTL_ENABLE; - if (IS_ALDERLAKE_P(dev_priv) && intel_phy_is_tc(dev_priv, phy)) { + if (DISPLAY_VER(dev_priv) >= 14) { + u8 lane_count = mtl_get_port_width(crtc_state->lane_count); + u32 port_buf = 0; + + port_buf |= XELPDP_PORT_WIDTH(lane_count); + + if (dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL) + port_buf |= XELPDP_PORT_REVERSAL; + + intel_de_rmw(dev_priv, XELPDP_PORT_BUF_CTL1(port), + XELPDP_PORT_WIDTH_MASK | XELPDP_PORT_REVERSAL, port_buf); + + buf_ctl |= DDI_PORT_WIDTH(lane_count); + } else if (IS_ALDERLAKE_P(dev_priv) && intel_phy_is_tc(dev_priv, phy)) { drm_WARN_ON(&dev_priv->drm, !intel_tc_port_in_legacy_mode(dig_port)); buf_ctl |= DDI_BUF_CTL_TC_PHY_OWNERSHIP; } + intel_de_write(dev_priv, DDI_BUF_CTL(port), buf_ctl); intel_wait_ddi_buf_active(dev_priv, port); @@ -3668,7 +3692,11 @@ static void intel_ddi_read_func_ctl(struct intel_encoder *encoder, fallthrough; case TRANS_DDI_MODE_SELECT_DVI: pipe_config->output_types |= BIT(INTEL_OUTPUT_HDMI); - pipe_config->lane_count = 4; + if (DISPLAY_VER(dev_priv) >= 14) + pipe_config->lane_count = + ((temp & DDI_PORT_WIDTH_MASK) >> DDI_PORT_WIDTH_SHIFT) + 1; + else + pipe_config->lane_count = 4; break; case TRANS_DDI_MODE_SELECT_DP_SST: if (encoder->type == INTEL_OUTPUT_EDP) diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h index 33bc06011235..024a92f6bdba 100644 --- a/drivers/gpu/drm/i915/i915_reg.h +++ b/drivers/gpu/drm/i915/i915_reg.h @@ -5586,6 +5586,8 @@ enum skl_power_gate { #define TRANS_DDI_HDCP_SELECT REG_BIT(5) #define TRANS_DDI_BFI_ENABLE (1 << 4) #define TRANS_DDI_HIGH_TMDS_CHAR_RATE (1 << 4) +#define TRANS_DDI_PORT_WIDTH_MASK REG_GENMASK(3, 1) +#define TRANS_DDI_PORT_WIDTH(width) REG_FIELD_PREP(TRANS_DDI_PORT_WIDTH_MASK, (width) - 1) #define TRANS_DDI_HDMI_SCRAMBLING (1 << 0) #define TRANS_DDI_HDMI_SCRAMBLING_MASK (TRANS_DDI_HDMI_SCRAMBLER_CTS_ENABLE \ | TRANS_DDI_HDMI_SCRAMBLER_RESET_FREQ \ -- cgit v1.2.3 From 27ac123b454417ea92d77c13a5d94655f53b759c Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 14 Apr 2023 20:38:00 +0300 Subject: drm/i915/dp_mst: Fix active port PLL selection for secondary MST streams The port PLL selection needs to be up-to-date in the CRTC state of both the primary and all secondary MST streams. The commit removing the encoder update_prepare/complete hooks (see Fixes: below), stopped doing this for secondary streams, fix this up. Fixes: 0f752b2178c9 ("drm/i915: Remove the encoder update_prepare()/complete() hooks") Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/8336 Cc: Mika Kahola Signed-off-by: Imre Deak Reviewed-by: Mika Kahola Link: https://patchwork.freedesktop.org/patch/msgid/20230414173800.590790-1-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 27 ++++++++++++++++++++------- drivers/gpu/drm/i915/display/intel_ddi.h | 3 +++ drivers/gpu/drm/i915/display/intel_dp_mst.c | 7 +++++++ 3 files changed, 30 insertions(+), 7 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 2ef1af921a0b..8fb0f205b553 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3349,6 +3349,25 @@ void intel_ddi_update_pipe(struct intel_atomic_state *state, intel_hdcp_update_pipe(state, encoder, crtc_state, conn_state); } +void intel_ddi_update_active_dpll(struct intel_atomic_state *state, + struct intel_encoder *encoder, + struct intel_crtc *crtc) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + struct intel_crtc_state *crtc_state = + intel_atomic_get_new_crtc_state(state, crtc); + struct intel_crtc *slave_crtc; + enum phy phy = intel_port_to_phy(i915, encoder->port); + + if (!intel_phy_is_tc(i915, phy)) + return; + + intel_update_active_dpll(state, crtc, encoder); + for_each_intel_crtc_in_pipe_mask(&i915->drm, slave_crtc, + intel_crtc_bigjoiner_slave_pipes(crtc_state)) + intel_update_active_dpll(state, slave_crtc, encoder); +} + static void intel_ddi_pre_pll_enable(struct intel_atomic_state *state, struct intel_encoder *encoder, @@ -3363,15 +3382,9 @@ intel_ddi_pre_pll_enable(struct intel_atomic_state *state, if (is_tc_port) { struct intel_crtc *master_crtc = to_intel_crtc(crtc_state->uapi.crtc); - struct intel_crtc *slave_crtc; intel_tc_port_get_link(dig_port, crtc_state->lane_count); - - intel_update_active_dpll(state, master_crtc, encoder); - - for_each_intel_crtc_in_pipe_mask(&dev_priv->drm, slave_crtc, - intel_crtc_bigjoiner_slave_pipes(crtc_state)) - intel_update_active_dpll(state, slave_crtc, encoder); + intel_ddi_update_active_dpll(state, encoder, master_crtc); } main_link_aux_power_domain_get(dig_port, crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.h b/drivers/gpu/drm/i915/display/intel_ddi.h index c85e74ae68e4..2bc034042a93 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.h +++ b/drivers/gpu/drm/i915/display/intel_ddi.h @@ -72,5 +72,8 @@ void intel_ddi_sanitize_encoder_pll_mapping(struct intel_encoder *encoder); int intel_ddi_level(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, int lane); +void intel_ddi_update_active_dpll(struct intel_atomic_state *state, + struct intel_encoder *encoder, + struct intel_crtc *crtc); #endif /* __INTEL_DDI_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index a88b852c437c..2c49d9ab86a2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -674,6 +674,13 @@ static void intel_mst_pre_pll_enable_dp(struct intel_atomic_state *state, if (intel_dp->active_mst_links == 0) dig_port->base.pre_pll_enable(state, &dig_port->base, pipe_config, NULL); + else + /* + * The port PLL state needs to get updated for secondary + * streams as for the primary stream. + */ + intel_ddi_update_active_dpll(state, &dig_port->base, + to_intel_crtc(pipe_config->uapi.crtc)); } static void intel_mst_pre_enable_dp(struct intel_atomic_state *state, -- cgit v1.2.3 From 62618c7f117eedfd99b2f857885ed004d31df739 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Fri, 28 Apr 2023 12:54:21 +0300 Subject: drm/i915/mtl: C20 PLL programming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C20 phy PLL programming sequence for DP, DP2.0, HDMI2.x non-FRL and HDMI2.x FRL. This enables C20 MPLLA and MPLLB programming sequence. add 4 lane support for c20. v2: Add 6.48Gbps and 6.75Gbps modes for eDP (RK) Fix lane check (RK) Fix multiline commenting (Arun) use usleep_range() instead of msleep() (Andi) Reviewed-by: Arun R Murthy Signed-off-by: José Roberto de Souza Signed-off-by: Mika Kahola Signed-off-by: Bhanuprakash Modem Signed-off-by: Imre Deak Signed-off-by: Arun R Murthy Reviewed-by: Radhakrishna Sripada Signed-off-by: Radhakrishna Sripada Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-2-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 288 ++++++++++++++++++--- drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h | 33 +++ drivers/gpu/drm/i915/display/intel_ddi.c | 3 +- drivers/gpu/drm/i915/display/intel_display_types.h | 15 +- drivers/gpu/drm/i915/display/intel_dp.c | 12 +- 5 files changed, 309 insertions(+), 42 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 83180074b512..71163bc5bbf5 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -273,6 +273,18 @@ static void intel_cx0_write(struct drm_i915_private *i915, enum port port, __intel_cx0_write(i915, port, lane, addr, data, committed); } +static void intel_c20_sram_write(struct drm_i915_private *i915, enum port port, + int lane, u16 addr, u16 data) +{ + assert_dc_off(i915); + + intel_cx0_write(i915, port, lane, PHY_C20_WR_ADDRESS_H, addr >> 8, 0); + intel_cx0_write(i915, port, lane, PHY_C20_WR_ADDRESS_L, addr & 0xff, 0); + + intel_cx0_write(i915, port, lane, PHY_C20_WR_DATA_H, data >> 8, 0); + intel_cx0_write(i915, port, lane, PHY_C20_WR_DATA_L, data & 0xff, 1); +} + static void __intel_cx0_rmw(struct drm_i915_private *i915, enum port port, int lane, u16 addr, u8 clear, u8 set, bool committed) { @@ -1415,6 +1427,215 @@ void intel_c10pll_dump_hw_state(struct drm_i915_private *i915, i + 2, hw_state->pll[i + 2], i + 3, hw_state->pll[i + 3]); } +static bool intel_c20_use_mplla(u32 clock) +{ + /* 10G and 20G rates use MPLLA */ + if (clock == 312500 || clock == 625000) + return true; + + return false; +} + +static u8 intel_c20_get_dp_rate(u32 clock) +{ + switch (clock) { + case 162000: /* 1.62 Gbps DP1.4 */ + return 0; + case 270000: /* 2.7 Gbps DP1.4 */ + return 1; + case 540000: /* 5.4 Gbps DP 1.4 */ + return 2; + case 810000: /* 8.1 Gbps DP1.4 */ + return 3; + case 216000: /* 2.16 Gbps eDP */ + return 4; + case 243000: /* 2.43 Gbps eDP */ + return 5; + case 324000: /* 3.24 Gbps eDP */ + return 6; + case 432000: /* 4.32 Gbps eDP */ + return 7; + case 312500: /* 10 Gbps DP2.0 */ + return 8; + case 421875: /* 13.5 Gbps DP2.0 */ + return 9; + case 625000: /* 20 Gbps DP2.0*/ + return 10; + case 648000: /* 6.48 Gbps eDP*/ + return 11; + case 675000: /* 6.75 Gbps eDP*/ + return 12; + default: + MISSING_CASE(clock); + return 0; + } +} + +static u8 intel_c20_get_hdmi_rate(u32 clock) +{ + switch (clock) { + case 25175: + case 27000: + case 74250: + case 148500: + case 594000: + return 0; + case 166670: /* 3 Gbps */ + case 333330: /* 6 Gbps */ + case 666670: /* 12 Gbps */ + return 1; + case 444440: /* 8 Gbps */ + return 2; + case 555560: /* 10 Gbps */ + return 3; + default: + MISSING_CASE(clock); + return 0; + } +} + +static bool is_dp2(u32 clock) +{ + /* DP2.0 clock rates */ + if (clock == 312500 || clock == 421875 || clock == 625000) + return true; + + return false; +} + +static bool is_hdmi_frl(u32 clock) +{ + switch (clock) { + case 166670: /* 3 Gbps */ + case 333330: /* 6 Gbps */ + case 444440: /* 8 Gbps */ + case 555560: /* 10 Gbps */ + case 666670: /* 12 Gbps */ + return true; + default: + return false; + } +} + +static bool intel_c20_protocol_switch_valid(struct intel_encoder *encoder) +{ + struct intel_digital_port *intel_dig_port = enc_to_dig_port(encoder); + + /* banks should not be cleared for DPALT/USB4/TBT modes */ + /* TODO: optimize re-calibration in legacy mode */ + return intel_tc_port_in_legacy_mode(intel_dig_port); +} + +static int intel_get_c20_custom_width(u32 clock, bool dp) +{ + if (dp && is_dp2(clock)) + return 2; + else if (is_hdmi_frl(clock)) + return 1; + else + return 0; +} + +static void intel_c20_pll_program(struct drm_i915_private *i915, + const struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + const struct intel_c20pll_state *pll_state = &crtc_state->cx0pll_state.c20; + bool dp = false; + int lane = crtc_state->lane_count > 2 ? INTEL_CX0_BOTH_LANES : INTEL_CX0_LANE0; + bool cntx; + int i; + + if (intel_crtc_has_dp_encoder(crtc_state)) + dp = true; + + /* 1. Read current context selection */ + cntx = intel_cx0_read(i915, encoder->port, INTEL_CX0_LANE0, PHY_C20_VDR_CUSTOM_SERDES_RATE) & BIT(0); + + /* + * 2. If there is a protocol switch from HDMI to DP or vice versa, clear + * the lane #0 MPLLB CAL_DONE_BANK DP2.0 10G and 20G rates enable MPLLA. + * Protocol switch is only applicable for MPLLA + */ + if (intel_c20_protocol_switch_valid(encoder)) { + for (i = 0; i < 4; i++) + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, RAWLANEAONX_DIG_TX_MPLLB_CAL_DONE_BANK(i), 0); + usleep_range(4000, 4100); + } + + /* 3. Write SRAM configuration context. If A in use, write configuration to B context */ + /* 3.1 Tx configuration */ + for (i = 0; i < ARRAY_SIZE(pll_state->tx); i++) { + if (cntx) + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, PHY_C20_A_TX_CNTX_CFG(i), pll_state->tx[i]); + else + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, PHY_C20_B_TX_CNTX_CFG(i), pll_state->tx[i]); + } + + /* 3.2 common configuration */ + for (i = 0; i < ARRAY_SIZE(pll_state->cmn); i++) { + if (cntx) + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, PHY_C20_A_CMN_CNTX_CFG(i), pll_state->cmn[i]); + else + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, PHY_C20_B_CMN_CNTX_CFG(i), pll_state->cmn[i]); + } + + /* 3.3 mpllb or mplla configuration */ + if (intel_c20_use_mplla(pll_state->clock)) { + for (i = 0; i < ARRAY_SIZE(pll_state->mplla); i++) { + if (cntx) + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_A_MPLLA_CNTX_CFG(i), + pll_state->mplla[i]); + else + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_B_MPLLA_CNTX_CFG(i), + pll_state->mplla[i]); + } + } else { + for (i = 0; i < ARRAY_SIZE(pll_state->mpllb); i++) { + if (cntx) + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_A_MPLLB_CNTX_CFG(i), + pll_state->mpllb[i]); + else + intel_c20_sram_write(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_B_MPLLB_CNTX_CFG(i), + pll_state->mpllb[i]); + } + } + + /* 4. Program custom width to match the link protocol */ + intel_cx0_rmw(i915, encoder->port, lane, PHY_C20_VDR_CUSTOM_WIDTH, + PHY_C20_CUSTOM_WIDTH_MASK, + PHY_C20_CUSTOM_WIDTH(intel_get_c20_custom_width(pll_state->clock, dp)), + MB_WRITE_COMMITTED); + + /* 5. For DP or 6. For HDMI */ + if (dp) { + intel_cx0_rmw(i915, encoder->port, lane, PHY_C20_VDR_CUSTOM_SERDES_RATE, + BIT(6) | PHY_C20_CUSTOM_SERDES_MASK, + BIT(6) | PHY_C20_CUSTOM_SERDES(intel_c20_get_dp_rate(pll_state->clock)), + MB_WRITE_COMMITTED); + } else { + intel_cx0_rmw(i915, encoder->port, lane, PHY_C20_VDR_CUSTOM_SERDES_RATE, + BIT(7) | PHY_C20_CUSTOM_SERDES_MASK, + is_hdmi_frl(pll_state->clock) ? BIT(7) : 0, + MB_WRITE_COMMITTED); + + intel_cx0_write(i915, encoder->port, INTEL_CX0_BOTH_LANES, PHY_C20_VDR_HDMI_RATE, + intel_c20_get_hdmi_rate(pll_state->clock), + MB_WRITE_COMMITTED); + } + + /* + * 7. Write Vendor specific registers to toggle context setting to load + * the updated programming toggle context bit + */ + intel_cx0_rmw(i915, encoder->port, lane, PHY_C20_VDR_CUSTOM_SERDES_RATE, + BIT(0), cntx ? 0 : 1, MB_WRITE_COMMITTED); +} + int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, const struct intel_c10pll_state *pll_state) { @@ -1456,7 +1677,11 @@ static void intel_program_port_clock_ctl(struct intel_encoder *encoder, val |= XELPDP_LANE1_PHY_CLOCK_SELECT; val |= XELPDP_FORWARD_CLOCK_UNGATE; - val |= XELPDP_DDI_CLOCK_SELECT(XELPDP_DDI_CLOCK_SELECT_MAXPCLK); + + if (is_hdmi_frl(crtc_state->port_clock)) + val |= XELPDP_DDI_CLOCK_SELECT(XELPDP_DDI_CLOCK_SELECT_DIV18CLK); + else + val |= XELPDP_DDI_CLOCK_SELECT(XELPDP_DDI_CLOCK_SELECT_MAXPCLK); /* TODO: HDMI FRL */ /* TODO: DP2.0 10G and 20G rates enable MPLLA*/ @@ -1612,7 +1837,7 @@ static void intel_cx0_phy_lane_reset(struct drm_i915_private *i915, enum port po phy_name(phy), XELPDP_PORT_RESET_END_TIMEOUT); } -static void intel_c10_program_phy_lane(struct drm_i915_private *i915, +static void intel_cx0_program_phy_lane(struct drm_i915_private *i915, struct intel_encoder *encoder, int lane_count, bool lane_reversal) { @@ -1620,9 +1845,11 @@ static void intel_c10_program_phy_lane(struct drm_i915_private *i915, bool dp_alt_mode = intel_tc_port_in_dp_alt_mode(enc_to_dig_port(encoder)); enum port port = encoder->port; - intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), - 0, C10_VDR_CTRL_MSGBUS_ACCESS, - MB_WRITE_COMMITTED); + if (intel_is_c10phy(i915, intel_port_to_phy(i915, port))) + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, + PHY_C10_VDR_CONTROL(1), 0, + C10_VDR_CTRL_MSGBUS_ACCESS, + MB_WRITE_COMMITTED); /* TODO: DP-alt MFD case where only one PHY lane should be programmed. */ l0t1 = intel_cx0_read(i915, port, INTEL_CX0_LANE0, PHY_CX0_TX_CONTROL(1, 2)); @@ -1685,9 +1912,11 @@ static void intel_c10_program_phy_lane(struct drm_i915_private *i915, intel_cx0_write(i915, port, INTEL_CX0_LANE1, PHY_CX0_TX_CONTROL(2, 2), l1t2, MB_WRITE_COMMITTED); - intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, PHY_C10_VDR_CONTROL(1), - 0, C10_VDR_CTRL_UPDATE_CFG, - MB_WRITE_COMMITTED); + if (intel_is_c10phy(i915, intel_port_to_phy(i915, port))) + intel_cx0_rmw(i915, port, INTEL_CX0_BOTH_LANES, + PHY_C10_VDR_CONTROL(1), 0, + C10_VDR_CTRL_UPDATE_CFG, + MB_WRITE_COMMITTED); } static u32 intel_cx0_get_pclk_pll_request(u8 lane_mask) @@ -1712,8 +1941,8 @@ static u32 intel_cx0_get_pclk_pll_ack(u8 lane_mask) return val; } -static void intel_c10pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +void intel_cx0pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); enum phy phy = intel_port_to_phy(i915, encoder->port); @@ -1721,6 +1950,7 @@ static void intel_c10pll_enable(struct intel_encoder *encoder, bool lane_reversal = dig_port->saved_port_bits & DDI_BUF_PORT_REVERSAL; u8 maxpclk_lane = lane_reversal ? INTEL_CX0_LANE1 : INTEL_CX0_LANE0; + intel_wakeref_t wakeref = intel_cx0_phy_transaction_begin(encoder); /* * 1. Program PORT_CLOCK_CTL REGISTER to configure @@ -1739,13 +1969,16 @@ static void intel_c10pll_enable(struct intel_encoder *encoder, CX0_P2_STATE_READY); /* 4. Program PHY internal PLL internal registers. */ - intel_c10_pll_program(i915, crtc_state, encoder); + if (intel_is_c10phy(i915, phy)) + intel_c10_pll_program(i915, crtc_state, encoder); + else + intel_c20_pll_program(i915, crtc_state, encoder); /* * 5. Program the enabled and disabled owned PHY lane * transmitters over message bus */ - intel_c10_program_phy_lane(i915, encoder, crtc_state->lane_count, lane_reversal); + intel_cx0_program_phy_lane(i915, encoder, crtc_state->lane_count, lane_reversal); /* * 6. Follow the Display Voltage Frequency Switching - Sequence @@ -1779,32 +2012,22 @@ static void intel_c10pll_enable(struct intel_encoder *encoder, * 10. Follow the Display Voltage Frequency Switching Sequence After * Frequency Change. We handle this step in bxt_set_cdclk(). */ -} - -void intel_cx0pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) -{ - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - enum phy phy = intel_port_to_phy(i915, encoder->port); - intel_wakeref_t wakeref; - - wakeref = intel_cx0_phy_transaction_begin(encoder); - - drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); - intel_c10pll_enable(encoder, crtc_state); /* TODO: enable TBT-ALT mode */ intel_cx0_phy_transaction_end(encoder, wakeref); } -static void intel_c10pll_disable(struct intel_encoder *encoder) +void intel_cx0pll_disable(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); enum phy phy = intel_port_to_phy(i915, encoder->port); + bool is_c10 = intel_is_c10phy(i915, phy); + intel_wakeref_t wakeref = intel_cx0_phy_transaction_begin(encoder); /* 1. Change owned PHY lane power to Disable state. */ intel_cx0_powerdown_change_sequence(i915, encoder->port, INTEL_CX0_BOTH_LANES, - CX0_P2PG_STATE_DISABLE); + is_c10 ? CX0_P2PG_STATE_DISABLE : + CX0_P4PG_STATE_DISABLE); /* * 2. Follow the Display Voltage Frequency Switching Sequence Before @@ -1842,18 +2065,7 @@ static void intel_c10pll_disable(struct intel_encoder *encoder) XELPDP_DDI_CLOCK_SELECT_MASK, 0); intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), XELPDP_FORWARD_CLOCK_UNGATE, 0); -} - -void intel_cx0pll_disable(struct intel_encoder *encoder) -{ - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - enum phy phy = intel_port_to_phy(i915, encoder->port); - intel_wakeref_t wakeref; - wakeref = intel_cx0_phy_transaction_begin(encoder); - - drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); - intel_c10pll_disable(encoder); intel_cx0_phy_transaction_end(encoder, wakeref); } diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h index 20024622d0eb..3aa334b15eaf 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h @@ -177,4 +177,37 @@ #define PHY_CX0_TX_CONTROL(tx, control) (0x400 + ((tx) - 1) * 0x200 + (control)) #define CONTROL2_DISABLE_SINGLE_TX REG_BIT(6) +/* C20 Registers */ +#define PHY_C20_WR_ADDRESS_L 0xC02 +#define PHY_C20_WR_ADDRESS_H 0xC03 +#define PHY_C20_WR_DATA_L 0xC04 +#define PHY_C20_WR_DATA_H 0xC05 +#define PHY_C20_RD_ADDRESS_L 0xC06 +#define PHY_C20_RD_ADDRESS_H 0xC07 +#define PHY_C20_RD_DATA_L 0xC08 +#define PHY_C20_RD_DATA_H 0xC09 +#define PHY_C20_VDR_CUSTOM_SERDES_RATE 0xD00 +#define PHY_C20_VDR_HDMI_RATE 0xD01 +#define PHY_C20_CONTEXT_TOGGLE REG_BIT8(0) +#define PHY_C20_CUSTOM_SERDES_MASK REG_GENMASK8(4, 1) +#define PHY_C20_CUSTOM_SERDES(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_SERDES_MASK, val) +#define PHY_C20_VDR_CUSTOM_WIDTH 0xD02 +#define PHY_C20_CUSTOM_WIDTH_MASK REG_GENMASK(1, 0) +#define PHY_C20_CUSTOM_WIDTH(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_WIDTH_MASK, val) +#define PHY_C20_A_TX_CNTX_CFG(idx) (0xCF2E - (idx)) +#define PHY_C20_B_TX_CNTX_CFG(idx) (0xCF2A - (idx)) +#define PHY_C20_A_CMN_CNTX_CFG(idx) (0xCDAA - (idx)) +#define PHY_C20_B_CMN_CNTX_CFG(idx) (0xCDA5 - (idx)) +#define PHY_C20_A_MPLLA_CNTX_CFG(idx) (0xCCF0 - (idx)) +#define PHY_C20_B_MPLLA_CNTX_CFG(idx) (0xCCE5 - (idx)) +#define C20_MPLLA_FRACEN REG_BIT(14) +#define C20_MPLLA_TX_CLK_DIV_MASK REG_GENMASK(10, 8) +#define PHY_C20_A_MPLLB_CNTX_CFG(idx) (0xCB5A - (idx)) +#define PHY_C20_B_MPLLB_CNTX_CFG(idx) (0xCB4E - (idx)) +#define C20_MPLLB_TX_CLK_DIV_MASK REG_GENMASK(15, 13) +#define C20_MPLLB_FRACEN REG_BIT(13) +#define C20_MULTIPLIER_MASK REG_GENMASK(11, 0) + +#define RAWLANEAONX_DIG_TX_MPLLB_CAL_DONE_BANK(idx) (0x303D + (idx)) + #endif /* __INTEL_CX0_REG_DEFS_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 8fb0f205b553..e4cc7a3cd137 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3359,7 +3359,8 @@ void intel_ddi_update_active_dpll(struct intel_atomic_state *state, struct intel_crtc *slave_crtc; enum phy phy = intel_port_to_phy(i915, encoder->port); - if (!intel_phy_is_tc(i915, phy)) + /* FIXME: Add MTL pll_mgr */ + if (DISPLAY_VER(i915) >= 14 || !intel_phy_is_tc(i915, phy)) return; intel_update_active_dpll(state, crtc, encoder); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 35c260bd1461..99b187f2f4ed 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -996,8 +996,21 @@ struct intel_c10pll_state { u8 pll[20]; }; +struct intel_c20pll_state { + u32 clock; /* in kHz */ + u16 tx[3]; + u16 cmn[4]; + union { + u16 mplla[10]; + u16 mpllb[11]; + }; +}; + struct intel_cx0pll_state { - struct intel_c10pll_state c10; + union { + struct intel_c10pll_state c10; + struct intel_c20pll_state c20; + }; bool ssc_enabled; }; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 1d28a2560ae0..4361c1ac65c3 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -53,6 +53,7 @@ #include "intel_combo_phy_regs.h" #include "intel_connector.h" #include "intel_crtc.h" +#include "intel_cx0_phy.h" #include "intel_ddi.h" #include "intel_de.h" #include "intel_display_types.h" @@ -423,7 +424,14 @@ static int ehl_max_source_rate(struct intel_dp *intel_dp) static int mtl_max_source_rate(struct intel_dp *intel_dp) { - return intel_dp_is_edp(intel_dp) ? 675000 : 810000; + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum phy phy = intel_port_to_phy(i915, dig_port->base.port); + + if (intel_is_c10phy(i915, phy)) + return intel_dp_is_edp(intel_dp) ? 675000 : 810000; + + return 2000000; } static int vbt_max_link_rate(struct intel_dp *intel_dp) @@ -452,7 +460,7 @@ intel_dp_set_source_rates(struct intel_dp *intel_dp) /* The values must be in increasing order */ static const int mtl_rates[] = { 162000, 216000, 243000, 270000, 324000, 432000, 540000, 675000, - 810000, + 810000, 1000000, 1350000, 2000000, }; static const int icl_rates[] = { 162000, 216000, 270000, 324000, 432000, 540000, 648000, 810000, -- cgit v1.2.3 From 929f527a7b70a5a7810f83c5e8941657413596c3 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Fri, 28 Apr 2023 12:54:22 +0300 Subject: drm/i915/mtl: C20 HW readout Create a table for C20 DP1.4, DP2.0 and HDMI2.1 rates. The PLL settings are based on table, not for algorithmic alternative. For DP 1.4 only MPLLB is in use. Once register settings are done, we read back C20 HW state. BSpec: 64568 v2: Updated pll tables (RK) MPLLB selection fix (RK) Signed-off-by: Mika Kahola Signed-off-by: Arun R Murthy Signed-off-by: Ankit Nautiyal Reviewed-by: Radhakrishna Sripada Signed-off-by: Radhakrishna Sripada Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-3-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 624 ++++++++++++++++++++- drivers/gpu/drm/i915/display/intel_cx0_phy.h | 8 +- drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h | 1 + drivers/gpu/drm/i915/display/intel_ddi.c | 9 +- drivers/gpu/drm/i915/display/intel_display_types.h | 1 + drivers/gpu/drm/i915/display/intel_hdmi.c | 6 +- drivers/gpu/drm/i915/display/intel_hdmi.h | 1 + 7 files changed, 630 insertions(+), 20 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 71163bc5bbf5..651239006220 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -11,6 +11,7 @@ #include "intel_de.h" #include "intel_display_types.h" #include "intel_dp.h" +#include "intel_hdmi.h" #include "intel_panel.h" #include "intel_psr.h" #include "intel_tc.h" @@ -285,6 +286,23 @@ static void intel_c20_sram_write(struct drm_i915_private *i915, enum port port, intel_cx0_write(i915, port, lane, PHY_C20_WR_DATA_L, data & 0xff, 1); } +static u16 intel_c20_sram_read(struct drm_i915_private *i915, enum port port, + int lane, u16 addr) +{ + u16 val; + + assert_dc_off(i915); + + intel_cx0_write(i915, port, lane, PHY_C20_RD_ADDRESS_H, addr >> 8, 0); + intel_cx0_write(i915, port, lane, PHY_C20_RD_ADDRESS_L, addr & 0xff, 1); + + val = intel_cx0_read(i915, port, lane, PHY_C20_RD_DATA_H); + val <<= 8; + val |= intel_cx0_read(i915, port, lane, PHY_C20_RD_DATA_L); + + return val; +} + static void __intel_cx0_rmw(struct drm_i915_private *i915, enum port port, int lane, u16 addr, u8 clear, u8 set, bool committed) { @@ -659,6 +677,199 @@ static const struct intel_c10pll_state * const mtl_c10_edp_tables[] = { NULL, }; +/* C20 basic DP 1.4 tables */ +static const struct intel_c20pll_state mtl_c20_dp_rbr = { + .link_bit_rate = 162000, + .clock = 162000, + .tx = { 0xbe88, /* tx cfg0 */ + 0x5800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = {0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x50a8, /* mpllb cfg0 */ + 0x2120, /* mpllb cfg1 */ + 0xcd9a, /* mpllb cfg2 */ + 0xbfc1, /* mpllb cfg3 */ + 0x5ab8, /* mpllb cfg4 */ + 0x4c34, /* mpllb cfg5 */ + 0x2000, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x6000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0000, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_dp_hbr1 = { + .link_bit_rate = 270000, + .clock = 270000, + .tx = { 0xbe88, /* tx cfg0 */ + 0x4800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = {0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x308c, /* mpllb cfg0 */ + 0x2110, /* mpllb cfg1 */ + 0xcc9c, /* mpllb cfg2 */ + 0xbfc1, /* mpllb cfg3 */ + 0x4b9a, /* mpllb cfg4 */ + 0x3f81, /* mpllb cfg5 */ + 0x2000, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x5000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0000, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_dp_hbr2 = { + .link_bit_rate = 540000, + .clock = 540000, + .tx = { 0xbe88, /* tx cfg0 */ + 0x4800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = {0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x108c, /* mpllb cfg0 */ + 0x2108, /* mpllb cfg1 */ + 0xcc9c, /* mpllb cfg2 */ + 0xbfc1, /* mpllb cfg3 */ + 0x4b9a, /* mpllb cfg4 */ + 0x3f81, /* mpllb cfg5 */ + 0x2000, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x5000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0000, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_dp_hbr3 = { + .link_bit_rate = 810000, + .clock = 810000, + .tx = { 0xbe88, /* tx cfg0 */ + 0x4800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = {0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x10d2, /* mpllb cfg0 */ + 0x2108, /* mpllb cfg1 */ + 0x8d98, /* mpllb cfg2 */ + 0xbfc1, /* mpllb cfg3 */ + 0x7166, /* mpllb cfg4 */ + 0x5f42, /* mpllb cfg5 */ + 0x2000, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x7800, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0000, /* mpllb cfg10 */ + }, +}; + +/* C20 basic DP 2.0 tables */ +static const struct intel_c20pll_state mtl_c20_dp_uhbr10 = { + .link_bit_rate = 1000000, /* 10 Gbps */ + .clock = 312500, + .tx = { 0xbe21, /* tx cfg0 */ + 0x4800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = {0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mplla = { 0x3104, /* mplla cfg0 */ + 0xd105, /* mplla cfg1 */ + 0xc025, /* mplla cfg2 */ + 0xc025, /* mplla cfg3 */ + 0x8c00, /* mplla cfg4 */ + 0x759a, /* mplla cfg5 */ + 0x4000, /* mplla cfg6 */ + 0x0003, /* mplla cfg7 */ + 0x3555, /* mplla cfg8 */ + 0x0001, /* mplla cfg9 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_dp_uhbr13_5 = { + .link_bit_rate = 1350000, /* 13.5 Gbps */ + .clock = 421875, + .tx = { 0xbea0, /* tx cfg0 */ + 0x4800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = {0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x015f, /* mpllb cfg0 */ + 0x2205, /* mpllb cfg1 */ + 0x1b17, /* mpllb cfg2 */ + 0xffc1, /* mpllb cfg3 */ + 0xe100, /* mpllb cfg4 */ + 0xbd00, /* mpllb cfg5 */ + 0x2000, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x4800, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0000, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_dp_uhbr20 = { + .link_bit_rate = 2000000, /* 20 Gbps */ + .clock = 625000, + .tx = { 0xbe20, /* tx cfg0 */ + 0x4800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = {0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mplla = { 0x3104, /* mplla cfg0 */ + 0xd105, /* mplla cfg1 */ + 0xc025, /* mplla cfg2 */ + 0xc025, /* mplla cfg3 */ + 0xa6ab, /* mplla cfg4 */ + 0x8c00, /* mplla cfg5 */ + 0x4000, /* mplla cfg6 */ + 0x0003, /* mplla cfg7 */ + 0x3555, /* mplla cfg8 */ + 0x0001, /* mplla cfg9 */ + }, +}; + +static const struct intel_c20pll_state * const mtl_c20_dp_tables[] = { + &mtl_c20_dp_rbr, + &mtl_c20_dp_hbr1, + &mtl_c20_dp_hbr2, + &mtl_c20_dp_hbr3, + &mtl_c20_dp_uhbr10, + &mtl_c20_dp_uhbr13_5, + &mtl_c20_dp_uhbr20, + NULL, +}; + /* * HDMI link rates with 38.4 MHz reference clock. */ @@ -1243,7 +1454,281 @@ static const struct intel_c10pll_state * const mtl_c10_hdmi_tables[] = { NULL, }; -int intel_c10_phy_check_hdmi_link_rate(int clock) +static const struct intel_c20pll_state mtl_c20_hdmi_25_175 = { + .link_bit_rate = 25175, + .clock = 25175, + .tx = { 0xbe88, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0xa0d2, /* mpllb cfg0 */ + 0x7d80, /* mpllb cfg1 */ + 0x0906, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x0200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x0000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0001, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_27_0 = { + .link_bit_rate = 27000, + .clock = 27000, + .tx = { 0xbe88, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0xa0e0, /* mpllb cfg0 */ + 0x7d80, /* mpllb cfg1 */ + 0x0906, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x8000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0001, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_74_25 = { + .link_bit_rate = 74250, + .clock = 74250, + .tx = { 0xbe88, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x609a, /* mpllb cfg0 */ + 0x7d40, /* mpllb cfg1 */ + 0xca06, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x5800, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0001, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_148_5 = { + .link_bit_rate = 148500, + .clock = 148500, + .tx = { 0xbe88, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x409a, /* mpllb cfg0 */ + 0x7d20, /* mpllb cfg1 */ + 0xca06, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x5800, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0001, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_594 = { + .link_bit_rate = 594000, + .clock = 594000, + .tx = { 0xbe88, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x009a, /* mpllb cfg0 */ + 0x7d08, /* mpllb cfg1 */ + 0xca06, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x5800, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0001, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_300 = { + .link_bit_rate = 3000000, + .clock = 166670, + .tx = { 0xbe98, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x209c, /* mpllb cfg0 */ + 0x7d10, /* mpllb cfg1 */ + 0xca06, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x2000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0004, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_600 = { + .link_bit_rate = 6000000, + .clock = 333330, + .tx = { 0xbe98, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x009c, /* mpllb cfg0 */ + 0x7d08, /* mpllb cfg1 */ + 0xca06, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x2000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0004, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_800 = { + .link_bit_rate = 8000000, + .clock = 444440, + .tx = { 0xbe98, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x00d0, /* mpllb cfg0 */ + 0x7d08, /* mpllb cfg1 */ + 0x4a06, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0003, /* mpllb cfg7 */ + 0x2aaa, /* mpllb cfg8 */ + 0x0002, /* mpllb cfg9 */ + 0x0004, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_1000 = { + .link_bit_rate = 10000000, + .clock = 555560, + .tx = { 0xbe98, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x1104, /* mpllb cfg0 */ + 0x7d08, /* mpllb cfg1 */ + 0x0a06, /* mpllb cfg2 */ + 0xbe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0003, /* mpllb cfg7 */ + 0x3555, /* mpllb cfg8 */ + 0x0001, /* mpllb cfg9 */ + 0x0004, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state mtl_c20_hdmi_1200 = { + .link_bit_rate = 12000000, + .clock = 666670, + .tx = { 0xbe98, /* tx cfg0 */ + 0x9800, /* tx cfg1 */ + 0x0000, /* tx cfg2 */ + }, + .cmn = { 0x0500, /* cmn cfg0*/ + 0x0005, /* cmn cfg1 */ + 0x0000, /* cmn cfg2 */ + 0x0000, /* cmn cfg3 */ + }, + .mpllb = { 0x0138, /* mpllb cfg0 */ + 0x7d08, /* mpllb cfg1 */ + 0x5486, /* mpllb cfg2 */ + 0xfe40, /* mpllb cfg3 */ + 0x0000, /* mpllb cfg4 */ + 0x0000, /* mpllb cfg5 */ + 0x2200, /* mpllb cfg6 */ + 0x0001, /* mpllb cfg7 */ + 0x4000, /* mpllb cfg8 */ + 0x0000, /* mpllb cfg9 */ + 0x0004, /* mpllb cfg10 */ + }, +}; + +static const struct intel_c20pll_state * const mtl_c20_hdmi_tables[] = { + &mtl_c20_hdmi_25_175, + &mtl_c20_hdmi_27_0, + &mtl_c20_hdmi_74_25, + &mtl_c20_hdmi_148_5, + &mtl_c20_hdmi_594, + &mtl_c20_hdmi_300, + &mtl_c20_hdmi_600, + &mtl_c20_hdmi_800, + &mtl_c20_hdmi_1000, + &mtl_c20_hdmi_1200, + NULL, +}; + +static int intel_c10_phy_check_hdmi_link_rate(int clock) { const struct intel_c10pll_state * const *tables = mtl_c10_hdmi_tables; int i; @@ -1319,17 +1804,6 @@ static int intel_c10pll_calc_state(struct intel_crtc_state *crtc_state, return -EINVAL; } -int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, - struct intel_encoder *encoder) -{ - struct drm_i915_private *i915 = to_i915(encoder->base.dev); - enum phy phy = intel_port_to_phy(i915, encoder->port); - - drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); - - return intel_c10pll_calc_state(crtc_state, encoder); -} - void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c10pll_state *pll_state) { @@ -1427,6 +1901,74 @@ void intel_c10pll_dump_hw_state(struct drm_i915_private *i915, i + 2, hw_state->pll[i + 2], i + 3, hw_state->pll[i + 3]); } +static int intel_c20_phy_check_hdmi_link_rate(int clock) +{ + const struct intel_c20pll_state * const *tables = mtl_c20_hdmi_tables; + int i; + + for (i = 0; tables[i]; i++) { + if (clock == tables[i]->link_bit_rate) + return MODE_OK; + } + + return MODE_CLOCK_RANGE; +} + +int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock) +{ + struct intel_digital_port *dig_port = hdmi_to_dig_port(hdmi); + struct drm_i915_private *i915 = intel_hdmi_to_i915(hdmi); + enum phy phy = intel_port_to_phy(i915, dig_port->base.port); + + if (intel_is_c10phy(i915, phy)) + return intel_c10_phy_check_hdmi_link_rate(clock); + return intel_c20_phy_check_hdmi_link_rate(clock); +} + +static const struct intel_c20pll_state * const * +intel_c20_pll_tables_get(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + if (intel_crtc_has_dp_encoder(crtc_state)) + return mtl_c20_dp_tables; + else if (intel_crtc_has_type(crtc_state, INTEL_OUTPUT_HDMI)) + return mtl_c20_hdmi_tables; + + MISSING_CASE(encoder->type); + return NULL; +} + +static int intel_c20pll_calc_state(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + const struct intel_c20pll_state * const *tables; + int i; + + tables = intel_c20_pll_tables_get(crtc_state, encoder); + if (!tables) + return -EINVAL; + + for (i = 0; tables[i]; i++) { + if (crtc_state->port_clock == tables[i]->link_bit_rate) { + crtc_state->cx0pll_state.c20 = *tables[i]; + return 0; + } + } + + return -EINVAL; +} + +int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, + struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + if (intel_is_c10phy(i915, phy)) + return intel_c10pll_calc_state(crtc_state, encoder); + return intel_c20pll_calc_state(crtc_state, encoder); +} + static bool intel_c20_use_mplla(u32 clock) { /* 10G and 20G rates use MPLLA */ @@ -1436,6 +1978,64 @@ static bool intel_c20_use_mplla(u32 clock) return false; } +void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, + struct intel_c20pll_state *pll_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + bool cntx; + intel_wakeref_t wakeref; + int i; + + wakeref = intel_cx0_phy_transaction_begin(encoder); + + /* 1. Read current context selection */ + cntx = intel_cx0_read(i915, encoder->port, INTEL_CX0_LANE0, PHY_C20_VDR_CUSTOM_SERDES_RATE) & PHY_C20_CONTEXT_TOGGLE; + + /* Read Tx configuration */ + for (i = 0; i < ARRAY_SIZE(pll_state->tx); i++) { + if (cntx) + pll_state->tx[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_B_TX_CNTX_CFG(i)); + else + pll_state->tx[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_A_TX_CNTX_CFG(i)); + } + + /* Read common configuration */ + for (i = 0; i < ARRAY_SIZE(pll_state->cmn); i++) { + if (cntx) + pll_state->cmn[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_B_CMN_CNTX_CFG(i)); + else + pll_state->cmn[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_A_CMN_CNTX_CFG(i)); + } + + if (pll_state->tx[0] & C20_PHY_USE_MPLLB) { + /* MPLLB configuration */ + for (i = 0; i < ARRAY_SIZE(pll_state->mpllb); i++) { + if (cntx) + pll_state->mpllb[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_B_MPLLB_CNTX_CFG(i)); + else + pll_state->mpllb[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_A_MPLLB_CNTX_CFG(i)); + } + } else { + /* MPLLA configuration */ + for (i = 0; i < ARRAY_SIZE(pll_state->mplla); i++) { + if (cntx) + pll_state->mplla[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_B_MPLLA_CNTX_CFG(i)); + else + pll_state->mplla[i] = intel_c20_sram_read(i915, encoder->port, INTEL_CX0_LANE0, + PHY_C20_A_MPLLA_CNTX_CFG(i)); + } + } + + intel_cx0_phy_transaction_end(encoder, wakeref); +} + static u8 intel_c20_get_dp_rate(u32 clock) { switch (clock) { diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 509d1d12776e..9760c6292c81 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -30,8 +30,12 @@ int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, const struct intel_c10pll_state *pll_state); void intel_c10pll_state_verify(struct intel_atomic_state *state, struct intel_crtc_state *new_crtc_state); +void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, + struct intel_c20pll_state *pll_state); void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); -int intel_c10_phy_check_hdmi_link_rate(int clock); - +int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); +void intel_cx0_phy_ddi_vswing_sequence(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + u32 level); #endif /* __INTEL_CX0_PHY_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h index 3aa334b15eaf..bfb39bce3b04 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h @@ -207,6 +207,7 @@ #define C20_MPLLB_TX_CLK_DIV_MASK REG_GENMASK(15, 13) #define C20_MPLLB_FRACEN REG_BIT(13) #define C20_MULTIPLIER_MASK REG_GENMASK(11, 0) +#define C20_PHY_USE_MPLLB REG_BIT(7) #define RAWLANEAONX_DIG_TX_MPLLB_CAL_DONE_BANK(idx) (0x303D + (idx)) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index e4cc7a3cd137..bdd4c5871f86 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3853,10 +3853,13 @@ static void mtl_ddi_get_config(struct intel_encoder *encoder, struct drm_i915_private *i915 = to_i915(encoder->base.dev); enum phy phy = intel_port_to_phy(i915, encoder->port); - drm_WARN_ON(&i915->drm, !intel_is_c10phy(i915, phy)); + if (intel_is_c10phy(i915, phy)) { + intel_c10pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c10); + intel_c10pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c10); + } else { + intel_c20pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c20); + } - intel_c10pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c10); - intel_c10pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c10); crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); intel_ddi_get_config(encoder, crtc_state); diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 99b187f2f4ed..96a3183675be 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -997,6 +997,7 @@ struct intel_c10pll_state { }; struct intel_c20pll_state { + u32 link_bit_rate; u32 clock; /* in kHz */ u16 tx[3]; u16 cmn[4]; diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index b3f3f761cbb3..6ebdea722c84 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -58,7 +58,7 @@ #include "intel_panel.h" #include "intel_snps_phy.h" -static struct drm_i915_private *intel_hdmi_to_i915(struct intel_hdmi *intel_hdmi) +inline struct drm_i915_private *intel_hdmi_to_i915(struct intel_hdmi *intel_hdmi) { return to_i915(hdmi_to_dig_port(intel_hdmi)->base.base.dev); } @@ -1865,8 +1865,8 @@ hdmi_port_clock_valid(struct intel_hdmi *hdmi, * FIXME: We will hopefully get an algorithmic way of programming * the MPLLB for HDMI in the future. */ - if (IS_METEORLAKE(dev_priv)) - return intel_c10_phy_check_hdmi_link_rate(clock); + if (DISPLAY_VER(dev_priv) >= 14) + return intel_cx0_phy_check_hdmi_link_rate(hdmi, clock); else if (IS_DG2(dev_priv)) return intel_snps_phy_check_hdmi_link_rate(clock); diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index 774dda2376ed..492bd3921385 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -54,5 +54,6 @@ int intel_hdmi_dsc_get_num_slices(const struct intel_crtc_state *crtc_state, int src_max_slices, int src_max_slice_width, int hdmi_max_slices, int hdmi_throughput); int intel_hdmi_dsc_get_slice_height(int vactive); +struct drm_i915_private *intel_hdmi_to_i915(struct intel_hdmi *intel_hdmi); #endif /* __INTEL_HDMI_H__ */ -- cgit v1.2.3 From f968a25381acbe147c675fdd7a048f170e6cf44b Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Fri, 28 Apr 2023 12:54:23 +0300 Subject: drm/i915/mtl: Dump C20 pll hw state As we already do with C10 chip, let's dump the pll hw state for C20 as well. Reviewed-by: Radhakrishna Sripada Reviewed-by: Arun R Murthy Signed-off-by: Mika Kahola Signed-off-by: Radhakrishna Sripada Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-4-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 20 ++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cx0_phy.h | 2 ++ drivers/gpu/drm/i915/display/intel_ddi.c | 1 + 3 files changed, 23 insertions(+) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 651239006220..8a014f4edc0d 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2036,6 +2036,26 @@ void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, intel_cx0_phy_transaction_end(encoder, wakeref); } +void intel_c20pll_dump_hw_state(struct drm_i915_private *i915, + const struct intel_c20pll_state *hw_state) +{ + int i; + + drm_dbg_kms(&i915->drm, "c20pll_hw_state:\n"); + drm_dbg_kms(&i915->drm, "tx[0] = 0x%.4x, tx[1] = 0x%.4x, tx[2] = 0x%.4x\n", + hw_state->tx[0], hw_state->tx[1], hw_state->tx[2]); + drm_dbg_kms(&i915->drm, "cmn[0] = 0x%.4x, cmn[1] = 0x%.4x, cmn[2] = 0x%.4x, cmn[3] = 0x%.4x\n", + hw_state->cmn[0], hw_state->cmn[1], hw_state->cmn[2], hw_state->cmn[3]); + + if (intel_c20_use_mplla(hw_state->clock)) { + for (i = 0; i < ARRAY_SIZE(hw_state->mplla); i++) + drm_dbg_kms(&i915->drm, "mplla[%d] = 0x%.4x\n", i, hw_state->mplla[i]); + } else { + for (i = 0; i < ARRAY_SIZE(hw_state->mpllb); i++) + drm_dbg_kms(&i915->drm, "mpllb[%d] = 0x%.4x\n", i, hw_state->mpllb[i]); + } +} + static u8 intel_c20_get_dp_rate(u32 clock) { switch (clock) { diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 9760c6292c81..c643aae27bac 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -32,6 +32,8 @@ void intel_c10pll_state_verify(struct intel_atomic_state *state, struct intel_crtc_state *new_crtc_state); void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c20pll_state *pll_state); +void intel_c20pll_dump_hw_state(struct drm_i915_private *i915, + const struct intel_c20pll_state *hw_state); void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index bdd4c5871f86..2a0aa421422f 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3858,6 +3858,7 @@ static void mtl_ddi_get_config(struct intel_encoder *encoder, intel_c10pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c10); } else { intel_c20pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c20); + intel_c20pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c20); } crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); -- cgit v1.2.3 From f1f9e62737abc4b6481a9a814526bd3e2cfd4d2f Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Fri, 28 Apr 2023 12:54:24 +0300 Subject: drm/i915/mtl: C20 port clock calculation Calculate port clock with C20 phy. BSpec: 64568 Reviewed-by: Radhakrishna Sripada Reviewed-by: Arun R Murthy Signed-off-by: Mika Kahola Signed-off-by: Radhakrishna Sripada Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-5-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 45 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cx0_phy.h | 2 + drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h | 3 ++ drivers/gpu/drm/i915/display/intel_ddi.c | 4 +- drivers/gpu/drm/i915/display/intel_dpll.c | 2 + 5 files changed, 54 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 8a014f4edc0d..aa8fc7beaafa 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2283,6 +2283,51 @@ int intel_c10pll_calc_port_clock(struct intel_encoder *encoder, return tmpclk; } +int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c20pll_state *pll_state) +{ + unsigned int frac, frac_en, frac_quot, frac_rem, frac_den; + unsigned int multiplier, refclk = 38400; + unsigned int tx_clk_div; + unsigned int ref_clk_mpllb_div; + unsigned int fb_clk_div4_en; + unsigned int ref, vco; + unsigned int tx_rate_mult; + unsigned int tx_rate = REG_FIELD_GET(C20_PHY_TX_RATE, pll_state->tx[0]); + + if (pll_state->tx[0] & C20_PHY_USE_MPLLB) { + tx_rate_mult = 1; + frac_en = REG_FIELD_GET(C20_MPLLB_FRACEN, pll_state->mpllb[6]); + frac_quot = pll_state->mpllb[8]; + frac_rem = pll_state->mpllb[9]; + frac_den = pll_state->mpllb[7]; + multiplier = REG_FIELD_GET(C20_MULTIPLIER_MASK, pll_state->mpllb[0]); + tx_clk_div = REG_FIELD_GET(C20_MPLLB_TX_CLK_DIV_MASK, pll_state->mpllb[0]); + ref_clk_mpllb_div = REG_FIELD_GET(C20_REF_CLK_MPLLB_DIV_MASK, pll_state->mpllb[6]); + fb_clk_div4_en = 0; + } else { + tx_rate_mult = 2; + frac_en = REG_FIELD_GET(C20_MPLLA_FRACEN, pll_state->mplla[6]); + frac_quot = pll_state->mplla[8]; + frac_rem = pll_state->mplla[9]; + frac_den = pll_state->mplla[7]; + multiplier = REG_FIELD_GET(C20_MULTIPLIER_MASK, pll_state->mplla[0]); + tx_clk_div = REG_FIELD_GET(C20_MPLLA_TX_CLK_DIV_MASK, pll_state->mplla[1]); + ref_clk_mpllb_div = REG_FIELD_GET(C20_REF_CLK_MPLLB_DIV_MASK, pll_state->mplla[6]); + fb_clk_div4_en = REG_FIELD_GET(C20_FB_CLK_DIV4_EN, pll_state->mplla[0]); + } + + if (frac_en) + frac = frac_quot + DIV_ROUND_CLOSEST(frac_rem, frac_den); + else + frac = 0; + + ref = DIV_ROUND_CLOSEST(refclk * (1 << (1 + fb_clk_div4_en)), 1 << ref_clk_mpllb_div); + vco = DIV_ROUND_CLOSEST_ULL(mul_u32_u32(ref, (multiplier << (17 - 2)) + frac) >> 17, 10); + + return vco << tx_rate_mult >> tx_clk_div >> tx_rate; +} + static void intel_program_port_clock_ctl(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, bool lane_reversal) diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index c643aae27bac..83bd3500091b 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -34,6 +34,8 @@ void intel_c20pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c20pll_state *pll_state); void intel_c20pll_dump_hw_state(struct drm_i915_private *i915, const struct intel_c20pll_state *hw_state); +int intel_c20pll_calc_port_clock(struct intel_encoder *encoder, + const struct intel_c20pll_state *pll_state); void intel_cx0_phy_set_signal_levels(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h index bfb39bce3b04..d3de4df2b682 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy_regs.h @@ -196,16 +196,19 @@ #define PHY_C20_CUSTOM_WIDTH(val) REG_FIELD_PREP8(PHY_C20_CUSTOM_WIDTH_MASK, val) #define PHY_C20_A_TX_CNTX_CFG(idx) (0xCF2E - (idx)) #define PHY_C20_B_TX_CNTX_CFG(idx) (0xCF2A - (idx)) +#define C20_PHY_TX_RATE REG_GENMASK(2, 0) #define PHY_C20_A_CMN_CNTX_CFG(idx) (0xCDAA - (idx)) #define PHY_C20_B_CMN_CNTX_CFG(idx) (0xCDA5 - (idx)) #define PHY_C20_A_MPLLA_CNTX_CFG(idx) (0xCCF0 - (idx)) #define PHY_C20_B_MPLLA_CNTX_CFG(idx) (0xCCE5 - (idx)) #define C20_MPLLA_FRACEN REG_BIT(14) +#define C20_FB_CLK_DIV4_EN REG_BIT(13) #define C20_MPLLA_TX_CLK_DIV_MASK REG_GENMASK(10, 8) #define PHY_C20_A_MPLLB_CNTX_CFG(idx) (0xCB5A - (idx)) #define PHY_C20_B_MPLLB_CNTX_CFG(idx) (0xCB4E - (idx)) #define C20_MPLLB_TX_CLK_DIV_MASK REG_GENMASK(15, 13) #define C20_MPLLB_FRACEN REG_BIT(13) +#define C20_REF_CLK_MPLLB_DIV_MASK REG_GENMASK(12, 10) #define C20_MULTIPLIER_MASK REG_GENMASK(11, 0) #define C20_PHY_USE_MPLLB REG_BIT(7) diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 2a0aa421422f..ff4d8d6d4bc3 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3856,13 +3856,13 @@ static void mtl_ddi_get_config(struct intel_encoder *encoder, if (intel_is_c10phy(i915, phy)) { intel_c10pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c10); intel_c10pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c10); + crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); } else { intel_c20pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c20); intel_c20pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c20); + crtc_state->port_clock = intel_c20pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c20); } - crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); - intel_ddi_get_config(encoder, crtc_state); } diff --git a/drivers/gpu/drm/i915/display/intel_dpll.c b/drivers/gpu/drm/i915/display/intel_dpll.c index a9fbef0fa817..ca0f362a40e3 100644 --- a/drivers/gpu/drm/i915/display/intel_dpll.c +++ b/drivers/gpu/drm/i915/display/intel_dpll.c @@ -1014,6 +1014,8 @@ static int mtl_crtc_compute_clock(struct intel_atomic_state *state, /* TODO: Do the readback via intel_compute_shared_dplls() */ if (intel_is_c10phy(i915, phy)) crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); + else + crtc_state->port_clock = intel_c20pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c20); crtc_state->hw.adjusted_mode.crtc_clock = intel_crtc_dotclock(crtc_state); -- cgit v1.2.3 From 73fc3abcb79732c2c9d4745d61b6859f587f01a3 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Fri, 28 Apr 2023 12:54:27 +0300 Subject: drm/i915/mtl: Enabling/disabling sequence Thunderbolt pll Enabling and disabling sequence for Thunderbolt PLL. Bspec: 64568 v2: Use intel_de_wait_for_register() (RK) Reviewed-by: Radhakrishna Sripada Signed-off-by: Mika Kahola Signed-off-by: Radhakrishna Sripada Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-8-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 135 ++++++++++++++++++++++++++- drivers/gpu/drm/i915/display/intel_cx0_phy.h | 7 +- drivers/gpu/drm/i915/display/intel_ddi.c | 4 +- 3 files changed, 138 insertions(+), 8 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 507ae0e1d80d..c389af1a2d7b 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2609,8 +2609,8 @@ static u32 intel_cx0_get_pclk_pll_ack(u8 lane_mask) return val; } -void intel_cx0pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state) +static void intel_cx0pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); enum phy phy = intel_port_to_phy(i915, encoder->port); @@ -2685,7 +2685,86 @@ void intel_cx0pll_enable(struct intel_encoder *encoder, intel_cx0_phy_transaction_end(encoder, wakeref); } -void intel_cx0pll_disable(struct intel_encoder *encoder) +static int intel_mtl_tbt_clock_select(struct drm_i915_private *i915, int clock) +{ + switch (clock) { + case 162000: + return XELPDP_DDI_CLOCK_SELECT_TBT_162; + case 270000: + return XELPDP_DDI_CLOCK_SELECT_TBT_270; + case 540000: + return XELPDP_DDI_CLOCK_SELECT_TBT_540; + case 810000: + return XELPDP_DDI_CLOCK_SELECT_TBT_810; + default: + MISSING_CASE(clock); + return XELPDP_DDI_CLOCK_SELECT_TBT_162; + } +} + +static void intel_mtl_tbt_pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + u32 val = 0; + + /* + * 1. Program PORT_CLOCK_CTL REGISTER to configure + * clock muxes, gating and SSC + */ + val |= XELPDP_DDI_CLOCK_SELECT(intel_mtl_tbt_clock_select(i915, crtc_state->port_clock)); + val |= XELPDP_FORWARD_CLOCK_UNGATE; + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_DDI_CLOCK_SELECT_MASK | XELPDP_FORWARD_CLOCK_UNGATE, val); + + /* 2. Read back PORT_CLOCK_CTL REGISTER */ + val = intel_de_read(i915, XELPDP_PORT_CLOCK_CTL(encoder->port)); + + /* + * 3. Follow the Display Voltage Frequency Switching - Sequence + * Before Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 4. Set PORT_CLOCK_CTL register TBT CLOCK Request to "1" to enable PLL. + */ + val |= XELPDP_TBT_CLOCK_REQUEST; + intel_de_write(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), val); + + /* 5. Poll on PORT_CLOCK_CTL TBT CLOCK Ack == "1". */ + if (__intel_de_wait_for_register(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_TBT_CLOCK_ACK, + XELPDP_TBT_CLOCK_ACK, + 100, 0, NULL)) + drm_warn(&i915->drm, "[ENCODER:%d:%s][%c] PHY PLL not locked after 100us.\n", + encoder->base.base.id, encoder->base.name, phy_name(phy)); + + /* + * 6. Follow the Display Voltage Frequency Switching Sequence After + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 7. Program DDI_CLK_VALFREQ to match intended DDI + * clock frequency. + */ + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), + crtc_state->port_clock); +} + +void intel_mtl_pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + 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(encoder, crtc_state); + else + intel_cx0pll_enable(encoder, crtc_state); +} + +static void intel_cx0pll_disable(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); enum phy phy = intel_port_to_phy(i915, encoder->port); @@ -2737,6 +2816,56 @@ void intel_cx0pll_disable(struct intel_encoder *encoder) intel_cx0_phy_transaction_end(encoder, wakeref); } +static void intel_mtl_tbt_pll_disable(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + enum phy phy = intel_port_to_phy(i915, encoder->port); + + /* + * 1. Follow the Display Voltage Frequency Switching Sequence Before + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 2. Set PORT_CLOCK_CTL register TBT CLOCK Request to "0" to disable PLL. + */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_TBT_CLOCK_REQUEST, 0); + + /* 3. Poll on PORT_CLOCK_CTL TBT CLOCK Ack == "0". */ + if (__intel_de_wait_for_register(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_TBT_CLOCK_ACK, + ~XELPDP_TBT_CLOCK_ACK, + 10, 0, NULL)) + drm_warn(&i915->drm, "[ENCODER:%d:%s][%c] PHY PLL not unlocked after 10us.\n", + encoder->base.base.id, encoder->base.name, phy_name(phy)); + + /* + * 4. Follow the Display Voltage Frequency Switching Sequence After + * Frequency Change. We handle this step in bxt_set_cdclk(). + */ + + /* + * 5. Program PORT CLOCK CTRL register to disable and gate clocks + */ + intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), + XELPDP_DDI_CLOCK_SELECT_MASK | + XELPDP_FORWARD_CLOCK_UNGATE, 0); + + /* 6. Program DDI_CLK_VALFREQ to 0. */ + intel_de_write(i915, DDI_CLK_VALFREQ(encoder->port), 0); +} + +void intel_mtl_pll_disable(struct intel_encoder *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_disable(encoder); + else + intel_cx0pll_disable(encoder); +} + void intel_c10pll_state_verify(struct intel_atomic_state *state, struct intel_crtc_state *new_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 83bd3500091b..9ea6310b6d79 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -19,9 +19,9 @@ struct intel_crtc_state; enum phy; bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy phy); -void intel_cx0pll_enable(struct intel_encoder *encoder, - const struct intel_crtc_state *crtc_state); -void intel_cx0pll_disable(struct intel_encoder *encoder); +void intel_mtl_pll_enable(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state); +void intel_mtl_pll_disable(struct intel_encoder *encoder); void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c10pll_state *pll_state); int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder); void intel_c10pll_dump_hw_state(struct drm_i915_private *dev_priv, @@ -42,4 +42,5 @@ int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); void intel_cx0_phy_ddi_vswing_sequence(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, u32 level); +int intel_mtl_tbt_readout_hw_state(struct intel_encoder *encoder); #endif /* __INTEL_CX0_PHY_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index ff4d8d6d4bc3..56cffd8394bc 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4779,8 +4779,8 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) encoder->pipe_mask = ~0; if (DISPLAY_VER(dev_priv) >= 14) { - encoder->enable_clock = intel_cx0pll_enable; - encoder->disable_clock = intel_cx0pll_disable; + encoder->enable_clock = intel_mtl_pll_enable; + encoder->disable_clock = intel_mtl_pll_disable; encoder->get_config = mtl_ddi_get_config; } else if (IS_DG2(dev_priv)) { encoder->enable_clock = intel_mpllb_enable; -- cgit v1.2.3 From 7dee06bc0ff5ac7341ee3cde8161e58dc43dd9b0 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Fri, 28 Apr 2023 12:54:28 +0300 Subject: drm/i915/mtl: Readout Thunderbolt HW state Readout hw state for Thunderbolt. Reviewed-by: Radhakrishna Sripada Signed-off-by: Mika Kahola Signed-off-by: Radhakrishna Sripada Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-9-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 27 +++++++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_cx0_phy.h | 2 +- drivers/gpu/drm/i915/display/intel_ddi.c | 5 ++++- 3 files changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index c389af1a2d7b..7f7b63ae7651 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2685,6 +2685,33 @@ static void intel_cx0pll_enable(struct intel_encoder *encoder, intel_cx0_phy_transaction_end(encoder, wakeref); } +int intel_mtl_tbt_calc_port_clock(struct intel_encoder *encoder) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + u32 clock; + u32 val = intel_de_read(i915, XELPDP_PORT_CLOCK_CTL(encoder->port)); + + clock = REG_FIELD_GET(XELPDP_DDI_CLOCK_SELECT_MASK, val); + + drm_WARN_ON(&i915->drm, !(val & XELPDP_FORWARD_CLOCK_UNGATE)); + drm_WARN_ON(&i915->drm, !(val & XELPDP_TBT_CLOCK_REQUEST)); + drm_WARN_ON(&i915->drm, !(val & XELPDP_TBT_CLOCK_ACK)); + + switch (clock) { + case XELPDP_DDI_CLOCK_SELECT_TBT_162: + return 162000; + case XELPDP_DDI_CLOCK_SELECT_TBT_270: + return 270000; + case XELPDP_DDI_CLOCK_SELECT_TBT_540: + return 540000; + case XELPDP_DDI_CLOCK_SELECT_TBT_810: + return 810000; + default: + MISSING_CASE(clock); + return 162000; + } +} + static int intel_mtl_tbt_clock_select(struct drm_i915_private *i915, int clock) { switch (clock) { diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.h b/drivers/gpu/drm/i915/display/intel_cx0_phy.h index 9ea6310b6d79..c1b8f7980f69 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -42,5 +42,5 @@ int intel_cx0_phy_check_hdmi_link_rate(struct intel_hdmi *hdmi, int clock); void intel_cx0_phy_ddi_vswing_sequence(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state, u32 level); -int intel_mtl_tbt_readout_hw_state(struct intel_encoder *encoder); +int intel_mtl_tbt_calc_port_clock(struct intel_encoder *encoder); #endif /* __INTEL_CX0_PHY_H__ */ diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 56cffd8394bc..10d7327ba3fc 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3852,8 +3852,11 @@ static void mtl_ddi_get_config(struct intel_encoder *encoder, { struct drm_i915_private *i915 = to_i915(encoder->base.dev); enum phy phy = intel_port_to_phy(i915, encoder->port); + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); - if (intel_is_c10phy(i915, phy)) { + if (intel_tc_port_in_tbt_alt_mode(dig_port)) { + crtc_state->port_clock = intel_mtl_tbt_calc_port_clock(encoder); + } else if (intel_is_c10phy(i915, phy)) { intel_c10pll_readout_hw_state(encoder, &crtc_state->cx0pll_state.c10); intel_c10pll_dump_hw_state(i915, &crtc_state->cx0pll_state.c10); crtc_state->port_clock = intel_c10pll_calc_port_clock(encoder, &crtc_state->cx0pll_state.c10); -- cgit v1.2.3 From 6f0423b06a0cd55133bafb7a69fd2ac1ff7a6750 Mon Sep 17 00:00:00 2001 From: Mika Kahola Date: Fri, 28 Apr 2023 12:54:30 +0300 Subject: drm/i915/mtl: Power up TCSS Add register writes to enable powering up Type-C subsystem i.e. TCSS. For MeteorLake we need to request TCSS to power up and check the TCSS power state after 500 us. In addition, for PICA we need to set/clear the Type-C PHY ownnership bit when Type-C device is connected/disconnected. Reviewed-by: Matt Atwood Signed-off-by: Mika Kahola Signed-off-by: Imre Deak Signed-off-by: Radhakrishna Sripada Link: https://patchwork.freedesktop.org/patch/msgid/20230428095433.4109054-11-mika.kahola@intel.com --- drivers/gpu/drm/i915/display/intel_cx0_phy.c | 19 +++ drivers/gpu/drm/i915/display/intel_cx0_phy.h | 4 + drivers/gpu/drm/i915/display/intel_ddi.c | 1 + drivers/gpu/drm/i915/display/intel_display.c | 2 +- drivers/gpu/drm/i915/display/intel_tc.c | 199 +++++++++++++++++++++++++-- 5 files changed, 216 insertions(+), 9 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 7f7b63ae7651..d94127e7448b 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2893,6 +2893,25 @@ void intel_mtl_pll_disable(struct intel_encoder *encoder) intel_cx0pll_disable(encoder); } +enum icl_port_dpll_id +intel_mtl_port_pll_type(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state) +{ + struct drm_i915_private *i915 = to_i915(encoder->base.dev); + /* + * TODO: Determine the PLL type from the SW state, once MTL PLL + * handling is done via the standard shared DPLL framework. + */ + u32 val = intel_de_read(i915, XELPDP_PORT_CLOCK_CTL(encoder->port)); + u32 clock = REG_FIELD_GET(XELPDP_DDI_CLOCK_SELECT_MASK, val); + + if (clock == XELPDP_DDI_CLOCK_SELECT_MAXPCLK || + clock == XELPDP_DDI_CLOCK_SELECT_DIV18CLK) + return ICL_PORT_DPLL_MG_PHY; + else + return ICL_PORT_DPLL_DEFAULT; +} + void intel_c10pll_state_verify(struct intel_atomic_state *state, struct intel_crtc_state *new_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 c1b8f7980f69..f99809af257d 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.h +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.h @@ -16,12 +16,16 @@ struct drm_i915_private; struct intel_encoder; struct intel_crtc_state; +enum icl_port_dpll_id; enum phy; bool intel_is_c10phy(struct drm_i915_private *dev_priv, enum phy phy); void intel_mtl_pll_enable(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state); void intel_mtl_pll_disable(struct intel_encoder *encoder); +enum icl_port_dpll_id +intel_mtl_port_pll_type(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state); void intel_c10pll_readout_hw_state(struct intel_encoder *encoder, struct intel_c10pll_state *pll_state); int intel_cx0pll_calc_state(struct intel_crtc_state *crtc_state, struct intel_encoder *encoder); void intel_c10pll_dump_hw_state(struct drm_i915_private *dev_priv, diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 10d7327ba3fc..74d6544b7f88 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4784,6 +4784,7 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) if (DISPLAY_VER(dev_priv) >= 14) { encoder->enable_clock = intel_mtl_pll_enable; encoder->disable_clock = intel_mtl_pll_disable; + encoder->port_pll_type = intel_mtl_port_pll_type; encoder->get_config = mtl_ddi_get_config; } else if (IS_DG2(dev_priv)) { encoder->enable_clock = intel_mpllb_enable; diff --git a/drivers/gpu/drm/i915/display/intel_display.c b/drivers/gpu/drm/i915/display/intel_display.c index bf391a6cd8d6..0b9ae5759ffa 100644 --- a/drivers/gpu/drm/i915/display/intel_display.c +++ b/drivers/gpu/drm/i915/display/intel_display.c @@ -1756,7 +1756,7 @@ bool intel_phy_is_tc(struct drm_i915_private *dev_priv, enum phy phy) if (IS_DG2(dev_priv)) /* DG2's "TC1" output uses a SNPS PHY */ return false; - else if (IS_ALDERLAKE_P(dev_priv)) + else if (IS_ALDERLAKE_P(dev_priv) || IS_METEORLAKE(dev_priv)) return phy >= PHY_F && phy <= PHY_I; else if (IS_TIGERLAKE(dev_priv)) return phy >= PHY_D && phy <= PHY_I; diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 3b60995e9dfb..951b12ac51dc 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -5,6 +5,7 @@ #include "i915_drv.h" #include "i915_reg.h" +#include "intel_cx0_phy_regs.h" #include "intel_ddi.h" #include "intel_de.h" #include "intel_display.h" @@ -59,6 +60,7 @@ static enum intel_display_power_domain tc_phy_cold_off_domain(struct intel_tc_port *); static u32 tc_phy_hpd_live_status(struct intel_tc_port *tc); static bool tc_phy_is_ready(struct intel_tc_port *tc); +static bool tc_phy_wait_for_ready(struct intel_tc_port *tc); static enum tc_port_mode tc_phy_get_current_mode(struct intel_tc_port *tc); static const char *tc_port_mode_name(enum tc_port_mode mode) @@ -141,15 +143,23 @@ bool intel_tc_port_in_legacy_mode(struct intel_digital_port *dig_port) * * POWER_DOMAIN_TC_COLD_OFF: * ------------------------- - * TGL/legacy, DP-alt modes: + * ICL/DP-alt, TBT mode: + * - TCSS/TBT: block TC-cold power state for using the (direct or + * TBT DP-IN) AUX and main lanes. + * + * TGL/all modes: * - TCSS/IOM,FIA access for PHY ready, owned and HPD live state - * - TCSS/PHY: block TC-cold power state for using the PHY AUX and - * main lanes. + * - TCSS/PHY: block TC-cold power state for using the (direct or + * TBT DP-IN) AUX and main lanes. * - * ICL, TGL, ADLP/TBT mode: - * - TCSS/IOM,FIA access for HPD live state + * ADLP/TBT mode: * - TCSS/TBT: block TC-cold power state for using the (TBT DP-IN) * AUX and main lanes. + * + * XELPDP+/all modes: + * - TCSS/IOM,FIA access for PHY ready, owned state + * - TCSS/PHY: block TC-cold power state for using the (direct or + * TBT DP-IN) AUX and main lanes. */ bool intel_tc_cold_requires_aux_pw(struct intel_digital_port *dig_port) { @@ -872,6 +882,172 @@ static const struct intel_tc_phy_ops adlp_tc_phy_ops = { .init = adlp_tc_phy_init, }; +/* + * XELPDP TC PHY handlers + * ---------------------- + */ +static bool +xelpdp_tc_phy_tcss_power_is_enabled(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum port port = tc->dig_port->base.port; + + assert_tc_cold_blocked(tc); + + return intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)) & XELPDP_TCSS_POWER_STATE; +} + +static bool +xelpdp_tc_phy_wait_for_tcss_power(struct intel_tc_port *tc, bool enabled) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + + if (wait_for(xelpdp_tc_phy_tcss_power_is_enabled(tc) == enabled, 5)) { + drm_dbg_kms(&i915->drm, + "Port %s: timeout waiting for TCSS power to get %s\n", + enabled ? "enabled" : "disabled", + tc->port_name); + return false; + } + + return true; +} + +static void __xelpdp_tc_phy_enable_tcss_power(struct intel_tc_port *tc, bool enable) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum port port = tc->dig_port->base.port; + u32 val; + + assert_tc_cold_blocked(tc); + + val = intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)); + if (enable) + val |= XELPDP_TCSS_POWER_REQUEST; + else + val &= ~XELPDP_TCSS_POWER_REQUEST; + intel_de_write(i915, XELPDP_PORT_BUF_CTL1(port), val); +} + +static bool xelpdp_tc_phy_enable_tcss_power(struct intel_tc_port *tc, bool enable) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + + __xelpdp_tc_phy_enable_tcss_power(tc, enable); + + if ((!tc_phy_wait_for_ready(tc) || + !xelpdp_tc_phy_wait_for_tcss_power(tc, enable)) && + !drm_WARN_ON(&i915->drm, tc->mode == TC_PORT_LEGACY)) { + if (enable) { + __xelpdp_tc_phy_enable_tcss_power(tc, false); + xelpdp_tc_phy_wait_for_tcss_power(tc, false); + } + + return false; + } + + return true; +} + +static void xelpdp_tc_phy_take_ownership(struct intel_tc_port *tc, bool take) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum port port = tc->dig_port->base.port; + u32 val; + + assert_tc_cold_blocked(tc); + + val = intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)); + if (take) + val |= XELPDP_TC_PHY_OWNERSHIP; + else + val &= ~XELPDP_TC_PHY_OWNERSHIP; + intel_de_write(i915, XELPDP_PORT_BUF_CTL1(port), val); +} + +static bool xelpdp_tc_phy_is_owned(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + enum port port = tc->dig_port->base.port; + + assert_tc_cold_blocked(tc); + + return intel_de_read(i915, XELPDP_PORT_BUF_CTL1(port)) & XELPDP_TC_PHY_OWNERSHIP; +} + +static void xelpdp_tc_phy_get_hw_state(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + intel_wakeref_t tc_cold_wref; + enum intel_display_power_domain domain; + + tc_cold_wref = __tc_cold_block(tc, &domain); + + tc->mode = tc_phy_get_current_mode(tc); + if (tc->mode != TC_PORT_DISCONNECTED) + tc->lock_wakeref = tc_cold_block(tc); + + drm_WARN_ON(&i915->drm, + (tc->mode == TC_PORT_DP_ALT || tc->mode == TC_PORT_LEGACY) && + !xelpdp_tc_phy_tcss_power_is_enabled(tc)); + + __tc_cold_unblock(tc, domain, tc_cold_wref); +} + +static bool xelpdp_tc_phy_connect(struct intel_tc_port *tc, int required_lanes) +{ + tc->lock_wakeref = tc_cold_block(tc); + + if (tc->mode == TC_PORT_TBT_ALT) + return true; + + if (!xelpdp_tc_phy_enable_tcss_power(tc, true)) + goto out_unblock_tccold; + + xelpdp_tc_phy_take_ownership(tc, true); + + if (!tc_phy_verify_legacy_or_dp_alt_mode(tc, required_lanes)) + goto out_release_phy; + + return true; + +out_release_phy: + xelpdp_tc_phy_take_ownership(tc, false); + xelpdp_tc_phy_wait_for_tcss_power(tc, false); + +out_unblock_tccold: + tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); + + return false; +} + +static void xelpdp_tc_phy_disconnect(struct intel_tc_port *tc) +{ + switch (tc->mode) { + case TC_PORT_LEGACY: + case TC_PORT_DP_ALT: + xelpdp_tc_phy_take_ownership(tc, false); + xelpdp_tc_phy_enable_tcss_power(tc, false); + fallthrough; + case TC_PORT_TBT_ALT: + tc_cold_unblock(tc, fetch_and_zero(&tc->lock_wakeref)); + break; + default: + MISSING_CASE(tc->mode); + } +} + +static const struct intel_tc_phy_ops xelpdp_tc_phy_ops = { + .cold_off_domain = tgl_tc_phy_cold_off_domain, + .hpd_live_status = adlp_tc_phy_hpd_live_status, + .is_ready = adlp_tc_phy_is_ready, + .is_owned = xelpdp_tc_phy_is_owned, + .get_hw_state = xelpdp_tc_phy_get_hw_state, + .connect = xelpdp_tc_phy_connect, + .disconnect = xelpdp_tc_phy_disconnect, + .init = adlp_tc_phy_init, +}; + /* * Generic TC PHY handlers * ----------------------- @@ -945,13 +1121,18 @@ static bool tc_phy_is_connected(struct intel_tc_port *tc, return is_connected; } -static void tc_phy_wait_for_ready(struct intel_tc_port *tc) +static bool tc_phy_wait_for_ready(struct intel_tc_port *tc) { struct drm_i915_private *i915 = tc_to_i915(tc); - if (wait_for(tc_phy_is_ready(tc), 100)) + if (wait_for(tc_phy_is_ready(tc), 500)) { drm_err(&i915->drm, "Port %s: timeout waiting for PHY ready\n", tc->port_name); + + return false; + } + + return true; } static enum tc_port_mode @@ -1442,7 +1623,9 @@ int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) dig_port->tc = tc; tc->dig_port = dig_port; - if (DISPLAY_VER(i915) >= 13) + if (DISPLAY_VER(i915) >= 14) + tc->phy_ops = &xelpdp_tc_phy_ops; + else if (DISPLAY_VER(i915) >= 13) tc->phy_ops = &adlp_tc_phy_ops; else if (DISPLAY_VER(i915) >= 12) tc->phy_ops = &tgl_tc_phy_ops; -- cgit v1.2.3 From 34682d6006ca31e8a4961104dba15d556db533b4 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Mon, 7 Nov 2022 21:46:03 +0200 Subject: drm/i915: Move has_hdmi_sink out from intel_hdmi_compute_config() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll be wanting to compute has_hdmi_sink a bit differently for some platforms. To that end compute it in the encoder .compute_config_hook() before we call intel_hdmi_compute_config(). intel_hdmi_compute_has_hdmi_sink() will do the basic lifting beyond any platform specific stuff. Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20221107194604.15227-5-ville.syrjala@linux.intel.com Reviewed-by: Jani Nikula --- drivers/gpu/drm/i915/display/g4x_hdmi.c | 3 +++ drivers/gpu/drm/i915/display/intel_ddi.c | 3 +++ drivers/gpu/drm/i915/display/intel_hdmi.c | 14 ++++++++++---- drivers/gpu/drm/i915/display/intel_hdmi.h | 3 +++ 4 files changed, 19 insertions(+), 4 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/g4x_hdmi.c b/drivers/gpu/drm/i915/display/g4x_hdmi.c index 448ea26786e0..8498d2380d15 100644 --- a/drivers/gpu/drm/i915/display/g4x_hdmi.c +++ b/drivers/gpu/drm/i915/display/g4x_hdmi.c @@ -89,6 +89,9 @@ static int g4x_hdmi_compute_config(struct intel_encoder *encoder, if (HAS_PCH_SPLIT(i915)) crtc_state->has_pch_encoder = true; + crtc_state->has_hdmi_sink = + intel_hdmi_compute_has_hdmi_sink(encoder, crtc_state, conn_state); + return intel_hdmi_compute_config(encoder, crtc_state, conn_state); } diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 74d6544b7f88..b1caf521a348 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4063,6 +4063,9 @@ static int intel_ddi_compute_config(struct intel_encoder *encoder, pipe_config->cpu_transcoder = TRANSCODER_EDP; if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_HDMI)) { + pipe_config->has_hdmi_sink = + intel_hdmi_compute_has_hdmi_sink(encoder, pipe_config, conn_state); + ret = intel_hdmi_compute_config(encoder, pipe_config, conn_state); } else { ret = intel_dp_compute_config(encoder, pipe_config, conn_state); diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.c b/drivers/gpu/drm/i915/display/intel_hdmi.c index 7d796c099fb6..fd2083452305 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.c +++ b/drivers/gpu/drm/i915/display/intel_hdmi.c @@ -2264,11 +2264,20 @@ static bool source_supports_scrambling(struct intel_encoder *encoder) return intel_hdmi_source_max_tmds_clock(encoder) > 340000; } +bool intel_hdmi_compute_has_hdmi_sink(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state) +{ + struct intel_hdmi *hdmi = enc_to_intel_hdmi(encoder); + + return intel_has_hdmi_sink(hdmi, conn_state) && + !intel_hdmi_is_cloned(crtc_state); +} + int intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state) { - struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder); struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); struct drm_display_mode *adjusted_mode = &pipe_config->hw.adjusted_mode; struct drm_connector *connector = conn_state->connector; @@ -2283,9 +2292,6 @@ int intel_hdmi_compute_config(struct intel_encoder *encoder, return -EINVAL; pipe_config->output_format = INTEL_OUTPUT_FORMAT_RGB; - pipe_config->has_hdmi_sink = - intel_has_hdmi_sink(intel_hdmi, conn_state) && - !intel_hdmi_is_cloned(pipe_config); if (pipe_config->has_hdmi_sink) pipe_config->has_infoframe = true; diff --git a/drivers/gpu/drm/i915/display/intel_hdmi.h b/drivers/gpu/drm/i915/display/intel_hdmi.h index 08a2745729d0..6b39df38d57a 100644 --- a/drivers/gpu/drm/i915/display/intel_hdmi.h +++ b/drivers/gpu/drm/i915/display/intel_hdmi.h @@ -24,6 +24,9 @@ union hdmi_infoframe; void intel_hdmi_init_connector(struct intel_digital_port *dig_port, struct intel_connector *intel_connector); +bool intel_hdmi_compute_has_hdmi_sink(struct intel_encoder *encoder, + const struct intel_crtc_state *crtc_state, + const struct drm_connector_state *conn_state); int intel_hdmi_compute_config(struct intel_encoder *encoder, struct intel_crtc_state *pipe_config, struct drm_connector_state *conn_state); -- cgit v1.2.3 From 4c4279a8d58d146dc39b150226ee974652de4eef Mon Sep 17 00:00:00 2001 From: Suraj Kandpal Date: Mon, 15 May 2023 16:02:22 +0530 Subject: drm/i915/hdcp: add intel_atomic_state argument to hdcp_enable function Pass all the parameter in intel_encoder->enable() to intel_hdcp_enable as we need intel_atomic_state later down to get acquire_ctx. Cc: Jani Nikula Cc: Ankit Nautiyal Signed-off-by: Suraj Kandpal Reviewed-by: Jani Nikula Signed-off-by: Animesh Manna Link: https://patchwork.freedesktop.org/patch/msgid/20230515103225.688830-2-suraj.kandpal@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 4 +--- drivers/gpu/drm/i915/display/intel_dp_mst.c | 4 +--- drivers/gpu/drm/i915/display/intel_hdcp.c | 16 +++++++++------- drivers/gpu/drm/i915/display/intel_hdcp.h | 6 ++++-- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index b1caf521a348..db056bb084d1 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3264,9 +3264,7 @@ static void intel_enable_ddi(struct intel_atomic_state *state, /* Enable hdcp if it's desired */ if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) - intel_hdcp_enable(to_intel_connector(conn_state->connector), - crtc_state, - (u8)conn_state->hdcp_content_type); + intel_hdcp_enable(state, encoder, crtc_state, conn_state); } static void intel_disable_ddi_dp(struct intel_atomic_state *state, diff --git a/drivers/gpu/drm/i915/display/intel_dp_mst.c b/drivers/gpu/drm/i915/display/intel_dp_mst.c index 63d61e610210..6df8519b3347 100644 --- a/drivers/gpu/drm/i915/display/intel_dp_mst.c +++ b/drivers/gpu/drm/i915/display/intel_dp_mst.c @@ -801,9 +801,7 @@ static void intel_mst_enable_dp(struct intel_atomic_state *state, /* Enable hdcp if it's desired */ if (conn_state->content_protection == DRM_MODE_CONTENT_PROTECTION_DESIRED) - intel_hdcp_enable(to_intel_connector(conn_state->connector), - pipe_config, - (u8)conn_state->hdcp_content_type); + intel_hdcp_enable(state, encoder, pipe_config, conn_state); } static bool intel_dp_mst_enc_get_hw_state(struct intel_encoder *encoder, diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index b183efab04a1..bce0fa2e45a1 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -2332,10 +2332,14 @@ int intel_hdcp_init(struct intel_connector *connector, return 0; } -int intel_hdcp_enable(struct intel_connector *connector, - const struct intel_crtc_state *pipe_config, u8 content_type) +int intel_hdcp_enable(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *pipe_config, + const struct drm_connector_state *conn_state) { - struct drm_i915_private *dev_priv = to_i915(connector->base.dev); + struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); + struct intel_connector *connector = + to_intel_connector(conn_state->connector); struct intel_digital_port *dig_port = intel_attached_dig_port(connector); struct intel_hdcp *hdcp = &connector->hdcp; unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS; @@ -2354,7 +2358,7 @@ int intel_hdcp_enable(struct intel_connector *connector, mutex_lock(&dig_port->hdcp_mutex); drm_WARN_ON(&dev_priv->drm, hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED); - hdcp->content_type = content_type; + hdcp->content_type = (u8)conn_state->content_type; if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) { hdcp->cpu_transcoder = pipe_config->mst_master_transcoder; @@ -2485,9 +2489,7 @@ void intel_hdcp_update_pipe(struct intel_atomic_state *state, } if (desired_and_not_enabled || content_protection_type_changed) - intel_hdcp_enable(connector, - crtc_state, - (u8)conn_state->hdcp_content_type); + intel_hdcp_enable(state, encoder, crtc_state, conn_state); } void intel_hdcp_component_fini(struct drm_i915_private *dev_priv) diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h index 8f53b0c7fe5c..ce283f4f69fd 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.h +++ b/drivers/gpu/drm/i915/display/intel_hdcp.h @@ -28,8 +28,10 @@ void intel_hdcp_atomic_check(struct drm_connector *connector, int intel_hdcp_init(struct intel_connector *connector, struct intel_digital_port *dig_port, const struct intel_hdcp_shim *hdcp_shim); -int intel_hdcp_enable(struct intel_connector *connector, - const struct intel_crtc_state *pipe_config, u8 content_type); +int intel_hdcp_enable(struct intel_atomic_state *state, + struct intel_encoder *encoder, + const struct intel_crtc_state *pipe_config, + const struct drm_connector_state *conn_state); int intel_hdcp_disable(struct intel_connector *connector); void intel_hdcp_update_pipe(struct intel_atomic_state *state, struct intel_encoder *encoder, -- cgit v1.2.3 From 60ded7cc86f363161e37dc41c548b2ab3e1af5ce Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Wed, 10 May 2023 13:31:29 +0300 Subject: drm/i915: Factor out a helper for handling atomic modeset locks/state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch simplifying the handling of modeset locks and atomic state for an atomic commit is based on https://lore.kernel.org/all/20210715184954.7794-2-ville.syrjala@linux.intel.com/ adding the helper to i915. I find this approach preferrable than open-coding the corresponding steps (fixed for me an atomic state reset during a DEADLK retry, which I missed in the open-coded version) and also better than the existing DRM_MODESET_LOCK_ALL_BEGIN/END macros for the reasons described in the above original patchset. This change takes the helper into use only for atomic commits during DDI hotplug handling, as a preparation for a follow-up patch adding a similar commit started from the same spot. Other places doing a driver-internal atomic commit is to be converted by a follow-up patchset. Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230510103131.1618266-13-imre.deak@intel.com --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/display/intel_ddi.c | 17 ++------ drivers/gpu/drm/i915/display/intel_modeset_lock.c | 50 +++++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_modeset_lock.h | 33 +++++++++++++++ 4 files changed, 87 insertions(+), 14 deletions(-) create mode 100644 drivers/gpu/drm/i915/display/intel_modeset_lock.c create mode 100644 drivers/gpu/drm/i915/display/intel_modeset_lock.h (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile index 1f4c9b99c2c9..e3b699e7a755 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -265,6 +265,7 @@ i915-y += \ display/intel_hti.o \ display/intel_load_detect.o \ display/intel_lpe_audio.o \ + display/intel_modeset_lock.o \ display/intel_modeset_verify.o \ display/intel_modeset_setup.o \ display/intel_overlay.o \ diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index db056bb084d1..b8ffbbbb4642 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -63,6 +63,7 @@ #include "intel_hti.h" #include "intel_lspcon.h" #include "intel_mg_phy_regs.h" +#include "intel_modeset_lock.h" #include "intel_pps.h" #include "intel_psr.h" #include "intel_quirks.h" @@ -4401,26 +4402,14 @@ intel_ddi_hotplug(struct intel_encoder *encoder, state = intel_encoder_hotplug(encoder, connector); - drm_modeset_acquire_init(&ctx, 0); - - for (;;) { + intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) { if (connector->base.connector_type == DRM_MODE_CONNECTOR_HDMIA) ret = intel_hdmi_reset_link(encoder, &ctx); else ret = intel_dp_retrain_link(encoder, &ctx); - - if (ret == -EDEADLK) { - drm_modeset_backoff(&ctx); - continue; - } - - break; } - drm_modeset_drop_locks(&ctx); - drm_modeset_acquire_fini(&ctx); - drm_WARN(encoder->base.dev, ret, - "Acquiring modeset locks failed with %i\n", ret); + drm_WARN_ON(encoder->base.dev, ret); /* * Unpowered type-c dongles can take some time to boot and be diff --git a/drivers/gpu/drm/i915/display/intel_modeset_lock.c b/drivers/gpu/drm/i915/display/intel_modeset_lock.c new file mode 100644 index 000000000000..8fb6fd849a75 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_modeset_lock.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: MIT +/* + * Copyright © 2023 Intel Corporation + */ + +#include + +#include "intel_display_types.h" +#include "intel_modeset_lock.h" + +void _intel_modeset_lock_begin(struct drm_modeset_acquire_ctx *ctx, + struct intel_atomic_state *state, + unsigned int flags, int *ret) +{ + drm_modeset_acquire_init(ctx, flags); + + if (state) + state->base.acquire_ctx = ctx; + + *ret = -EDEADLK; +} + +bool _intel_modeset_lock_loop(int *ret) +{ + if (*ret == -EDEADLK) { + *ret = 0; + return true; + } + + return false; +} + +void _intel_modeset_lock_end(struct drm_modeset_acquire_ctx *ctx, + struct intel_atomic_state *state, + int *ret) +{ + if (*ret == -EDEADLK) { + if (state) + drm_atomic_state_clear(&state->base); + + *ret = drm_modeset_backoff(ctx); + if (*ret == 0) { + *ret = -EDEADLK; + return; + } + } + + drm_modeset_drop_locks(ctx); + drm_modeset_acquire_fini(ctx); +} diff --git a/drivers/gpu/drm/i915/display/intel_modeset_lock.h b/drivers/gpu/drm/i915/display/intel_modeset_lock.h new file mode 100644 index 000000000000..edb5099bcd99 --- /dev/null +++ b/drivers/gpu/drm/i915/display/intel_modeset_lock.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Copyright © 2023 Intel Corporation + */ + +#ifndef __INTEL_MODESET_LOCK_H__ +#define __INTEL_MODESET_LOCK_H__ + +#include + +struct drm_modeset_acquire_ctx; +struct intel_atomic_state; + +void _intel_modeset_lock_begin(struct drm_modeset_acquire_ctx *ctx, + struct intel_atomic_state *state, + unsigned int flags, + int *ret); +bool _intel_modeset_lock_loop(int *ret); +void _intel_modeset_lock_end(struct drm_modeset_acquire_ctx *ctx, + struct intel_atomic_state *state, + int *ret); + +/* + * Note that one must always use "continue" rather than + * "break" or "return" to handle errors within the + * intel_modeset_lock_ctx_retry() block. + */ +#define intel_modeset_lock_ctx_retry(ctx, state, flags, ret) \ + for (_intel_modeset_lock_begin((ctx), (state), (flags), &(ret)); \ + _intel_modeset_lock_loop(&(ret)); \ + _intel_modeset_lock_end((ctx), (state), &(ret))) + +#endif /* __INTEL_MODESET_LOCK_H__ */ -- cgit v1.2.3 From b61fad5f7e5d859d95a413c3a57f59d007951fa6 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 12 May 2023 22:55:12 +0300 Subject: drm/i915/tc: Call TypeC port flush_work/cleanup without modeset locks held MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Call the TypeC port flush_work and cleanup handlers without the modeset locks held. These don't require the locks, as the work takes - as it should be able to at any point in time - any locks it needs and by the time cleanup is called and after cleanup returns the encoder is not in use. This is required by the next patch canceling a TypeC port work synchronously during encoder suspend and shutdown, where the work can take modeset locks as well, hence the canceling must be done without holding the locks. I also considered moving the modeset locking down to each encoder suspend()/shutdown() hook instead, however locking the full modeset state for each encoder separately would be odd, and the bigger change - affecting all encoders - is beyond the scope of this patchset. v2: - Add a TODO: comment to remove modeset locks if no encoder depends on this. (Ville) Cc: Ville Syrjälä Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230512195513.2699-1-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 27 +++++++++++----------- drivers/gpu/drm/i915/display/intel_display_types.h | 12 ++++++++++ drivers/gpu/drm/i915/i915_driver.c | 16 +++++++++++++ 3 files changed, 41 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index b8ffbbbb4642..6566518eeb7f 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -4615,31 +4615,27 @@ static bool intel_ddi_is_tc(struct drm_i915_private *i915, enum port port) static void intel_ddi_encoder_suspend(struct intel_encoder *encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum phy phy = intel_port_to_phy(i915, encoder->port); - intel_dp_encoder_suspend(encoder); +} - if (!intel_phy_is_tc(i915, phy)) - return; +static void intel_ddi_tc_encoder_suspend_complete(struct intel_encoder *encoder) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); intel_tc_port_flush_work(dig_port); } static void intel_ddi_encoder_shutdown(struct intel_encoder *encoder) { - struct intel_dp *intel_dp = enc_to_intel_dp(encoder); - struct drm_i915_private *i915 = dp_to_i915(intel_dp); - struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - enum phy phy = intel_port_to_phy(i915, encoder->port); - intel_dp_encoder_shutdown(encoder); intel_hdmi_encoder_shutdown(encoder); +} - if (!intel_phy_is_tc(i915, phy)) - return; +static void intel_ddi_tc_encoder_shutdown_complete(struct intel_encoder *encoder) +{ + struct intel_dp *intel_dp = enc_to_intel_dp(encoder); + struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); intel_tc_port_cleanup(dig_port); } @@ -4906,6 +4902,9 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port) is_legacy ? "legacy" : "non-legacy"); } + encoder->suspend_complete = intel_ddi_tc_encoder_suspend_complete; + encoder->shutdown_complete = intel_ddi_tc_encoder_shutdown_complete; + if (intel_tc_port_init(dig_port, is_legacy) < 0) goto err; } diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 270c4c84a292..88b2a55d19f2 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -233,13 +233,25 @@ struct intel_encoder { * Called during system suspend after all pending requests for the * encoder are flushed (for example for DP AUX transactions) and * device interrupts are disabled. + * All modeset locks are held while the hook is called. */ void (*suspend)(struct intel_encoder *); + /* + * Called without the modeset locks held after the suspend() hook for + * all encoders have been called. + */ + void (*suspend_complete)(struct intel_encoder *encoder); /* * Called during system reboot/shutdown after all the * encoders have been disabled and suspended. + * All modeset locks are held while the hook is called. */ void (*shutdown)(struct intel_encoder *encoder); + /* + * Called without the modeset locks held after the shutdown() hook for + * all encoders have been called. + */ + void (*shutdown_complete)(struct intel_encoder *encoder); /* * Enable/disable the clock to the port. */ diff --git a/drivers/gpu/drm/i915/i915_driver.c b/drivers/gpu/drm/i915/i915_driver.c index bf3af32fa962..31d4847eb21c 100644 --- a/drivers/gpu/drm/i915/i915_driver.c +++ b/drivers/gpu/drm/i915/i915_driver.c @@ -957,11 +957,19 @@ static void intel_suspend_encoders(struct drm_i915_private *dev_priv) if (!HAS_DISPLAY(dev_priv)) return; + /* + * TODO: check and remove holding the modeset locks if none of + * the encoders depends on this. + */ drm_modeset_lock_all(&dev_priv->drm); for_each_intel_encoder(&dev_priv->drm, encoder) if (encoder->suspend) encoder->suspend(encoder); drm_modeset_unlock_all(&dev_priv->drm); + + for_each_intel_encoder(&dev_priv->drm, encoder) + if (encoder->suspend_complete) + encoder->suspend_complete(encoder); } static void intel_shutdown_encoders(struct drm_i915_private *dev_priv) @@ -971,11 +979,19 @@ static void intel_shutdown_encoders(struct drm_i915_private *dev_priv) if (!HAS_DISPLAY(dev_priv)) return; + /* + * TODO: check and remove holding the modeset locks if none of + * the encoders depends on this. + */ drm_modeset_lock_all(&dev_priv->drm); for_each_intel_encoder(&dev_priv->drm, encoder) if (encoder->shutdown) encoder->shutdown(encoder); drm_modeset_unlock_all(&dev_priv->drm); + + for_each_intel_encoder(&dev_priv->drm, encoder) + if (encoder->shutdown_complete) + encoder->shutdown_complete(encoder); } void i915_driver_shutdown(struct drm_i915_private *i915) -- cgit v1.2.3 From c598c335da420715670b1adac846e4f3ebd01e40 Mon Sep 17 00:00:00 2001 From: Imre Deak Date: Fri, 12 May 2023 22:55:13 +0300 Subject: drm/i915/tc: Reset TypeC PHYs left enabled in DP-alt mode after the sink disconnects MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the output on a DP-alt link with its sink disconnected is kept enabled for too long (about 20 sec), then some IOM/TCSS firmware timeout will cause havoc on the PCI bus, at least for other GFX devices on it which will stop powering up. Since user space is not guaranteed to do a disabling modeset in time, switch such disconnected but active links to TBT mode - which is without such shortcomings - with a 2 second delay. If the above condition is detected already during the driver load/system resume sanitization step disable the output instead, as at that point no user space or kernel client depends on a consistent output state yet and because subsequent atomic modeset on such connectors - without the actual sink capabilities available - can fail. An active/disconnected port as above will also block the HPD status of other active/disconnected ports to get updated (stuck in the connected state), until the former port is disabled, its PHY is disconnected and a ~10 ms delay has elapsed. This means the link state for all TypeC ports/CRTCs must be rechecked after a CRTC is disabled due to the above reason. For this disconnect the PHY synchronously after the CRTC/port is disabled and recheck all CRTCs for the above condition whenever such a port is disabled. To account for a race condition during driver loading where the sink is disconnected after the above sanitization step and before the HPD interrupts get enabled, do an explicit check/link reset if needed from the encoder's late_register hook, which is called after the HPD interrupts are enabled already. v2: - Handle an active/disconnected port blocking the HPD state update of another active/disconnected port. - Cancel the delayed work resetting the link also from the encoder enable/suspend/shutdown hooks. - Rebase on the earlier intel_modeset_lock_ctx_retry() addition, fixing here the missed atomic state reset in case of a retry. - Fix handling of an error return from intel_atomic_get_crtc_state(). - Recheck if the port needs to be reset after all the atomic state is locked and async commits are waited on. v3: - Add intel_crtc_needs_link_reset(), instead of open-coding it, keep intel_crtc_has_encoders(). (Ville) - Fix state dumping and use a bitmask to track disabled CRTCs in intel_sanitize_all_crtcs(). (Ville) - Set internal in intel_atomic_state right after allocating it. (Ville) - Recheck all CRTCs (not yet force-disabled) after a CRTC is force-disabled for any reason (not only due to a link state) in intel_sanitize_all_crtcs(). - Reduce delay after CRTC disabling to 20ms, and use the simpler msleep(). - Clarify code comment about HPD behaviour in intel_sanitize_all_crtcs(). - Move all the TC link reset logic to intel_tc.c . - Cancel the link reset work synchronously during system suspend, driver unload and shutdown. v4: - Rebased on previous patch, which allows calling the TC port suspend/cleanup handlers without modeset locks held; remove the display driver suspended assert from the link reset work accordingly. v5: (Ville) - Remove reset work canceling from intel_ddi_pre_pll_enable(). - Track a crtc vs. pipe mask in intel_sanitize_all_crtcs(). - Add reset_link_commit() to clarify the intel_modeset_lock_ctx_retry loop. Cc: Kai-Heng Feng Cc: Ville Syrjälä Tested-by: Kai-Heng Feng Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/5860 Reviewed-by: Ville Syrjälä Signed-off-by: Imre Deak Link: https://patchwork.freedesktop.org/patch/msgid/20230512195513.2699-2-imre.deak@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 30 ++-- drivers/gpu/drm/i915/display/intel_dp.c | 6 +- drivers/gpu/drm/i915/display/intel_dp.h | 3 + drivers/gpu/drm/i915/display/intel_modeset_setup.c | 87 +++++++++-- drivers/gpu/drm/i915/display/intel_tc.c | 159 ++++++++++++++++++++- drivers/gpu/drm/i915/display/intel_tc.h | 5 +- 6 files changed, 265 insertions(+), 25 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 6566518eeb7f..9c2b7f7deb63 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -3311,6 +3311,8 @@ static void intel_disable_ddi(struct intel_atomic_state *state, const struct intel_crtc_state *old_crtc_state, const struct drm_connector_state *old_conn_state) { + intel_tc_port_link_cancel_reset_work(enc_to_dig_port(encoder)); + intel_hdcp_disable(to_intel_connector(old_conn_state->connector)); if (intel_crtc_has_type(old_crtc_state, INTEL_OUTPUT_HDMI)) @@ -4230,9 +4232,19 @@ static void intel_ddi_encoder_reset(struct drm_encoder *encoder) intel_tc_port_init_mode(dig_port); } +static int intel_ddi_encoder_late_register(struct drm_encoder *_encoder) +{ + struct intel_encoder *encoder = to_intel_encoder(_encoder); + + intel_tc_port_link_reset(enc_to_dig_port(encoder)); + + return 0; +} + static const struct drm_encoder_funcs intel_ddi_funcs = { .reset = intel_ddi_encoder_reset, .destroy = intel_ddi_encoder_destroy, + .late_register = intel_ddi_encoder_late_register, }; static struct intel_connector * @@ -4402,14 +4414,16 @@ intel_ddi_hotplug(struct intel_encoder *encoder, state = intel_encoder_hotplug(encoder, connector); - intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) { - if (connector->base.connector_type == DRM_MODE_CONNECTOR_HDMIA) - ret = intel_hdmi_reset_link(encoder, &ctx); - else - ret = intel_dp_retrain_link(encoder, &ctx); - } + if (!intel_tc_port_link_reset(dig_port)) { + intel_modeset_lock_ctx_retry(&ctx, NULL, 0, ret) { + if (connector->base.connector_type == DRM_MODE_CONNECTOR_HDMIA) + ret = intel_hdmi_reset_link(encoder, &ctx); + else + ret = intel_dp_retrain_link(encoder, &ctx); + } - drm_WARN_ON(encoder->base.dev, ret); + drm_WARN_ON(encoder->base.dev, ret); + } /* * Unpowered type-c dongles can take some time to boot and be @@ -4623,7 +4637,7 @@ static void intel_ddi_tc_encoder_suspend_complete(struct intel_encoder *encoder) struct intel_dp *intel_dp = enc_to_intel_dp(encoder); struct intel_digital_port *dig_port = dp_to_dig_port(intel_dp); - intel_tc_port_flush_work(dig_port); + intel_tc_port_suspend(dig_port); } static void intel_ddi_encoder_shutdown(struct intel_encoder *encoder) diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 088d67c44824..4bec8cd7979f 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -4232,9 +4232,9 @@ static bool intel_dp_has_connector(struct intel_dp *intel_dp, return false; } -static int intel_dp_get_active_pipes(struct intel_dp *intel_dp, - struct drm_modeset_acquire_ctx *ctx, - u8 *pipe_mask) +int intel_dp_get_active_pipes(struct intel_dp *intel_dp, + struct drm_modeset_acquire_ctx *ctx, + u8 *pipe_mask) { struct drm_i915_private *i915 = dp_to_i915(intel_dp); struct drm_connector_list_iter conn_iter; diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index ef39e4f7a329..5f86157a10d2 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -42,6 +42,9 @@ void intel_dp_set_link_params(struct intel_dp *intel_dp, int link_rate, int lane_count); int intel_dp_get_link_train_fallback_values(struct intel_dp *intel_dp, int link_rate, u8 lane_count); +int intel_dp_get_active_pipes(struct intel_dp *intel_dp, + struct drm_modeset_acquire_ctx *ctx, + u8 *pipe_mask); int intel_dp_retrain_link(struct intel_encoder *encoder, struct drm_modeset_acquire_ctx *ctx); void intel_dp_set_power(struct intel_dp *intel_dp, u8 mode); diff --git a/drivers/gpu/drm/i915/display/intel_modeset_setup.c b/drivers/gpu/drm/i915/display/intel_modeset_setup.c index 75b4dea1e442..5ff99ca7f1de 100644 --- a/drivers/gpu/drm/i915/display/intel_modeset_setup.c +++ b/drivers/gpu/drm/i915/display/intel_modeset_setup.c @@ -26,6 +26,7 @@ #include "intel_fifo_underrun.h" #include "intel_modeset_setup.h" #include "intel_pch_display.h" +#include "intel_tc.h" #include "intel_vblank.h" #include "intel_wm.h" #include "skl_watermark.h" @@ -379,6 +380,21 @@ static bool intel_crtc_has_encoders(struct intel_crtc *crtc) return false; } +static bool intel_crtc_needs_link_reset(struct intel_crtc *crtc) +{ + struct drm_device *dev = crtc->base.dev; + struct intel_encoder *encoder; + + for_each_encoder_on_crtc(dev, &crtc->base, encoder) { + struct intel_digital_port *dig_port = enc_to_dig_port(encoder); + + if (dig_port && intel_tc_port_link_needs_reset(dig_port)) + return true; + } + + return false; +} + static struct intel_connector *intel_encoder_find_connector(struct intel_encoder *encoder) { struct drm_i915_private *i915 = to_i915(encoder->base.dev); @@ -421,11 +437,12 @@ static void intel_sanitize_fifo_underrun_reporting(const struct intel_crtc_state !HAS_GMCH(i915)); } -static void intel_sanitize_crtc(struct intel_crtc *crtc, +static bool intel_sanitize_crtc(struct intel_crtc *crtc, struct drm_modeset_acquire_ctx *ctx) { struct drm_i915_private *i915 = to_i915(crtc->base.dev); struct intel_crtc_state *crtc_state = to_intel_crtc_state(crtc->base.state); + bool needs_link_reset; if (crtc_state->hw.active) { struct intel_plane *plane; @@ -445,13 +462,67 @@ static void intel_sanitize_crtc(struct intel_crtc *crtc, intel_color_commit_arm(crtc_state); } + if (!crtc_state->hw.active || + intel_crtc_is_bigjoiner_slave(crtc_state)) + return false; + + needs_link_reset = intel_crtc_needs_link_reset(crtc); + /* * Adjust the state of the output pipe according to whether we have * active connectors/encoders. */ - if (crtc_state->hw.active && !intel_crtc_has_encoders(crtc) && - !intel_crtc_is_bigjoiner_slave(crtc_state)) - intel_crtc_disable_noatomic(crtc, ctx); + if (!needs_link_reset && intel_crtc_has_encoders(crtc)) + return false; + + intel_crtc_disable_noatomic(crtc, ctx); + + /* + * The HPD state on other active/disconnected TC ports may be stuck in + * the connected state until this port is disabled and a ~10ms delay has + * passed, wait here for that so that sanitizing other CRTCs will see the + * up-to-date HPD state. + */ + if (needs_link_reset) + msleep(20); + + return true; +} + +static void intel_sanitize_all_crtcs(struct drm_i915_private *i915, + struct drm_modeset_acquire_ctx *ctx) +{ + struct intel_crtc *crtc; + u32 crtcs_forced_off = 0; + + /* + * An active and disconnected TypeC port prevents the HPD live state + * to get updated on other active/disconnected TypeC ports, so after + * a port gets disabled the CRTCs using other TypeC ports must be + * rechecked wrt. their link status. + */ + for (;;) { + u32 old_mask = crtcs_forced_off; + + for_each_intel_crtc(&i915->drm, crtc) { + u32 crtc_mask = drm_crtc_mask(&crtc->base); + + if (crtcs_forced_off & crtc_mask) + continue; + + if (intel_sanitize_crtc(crtc, ctx)) + crtcs_forced_off |= crtc_mask; + } + if (crtcs_forced_off == old_mask) + break; + } + + for_each_intel_crtc(&i915->drm, crtc) { + struct intel_crtc_state *crtc_state = + to_intel_crtc_state(crtc->base.state); + + intel_crtc_state_dump(crtc_state, NULL, "setup_hw_state"); + } } static bool has_bogus_dpll_config(const struct intel_crtc_state *crtc_state) @@ -871,13 +942,7 @@ void intel_modeset_setup_hw_state(struct drm_i915_private *i915, */ intel_modeset_update_connector_atomic_state(i915); - for_each_intel_crtc(&i915->drm, crtc) { - struct intel_crtc_state *crtc_state = - to_intel_crtc_state(crtc->base.state); - - intel_sanitize_crtc(crtc, ctx); - intel_crtc_state_dump(crtc_state, NULL, "setup_hw_state"); - } + intel_sanitize_all_crtcs(i915, ctx); intel_dpll_sanitize_state(i915); diff --git a/drivers/gpu/drm/i915/display/intel_tc.c b/drivers/gpu/drm/i915/display/intel_tc.c index 4fca711a58bc..3ebf41859043 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.c +++ b/drivers/gpu/drm/i915/display/intel_tc.c @@ -5,15 +5,19 @@ #include "i915_drv.h" #include "i915_reg.h" +#include "intel_atomic.h" #include "intel_cx0_phy_regs.h" #include "intel_ddi.h" #include "intel_de.h" #include "intel_display.h" +#include "intel_display_driver.h" #include "intel_display_power_map.h" #include "intel_display_types.h" #include "intel_dkl_phy_regs.h" +#include "intel_dp.h" #include "intel_dp_mst.h" #include "intel_mg_phy_regs.h" +#include "intel_modeset_lock.h" #include "intel_tc.h" #define DP_PIN_ASSIGNMENT_C 0x3 @@ -51,6 +55,7 @@ struct intel_tc_port { enum intel_display_power_domain lock_power_domain; #endif struct delayed_work disconnect_phy_work; + struct delayed_work link_reset_work; int link_refcount; bool legacy_port:1; char port_name[8]; @@ -1572,6 +1577,138 @@ bool intel_tc_port_connected(struct intel_encoder *encoder) return is_connected; } +static bool __intel_tc_port_link_needs_reset(struct intel_tc_port *tc) +{ + bool ret; + + mutex_lock(&tc->lock); + + ret = tc->link_refcount && + tc->mode == TC_PORT_DP_ALT && + intel_tc_port_needs_reset(tc); + + mutex_unlock(&tc->lock); + + return ret; +} + +bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum phy phy = intel_port_to_phy(i915, dig_port->base.port); + + if (!intel_phy_is_tc(i915, phy)) + return false; + + return __intel_tc_port_link_needs_reset(to_tc_port(dig_port)); +} + +static int reset_link_commit(struct intel_tc_port *tc, + struct intel_atomic_state *state, + struct drm_modeset_acquire_ctx *ctx) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + struct intel_digital_port *dig_port = tc->dig_port; + struct intel_dp *intel_dp = enc_to_intel_dp(&dig_port->base); + struct intel_crtc *crtc; + u8 pipe_mask; + int ret; + + ret = drm_modeset_lock(&i915->drm.mode_config.connection_mutex, ctx); + if (ret) + return ret; + + ret = intel_dp_get_active_pipes(intel_dp, ctx, &pipe_mask); + if (ret) + return ret; + + if (!pipe_mask) + return 0; + + for_each_intel_crtc_in_pipe_mask(&i915->drm, crtc, pipe_mask) { + struct intel_crtc_state *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; + } + + if (!__intel_tc_port_link_needs_reset(tc)) + return 0; + + return drm_atomic_commit(&state->base); +} + +static int reset_link(struct intel_tc_port *tc) +{ + struct drm_i915_private *i915 = tc_to_i915(tc); + struct drm_modeset_acquire_ctx ctx; + struct drm_atomic_state *_state; + struct intel_atomic_state *state; + int ret; + + _state = drm_atomic_state_alloc(&i915->drm); + if (!_state) + return -ENOMEM; + + state = to_intel_atomic_state(_state); + state->internal = true; + + intel_modeset_lock_ctx_retry(&ctx, state, 0, ret) + ret = reset_link_commit(tc, state, &ctx); + + drm_atomic_state_put(&state->base); + + return ret; +} + +static void intel_tc_port_link_reset_work(struct work_struct *work) +{ + struct intel_tc_port *tc = + container_of(work, struct intel_tc_port, link_reset_work.work); + struct drm_i915_private *i915 = tc_to_i915(tc); + int ret; + + if (!__intel_tc_port_link_needs_reset(tc)) + return; + + mutex_lock(&i915->drm.mode_config.mutex); + + drm_dbg_kms(&i915->drm, + "Port %s: TypeC DP-alt sink disconnected, resetting link\n", + tc->port_name); + ret = reset_link(tc); + drm_WARN_ON(&i915->drm, ret); + + mutex_unlock(&i915->drm.mode_config.mutex); +} + +bool intel_tc_port_link_reset(struct intel_digital_port *dig_port) +{ + if (!intel_tc_port_link_needs_reset(dig_port)) + return false; + + queue_delayed_work(system_unbound_wq, + &to_tc_port(dig_port)->link_reset_work, + msecs_to_jiffies(2000)); + + return true; +} + +void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port) +{ + struct drm_i915_private *i915 = to_i915(dig_port->base.base.dev); + enum phy phy = intel_port_to_phy(i915, dig_port->base.port); + struct intel_tc_port *tc = to_tc_port(dig_port); + + if (!intel_phy_is_tc(i915, phy)) + return; + + cancel_delayed_work(&tc->link_reset_work); +} + static void __intel_tc_port_lock(struct intel_tc_port *tc, int required_lanes) { @@ -1619,11 +1756,19 @@ static void intel_tc_port_disconnect_phy_work(struct work_struct *work) * * Flush the delayed work disconnecting an idle PHY. */ -void intel_tc_port_flush_work(struct intel_digital_port *dig_port) +static void intel_tc_port_flush_work(struct intel_digital_port *dig_port) { flush_delayed_work(&to_tc_port(dig_port)->disconnect_phy_work); } +void intel_tc_port_suspend(struct intel_digital_port *dig_port) +{ + struct intel_tc_port *tc = to_tc_port(dig_port); + + cancel_delayed_work_sync(&tc->link_reset_work); + intel_tc_port_flush_work(dig_port); +} + void intel_tc_port_unlock(struct intel_digital_port *dig_port) { struct intel_tc_port *tc = to_tc_port(dig_port); @@ -1660,6 +1805,14 @@ void intel_tc_port_put_link(struct intel_digital_port *dig_port) intel_tc_port_lock(dig_port); __intel_tc_port_put_link(tc); intel_tc_port_unlock(dig_port); + + /* + * The firmware will not update the HPD status of other TypeC ports + * that are active in DP-alt mode with their sink disconnected, until + * this port is disabled and its PHY gets disconnected. Make sure this + * happens in a timely manner by disconnecting the PHY synchronously. + */ + intel_tc_port_flush_work(dig_port); } int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) @@ -1692,7 +1845,9 @@ int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) "%c/TC#%d", port_name(port), tc_port + 1); mutex_init(&tc->lock); + /* TODO: Combine the two works */ INIT_DELAYED_WORK(&tc->disconnect_phy_work, intel_tc_port_disconnect_phy_work); + INIT_DELAYED_WORK(&tc->link_reset_work, intel_tc_port_link_reset_work); tc->legacy_port = is_legacy; tc->mode = TC_PORT_DISCONNECTED; tc->link_refcount = 0; @@ -1706,7 +1861,7 @@ int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy) void intel_tc_port_cleanup(struct intel_digital_port *dig_port) { - intel_tc_port_flush_work(dig_port); + intel_tc_port_suspend(dig_port); kfree(dig_port->tc); dig_port->tc = NULL; diff --git a/drivers/gpu/drm/i915/display/intel_tc.h b/drivers/gpu/drm/i915/display/intel_tc.h index dd0810f9ea95..3b16491925fa 100644 --- a/drivers/gpu/drm/i915/display/intel_tc.h +++ b/drivers/gpu/drm/i915/display/intel_tc.h @@ -30,11 +30,14 @@ void intel_tc_port_sanitize_mode(struct intel_digital_port *dig_port, const struct intel_crtc_state *crtc_state); void intel_tc_port_lock(struct intel_digital_port *dig_port); void intel_tc_port_unlock(struct intel_digital_port *dig_port); -void intel_tc_port_flush_work(struct intel_digital_port *dig_port); +void intel_tc_port_suspend(struct intel_digital_port *dig_port); void intel_tc_port_get_link(struct intel_digital_port *dig_port, int required_lanes); void intel_tc_port_put_link(struct intel_digital_port *dig_port); bool intel_tc_port_ref_held(struct intel_digital_port *dig_port); +bool intel_tc_port_link_needs_reset(struct intel_digital_port *dig_port); +bool intel_tc_port_link_reset(struct intel_digital_port *dig_port); +void intel_tc_port_link_cancel_reset_work(struct intel_digital_port *dig_port); int intel_tc_port_init(struct intel_digital_port *dig_port, bool is_legacy); void intel_tc_port_cleanup(struct intel_digital_port *dig_port); -- cgit v1.2.3 From 7ffa2f27cf0aa1a89bea7fdacd9a51668ec32e7b Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Tue, 30 May 2023 12:08:16 +0300 Subject: drm/i915/dp: stop caching has_hdmi_sink in struct intel_dp Use the information stored in display info. Add intel_dp_has_hdmi_sink() helper to access it. v2: Rebased Reviewed-by: Ankit Nautiyal Signed-off-by: Jani Nikula Link: https://patchwork.freedesktop.org/patch/msgid/748103fda572b3552e5bbdafb300d8508d4eeaf4.1685437500.git.jani.nikula@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 6 +++--- drivers/gpu/drm/i915/display/intel_display_types.h | 1 - drivers/gpu/drm/i915/display/intel_dp.c | 21 +++++++++++---------- drivers/gpu/drm/i915/display/intel_dp.h | 1 + 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index d1a9a3cf94b5..70d44edd8c6e 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2801,7 +2801,7 @@ static void intel_ddi_pre_enable(struct intel_atomic_state *state, /* FIXME precompute everything properly */ /* FIXME how do we turn infoframes off again? */ - if (dig_port->lspcon.active && dig_port->dp.has_hdmi_sink) + if (dig_port->lspcon.active && intel_dp_has_hdmi_sink(&dig_port->dp)) dig_port->set_infoframes(encoder, crtc_state->has_infoframe, crtc_state, conn_state); @@ -3110,7 +3110,7 @@ static void intel_enable_ddi_dp(struct intel_atomic_state *state, drm_connector_update_privacy_screen(conn_state); intel_edp_backlight_on(crtc_state, conn_state); - if (!dig_port->lspcon.active || dig_port->dp.has_hdmi_sink) + if (!dig_port->lspcon.active || intel_dp_has_hdmi_sink(&dig_port->dp)) intel_dp_set_infoframes(encoder, true, crtc_state, conn_state); intel_audio_codec_enable(encoder, crtc_state, conn_state); @@ -3738,7 +3738,7 @@ static void intel_ddi_read_func_ctl(struct intel_encoder *encoder, pipe_config->fec_enable); } - if (dig_port->lspcon.active && dig_port->dp.has_hdmi_sink) + if (dig_port->lspcon.active && intel_dp_has_hdmi_sink(&dig_port->dp)) pipe_config->infoframes.enable |= intel_lspcon_infoframes_enabled(encoder, pipe_config); else diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h index 94a59febc987..c5bd0c7be2fa 100644 --- a/drivers/gpu/drm/i915/display/intel_display_types.h +++ b/drivers/gpu/drm/i915/display/intel_display_types.h @@ -1707,7 +1707,6 @@ struct intel_dp { u8 lane_count; u8 sink_count; bool link_trained; - bool has_hdmi_sink; bool reset_link_params; bool use_max_params; u8 dpcd[DP_RECEIVER_CAP_SIZE]; diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index aa1445ad9da8..8445a37d9d01 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -1287,6 +1287,13 @@ void intel_dp_compute_rate(struct intel_dp *intel_dp, int port_clock, } } +bool intel_dp_has_hdmi_sink(struct intel_dp *intel_dp) +{ + struct intel_connector *connector = intel_dp->attached_connector; + + return connector->base.display_info.is_hdmi; +} + static bool intel_dp_source_supports_fec(struct intel_dp *intel_dp, const struct intel_crtc_state *pipe_config) { @@ -1342,7 +1349,7 @@ static int intel_dp_hdmi_compute_bpc(struct intel_dp *intel_dp, for (; bpc >= 8; bpc -= 2) { if (intel_hdmi_bpc_possible(crtc_state, bpc, - intel_dp->has_hdmi_sink) && + intel_dp_has_hdmi_sink(intel_dp)) && intel_dp_tmds_clock_valid(intel_dp, clock, bpc, crtc_state->sink_format, respect_downstream_limits) == MODE_OK) return bpc; @@ -2732,7 +2739,7 @@ frl_trained: static bool intel_dp_is_hdmi_2_1_sink(struct intel_dp *intel_dp) { if (drm_dp_is_branch(intel_dp->dpcd) && - intel_dp->has_hdmi_sink && + intel_dp_has_hdmi_sink(intel_dp) && intel_dp_hdmi_sink_max_frl(intel_dp) > 0) return true; @@ -2900,13 +2907,12 @@ void intel_dp_configure_protocol_converter(struct intel_dp *intel_dp, if (!drm_dp_is_branch(intel_dp->dpcd)) return; - tmp = intel_dp->has_hdmi_sink ? - DP_HDMI_DVI_OUTPUT_CONFIG : 0; + tmp = intel_dp_has_hdmi_sink(intel_dp) ? DP_HDMI_DVI_OUTPUT_CONFIG : 0; if (drm_dp_dpcd_writeb(&intel_dp->aux, DP_PROTOCOL_CONVERTER_CONTROL_0, tmp) != 1) drm_dbg_kms(&i915->drm, "Failed to %s protocol converter HDMI mode\n", - str_enable_disable(intel_dp->has_hdmi_sink)); + str_enable_disable(intel_dp_has_hdmi_sink(intel_dp))); if (crtc_state->sink_format == INTEL_OUTPUT_FORMAT_YCBCR420) { switch (crtc_state->output_format) { @@ -4812,9 +4818,6 @@ intel_dp_set_edid(struct intel_dp *intel_dp) /* FIXME: Get rid of drm_edid_raw() */ edid = drm_edid_raw(drm_edid); - if (edid && edid->input & DRM_EDID_INPUT_DIGITAL) { - intel_dp->has_hdmi_sink = drm_detect_hdmi_monitor(edid); - } drm_dp_cec_set_edid(&intel_dp->aux, edid); } @@ -4828,8 +4831,6 @@ intel_dp_unset_edid(struct intel_dp *intel_dp) drm_edid_free(connector->detect_edid); connector->detect_edid = NULL; - intel_dp->has_hdmi_sink = false; - intel_dp->dfp.max_bpc = 0; intel_dp->dfp.max_dotclock = 0; intel_dp->dfp.min_tmds_clock = 0; diff --git a/drivers/gpu/drm/i915/display/intel_dp.h b/drivers/gpu/drm/i915/display/intel_dp.h index 5f86157a10d2..22099de3ca45 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.h +++ b/drivers/gpu/drm/i915/display/intel_dp.h @@ -65,6 +65,7 @@ int intel_dp_dsc_compute_config(struct intel_dp *intel_dp, struct link_config_limits *limits, int timeslots, bool recompute_pipe_bpp); +bool intel_dp_has_hdmi_sink(struct intel_dp *intel_dp); bool intel_dp_is_edp(struct intel_dp *intel_dp); bool intel_dp_is_uhbr(const struct intel_crtc_state *crtc_state); bool intel_dp_is_port_edp(struct drm_i915_private *dev_priv, enum port port); -- cgit v1.2.3 From 3ba39084d6fc5dd4db321b367f8bbd46c6ea90ff Mon Sep 17 00:00:00 2001 From: Jani Nikula Date: Fri, 26 May 2023 19:37:55 +0300 Subject: drm/i915/ddi: drop unused but set variable intel_dp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prepare for re-enabling -Wunused-but-set-variable. The intel_dp variable has been unused since commit ef79fafe9dae ("drm/i915: Eliminate intel_dp.regs.dp_tp_{ctl,status}"). Signed-off-by: Jani Nikula Reviewed-by: Jouni Högander Link: https://patchwork.freedesktop.org/patch/msgid/66ca543b400a2048a6a84bb57a7bac8943014a96.1685119007.git.jani.nikula@intel.com --- drivers/gpu/drm/i915/display/intel_ddi.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/gpu/drm/i915/display/intel_ddi.c') diff --git a/drivers/gpu/drm/i915/display/intel_ddi.c b/drivers/gpu/drm/i915/display/intel_ddi.c index 70d44edd8c6e..090f242e610c 100644 --- a/drivers/gpu/drm/i915/display/intel_ddi.c +++ b/drivers/gpu/drm/i915/display/intel_ddi.c @@ -2225,12 +2225,10 @@ static void intel_ddi_enable_fec(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - struct intel_dp *intel_dp; if (!crtc_state->fec_enable) return; - intel_dp = enc_to_intel_dp(encoder); intel_de_rmw(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), 0, DP_TP_CTL_FEC_ENABLE); } @@ -2239,12 +2237,10 @@ static void intel_ddi_disable_fec_state(struct intel_encoder *encoder, const struct intel_crtc_state *crtc_state) { struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); - struct intel_dp *intel_dp; if (!crtc_state->fec_enable) return; - intel_dp = enc_to_intel_dp(encoder); intel_de_rmw(dev_priv, dp_tp_ctl_reg(encoder, crtc_state), DP_TP_CTL_FEC_ENABLE, 0); intel_de_posting_read(dev_priv, dp_tp_ctl_reg(encoder, crtc_state)); -- cgit v1.2.3