mirror of
https://github.com/torvalds/linux.git
synced 2026-04-24 01:25:49 -04:00
The dedicated display PHYs reset to a power state that blocks S0ix,
increasing idle system power. After a system reset (cold boot,
S3/4/5, warm reset) if a dedicated PHY is not being brought up
shortly, use these steps to move the PHY to the lowest power state
to save power.
1. Follow the PLL Enable Sequence, using any valid frequency such
as DP 1.62 GHz. This brings lanes out of reset and enables the
PLL to allow powerdown to be moved to the Disable state.
2. Follow PLL Disable Sequence. This moves powerdown to the Disable
state and disables the PLL.
v2: Rename WA function to more descriptive (Jani)
For PTL, only port A needs this wa
Add helpers to check presence of C10 phy and pll enabling (Imre)
v3: Rename wa function (Imre)
Check return value of C10 pll tables readout (Imre)
Use PLL request to check pll enabling (Imre)
v4: Move intel_cx0_pll_is_enabled() right after
intel_cx0_pll_disable() (Imre)
Add drm_WARN_ON() if C10 state cannot be calculated from
the tables (Imre)
v5: Add debug message on PLL enabling (Imre)
Add check for intel_encoder_is_dig_port() (Imre)
Signed-off-by: Mika Kahola <mika.kahola@intel.com>
Reviewed-by: Imre Deak <imre.deak@intel.com>
Link: https://patchwork.freedesktop.org/patch/msgid/20250218100019.740556-3-mika.kahola@intel.com
139 lines
3.6 KiB
C
139 lines
3.6 KiB
C
// SPDX-License-Identifier: MIT
|
|
/*
|
|
* Copyright © 2023 Intel Corporation
|
|
*/
|
|
|
|
#include <drm/drm_atomic_helper.h>
|
|
|
|
#include "i915_drv.h"
|
|
#include "intel_clock_gating.h"
|
|
#include "intel_cx0_phy.h"
|
|
#include "intel_display_driver.h"
|
|
#include "intel_display_reset.h"
|
|
#include "intel_display_types.h"
|
|
#include "intel_hotplug.h"
|
|
#include "intel_pps.h"
|
|
|
|
static bool gpu_reset_clobbers_display(struct drm_i915_private *dev_priv)
|
|
{
|
|
return (INTEL_INFO(dev_priv)->gpu_reset_clobbers_display &&
|
|
intel_has_gpu_reset(to_gt(dev_priv)));
|
|
}
|
|
|
|
void intel_display_reset_prepare(struct drm_i915_private *dev_priv)
|
|
{
|
|
struct drm_modeset_acquire_ctx *ctx = &dev_priv->display.restore.reset_ctx;
|
|
struct drm_atomic_state *state;
|
|
int ret;
|
|
|
|
if (!HAS_DISPLAY(dev_priv))
|
|
return;
|
|
|
|
/* reset doesn't touch the display */
|
|
if (!dev_priv->display.params.force_reset_modeset_test &&
|
|
!gpu_reset_clobbers_display(dev_priv))
|
|
return;
|
|
|
|
/* We have a modeset vs reset deadlock, defensively unbreak it. */
|
|
set_bit(I915_RESET_MODESET, &to_gt(dev_priv)->reset.flags);
|
|
smp_mb__after_atomic();
|
|
wake_up_bit(&to_gt(dev_priv)->reset.flags, I915_RESET_MODESET);
|
|
|
|
if (atomic_read(&dev_priv->gpu_error.pending_fb_pin)) {
|
|
drm_dbg_kms(&dev_priv->drm,
|
|
"Modeset potentially stuck, unbreaking through wedging\n");
|
|
intel_gt_set_wedged(to_gt(dev_priv));
|
|
}
|
|
|
|
/*
|
|
* Need mode_config.mutex so that we don't
|
|
* trample ongoing ->detect() and whatnot.
|
|
*/
|
|
mutex_lock(&dev_priv->drm.mode_config.mutex);
|
|
drm_modeset_acquire_init(ctx, 0);
|
|
while (1) {
|
|
ret = drm_modeset_lock_all_ctx(&dev_priv->drm, ctx);
|
|
if (ret != -EDEADLK)
|
|
break;
|
|
|
|
drm_modeset_backoff(ctx);
|
|
}
|
|
/*
|
|
* Disabling the crtcs gracefully seems nicer. Also the
|
|
* g33 docs say we should at least disable all the planes.
|
|
*/
|
|
state = drm_atomic_helper_duplicate_state(&dev_priv->drm, ctx);
|
|
if (IS_ERR(state)) {
|
|
ret = PTR_ERR(state);
|
|
drm_err(&dev_priv->drm, "Duplicating state failed with %i\n",
|
|
ret);
|
|
return;
|
|
}
|
|
|
|
ret = drm_atomic_helper_disable_all(&dev_priv->drm, ctx);
|
|
if (ret) {
|
|
drm_err(&dev_priv->drm, "Suspending crtc's failed with %i\n",
|
|
ret);
|
|
drm_atomic_state_put(state);
|
|
return;
|
|
}
|
|
|
|
dev_priv->display.restore.modeset_state = state;
|
|
state->acquire_ctx = ctx;
|
|
}
|
|
|
|
void intel_display_reset_finish(struct drm_i915_private *i915)
|
|
{
|
|
struct intel_display *display = &i915->display;
|
|
struct drm_modeset_acquire_ctx *ctx = &display->restore.reset_ctx;
|
|
struct drm_atomic_state *state;
|
|
int ret;
|
|
|
|
if (!HAS_DISPLAY(i915))
|
|
return;
|
|
|
|
/* reset doesn't touch the display */
|
|
if (!test_bit(I915_RESET_MODESET, &to_gt(i915)->reset.flags))
|
|
return;
|
|
|
|
state = fetch_and_zero(&display->restore.modeset_state);
|
|
if (!state)
|
|
goto unlock;
|
|
|
|
/* reset doesn't touch the display */
|
|
if (!gpu_reset_clobbers_display(i915)) {
|
|
/* for testing only restore the display */
|
|
ret = drm_atomic_helper_commit_duplicated_state(state, ctx);
|
|
if (ret) {
|
|
drm_WARN_ON(&i915->drm, ret == -EDEADLK);
|
|
drm_err(&i915->drm,
|
|
"Restoring old state failed with %i\n", ret);
|
|
}
|
|
} else {
|
|
/*
|
|
* The display has been reset as well,
|
|
* so need a full re-initialization.
|
|
*/
|
|
intel_pps_unlock_regs_wa(display);
|
|
intel_display_driver_init_hw(display);
|
|
intel_clock_gating_init(i915);
|
|
intel_cx0_pll_power_save_wa(display);
|
|
intel_hpd_init(i915);
|
|
|
|
ret = __intel_display_driver_resume(display, state, ctx);
|
|
if (ret)
|
|
drm_err(&i915->drm,
|
|
"Restoring old state failed with %i\n", ret);
|
|
|
|
intel_hpd_poll_disable(i915);
|
|
}
|
|
|
|
drm_atomic_state_put(state);
|
|
unlock:
|
|
drm_modeset_drop_locks(ctx);
|
|
drm_modeset_acquire_fini(ctx);
|
|
mutex_unlock(&i915->drm.mode_config.mutex);
|
|
|
|
clear_bit_unlock(I915_RESET_MODESET, &to_gt(i915)->reset.flags);
|
|
}
|