1 | // SPDX-License-Identifier: MIT |
2 | /* |
3 | * Copyright © 2023 Intel Corporation |
4 | */ |
5 | |
6 | #include <drm/drm_atomic_helper.h> |
7 | |
8 | #include "i915_drv.h" |
9 | #include "intel_clock_gating.h" |
10 | #include "intel_display_driver.h" |
11 | #include "intel_display_reset.h" |
12 | #include "intel_display_types.h" |
13 | #include "intel_hotplug.h" |
14 | #include "intel_pps.h" |
15 | |
16 | static bool gpu_reset_clobbers_display(struct drm_i915_private *dev_priv) |
17 | { |
18 | return (INTEL_INFO(dev_priv)->gpu_reset_clobbers_display && |
19 | intel_has_gpu_reset(gt: to_gt(i915: dev_priv))); |
20 | } |
21 | |
22 | void intel_display_reset_prepare(struct drm_i915_private *dev_priv) |
23 | { |
24 | struct drm_modeset_acquire_ctx *ctx = &dev_priv->display.restore.reset_ctx; |
25 | struct drm_atomic_state *state; |
26 | int ret; |
27 | |
28 | if (!HAS_DISPLAY(dev_priv)) |
29 | return; |
30 | |
31 | /* reset doesn't touch the display */ |
32 | if (!dev_priv->display.params.force_reset_modeset_test && |
33 | !gpu_reset_clobbers_display(dev_priv)) |
34 | return; |
35 | |
36 | /* We have a modeset vs reset deadlock, defensively unbreak it. */ |
37 | set_bit(I915_RESET_MODESET, addr: &to_gt(i915: dev_priv)->reset.flags); |
38 | smp_mb__after_atomic(); |
39 | wake_up_bit(word: &to_gt(i915: dev_priv)->reset.flags, I915_RESET_MODESET); |
40 | |
41 | if (atomic_read(v: &dev_priv->gpu_error.pending_fb_pin)) { |
42 | drm_dbg_kms(&dev_priv->drm, |
43 | "Modeset potentially stuck, unbreaking through wedging\n" ); |
44 | intel_gt_set_wedged(gt: to_gt(i915: dev_priv)); |
45 | } |
46 | |
47 | /* |
48 | * Need mode_config.mutex so that we don't |
49 | * trample ongoing ->detect() and whatnot. |
50 | */ |
51 | mutex_lock(&dev_priv->drm.mode_config.mutex); |
52 | drm_modeset_acquire_init(ctx, flags: 0); |
53 | while (1) { |
54 | ret = drm_modeset_lock_all_ctx(dev: &dev_priv->drm, ctx); |
55 | if (ret != -EDEADLK) |
56 | break; |
57 | |
58 | drm_modeset_backoff(ctx); |
59 | } |
60 | /* |
61 | * Disabling the crtcs gracefully seems nicer. Also the |
62 | * g33 docs say we should at least disable all the planes. |
63 | */ |
64 | state = drm_atomic_helper_duplicate_state(dev: &dev_priv->drm, ctx); |
65 | if (IS_ERR(ptr: state)) { |
66 | ret = PTR_ERR(ptr: state); |
67 | drm_err(&dev_priv->drm, "Duplicating state failed with %i\n" , |
68 | ret); |
69 | return; |
70 | } |
71 | |
72 | ret = drm_atomic_helper_disable_all(dev: &dev_priv->drm, ctx); |
73 | if (ret) { |
74 | drm_err(&dev_priv->drm, "Suspending crtc's failed with %i\n" , |
75 | ret); |
76 | drm_atomic_state_put(state); |
77 | return; |
78 | } |
79 | |
80 | dev_priv->display.restore.modeset_state = state; |
81 | state->acquire_ctx = ctx; |
82 | } |
83 | |
84 | void intel_display_reset_finish(struct drm_i915_private *i915) |
85 | { |
86 | struct drm_modeset_acquire_ctx *ctx = &i915->display.restore.reset_ctx; |
87 | struct drm_atomic_state *state; |
88 | int ret; |
89 | |
90 | if (!HAS_DISPLAY(i915)) |
91 | return; |
92 | |
93 | /* reset doesn't touch the display */ |
94 | if (!test_bit(I915_RESET_MODESET, &to_gt(i915)->reset.flags)) |
95 | return; |
96 | |
97 | state = fetch_and_zero(&i915->display.restore.modeset_state); |
98 | if (!state) |
99 | goto unlock; |
100 | |
101 | /* reset doesn't touch the display */ |
102 | if (!gpu_reset_clobbers_display(dev_priv: i915)) { |
103 | /* for testing only restore the display */ |
104 | ret = drm_atomic_helper_commit_duplicated_state(state, ctx); |
105 | if (ret) { |
106 | drm_WARN_ON(&i915->drm, ret == -EDEADLK); |
107 | drm_err(&i915->drm, |
108 | "Restoring old state failed with %i\n" , ret); |
109 | } |
110 | } else { |
111 | /* |
112 | * The display has been reset as well, |
113 | * so need a full re-initialization. |
114 | */ |
115 | intel_pps_unlock_regs_wa(i915); |
116 | intel_display_driver_init_hw(i915); |
117 | intel_clock_gating_init(i915); |
118 | intel_hpd_init(dev_priv: i915); |
119 | |
120 | ret = __intel_display_driver_resume(i915, state, ctx); |
121 | if (ret) |
122 | drm_err(&i915->drm, |
123 | "Restoring old state failed with %i\n" , ret); |
124 | |
125 | intel_hpd_poll_disable(dev_priv: i915); |
126 | } |
127 | |
128 | drm_atomic_state_put(state); |
129 | unlock: |
130 | drm_modeset_drop_locks(ctx); |
131 | drm_modeset_acquire_fini(ctx); |
132 | mutex_unlock(lock: &i915->drm.mode_config.mutex); |
133 | |
134 | clear_bit_unlock(I915_RESET_MODESET, addr: &to_gt(i915)->reset.flags); |
135 | } |
136 | |