| 1 | // SPDX-License-Identifier: MIT |
| 2 | /* |
| 3 | * Copyright © 2022 Intel Corporation |
| 4 | * |
| 5 | * High level crtc/connector/encoder modeset state verification. |
| 6 | */ |
| 7 | |
| 8 | #include <drm/drm_atomic_state_helper.h> |
| 9 | #include <drm/drm_print.h> |
| 10 | |
| 11 | #include "intel_atomic.h" |
| 12 | #include "intel_crtc.h" |
| 13 | #include "intel_crtc_state_dump.h" |
| 14 | #include "intel_cx0_phy.h" |
| 15 | #include "intel_display.h" |
| 16 | #include "intel_display_core.h" |
| 17 | #include "intel_display_types.h" |
| 18 | #include "intel_fdi.h" |
| 19 | #include "intel_lt_phy.h" |
| 20 | #include "intel_modeset_verify.h" |
| 21 | #include "intel_snps_phy.h" |
| 22 | #include "skl_watermark.h" |
| 23 | |
| 24 | /* |
| 25 | * Cross check the actual hw state with our own modeset state tracking (and its |
| 26 | * internal consistency). |
| 27 | */ |
| 28 | static void intel_connector_verify_state(const struct intel_crtc_state *crtc_state, |
| 29 | const struct drm_connector_state *conn_state) |
| 30 | { |
| 31 | struct intel_connector *connector = to_intel_connector(conn_state->connector); |
| 32 | struct intel_display *display = to_intel_display(connector); |
| 33 | |
| 34 | drm_dbg_kms(display->drm, "[CONNECTOR:%d:%s]\n" , |
| 35 | connector->base.base.id, connector->base.name); |
| 36 | |
| 37 | if (connector->get_hw_state(connector)) { |
| 38 | struct intel_encoder *encoder = intel_attached_encoder(connector); |
| 39 | |
| 40 | INTEL_DISPLAY_STATE_WARN(display, !crtc_state, |
| 41 | "connector enabled without attached crtc\n" ); |
| 42 | |
| 43 | if (!crtc_state) |
| 44 | return; |
| 45 | |
| 46 | INTEL_DISPLAY_STATE_WARN(display, !crtc_state->hw.active, |
| 47 | "connector is active, but attached crtc isn't\n" ); |
| 48 | |
| 49 | if (!encoder || encoder->type == INTEL_OUTPUT_DP_MST) |
| 50 | return; |
| 51 | |
| 52 | INTEL_DISPLAY_STATE_WARN(display, |
| 53 | conn_state->best_encoder != &encoder->base, |
| 54 | "atomic encoder doesn't match attached encoder\n" ); |
| 55 | |
| 56 | INTEL_DISPLAY_STATE_WARN(display, conn_state->crtc != encoder->base.crtc, |
| 57 | "attached encoder crtc differs from connector crtc\n" ); |
| 58 | } else { |
| 59 | INTEL_DISPLAY_STATE_WARN(display, crtc_state && crtc_state->hw.active, |
| 60 | "attached crtc is active, but connector isn't\n" ); |
| 61 | INTEL_DISPLAY_STATE_WARN(display, !crtc_state && conn_state->best_encoder, |
| 62 | "best encoder set without crtc!\n" ); |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | static void |
| 67 | verify_connector_state(struct intel_atomic_state *state, |
| 68 | struct intel_crtc *crtc) |
| 69 | { |
| 70 | struct intel_display *display = to_intel_display(state); |
| 71 | struct drm_connector *connector; |
| 72 | const struct drm_connector_state *new_conn_state; |
| 73 | int i; |
| 74 | |
| 75 | for_each_new_connector_in_state(&state->base, connector, new_conn_state, i) { |
| 76 | struct drm_encoder *encoder = connector->encoder; |
| 77 | const struct intel_crtc_state *crtc_state = NULL; |
| 78 | |
| 79 | if (new_conn_state->crtc != &crtc->base) |
| 80 | continue; |
| 81 | |
| 82 | if (crtc) |
| 83 | crtc_state = intel_atomic_get_new_crtc_state(state, crtc); |
| 84 | |
| 85 | intel_connector_verify_state(crtc_state, conn_state: new_conn_state); |
| 86 | |
| 87 | INTEL_DISPLAY_STATE_WARN(display, new_conn_state->best_encoder != encoder, |
| 88 | "connector's atomic encoder doesn't match legacy encoder\n" ); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | static void intel_pipe_config_sanity_check(const struct intel_crtc_state *crtc_state) |
| 93 | { |
| 94 | struct intel_display *display = to_intel_display(crtc_state); |
| 95 | |
| 96 | if (crtc_state->has_pch_encoder) { |
| 97 | int fdi_dotclock = intel_dotclock_calculate(link_freq: intel_fdi_link_freq(display, pipe_config: crtc_state), |
| 98 | m_n: &crtc_state->fdi_m_n); |
| 99 | int dotclock = crtc_state->hw.adjusted_mode.crtc_clock; |
| 100 | |
| 101 | /* |
| 102 | * FDI already provided one idea for the dotclock. |
| 103 | * Yell if the encoder disagrees. Allow for slight |
| 104 | * rounding differences. |
| 105 | */ |
| 106 | drm_WARN(display->drm, abs(fdi_dotclock - dotclock) > 1, |
| 107 | "FDI dotclock and encoder dotclock mismatch, fdi: %i, encoder: %i\n" , |
| 108 | fdi_dotclock, dotclock); |
| 109 | } |
| 110 | } |
| 111 | |
| 112 | static void |
| 113 | verify_encoder_state(struct intel_atomic_state *state) |
| 114 | { |
| 115 | struct intel_display *display = to_intel_display(state); |
| 116 | struct intel_encoder *encoder; |
| 117 | struct drm_connector *connector; |
| 118 | const struct drm_connector_state *old_conn_state, *new_conn_state; |
| 119 | int i; |
| 120 | |
| 121 | for_each_intel_encoder(display->drm, encoder) { |
| 122 | bool enabled = false, found = false; |
| 123 | enum pipe pipe; |
| 124 | |
| 125 | drm_dbg_kms(display->drm, "[ENCODER:%d:%s]\n" , |
| 126 | encoder->base.base.id, |
| 127 | encoder->base.name); |
| 128 | |
| 129 | for_each_oldnew_connector_in_state(&state->base, connector, old_conn_state, |
| 130 | new_conn_state, i) { |
| 131 | if (old_conn_state->best_encoder == &encoder->base) |
| 132 | found = true; |
| 133 | |
| 134 | if (new_conn_state->best_encoder != &encoder->base) |
| 135 | continue; |
| 136 | |
| 137 | found = true; |
| 138 | enabled = true; |
| 139 | |
| 140 | INTEL_DISPLAY_STATE_WARN(display, |
| 141 | new_conn_state->crtc != encoder->base.crtc, |
| 142 | "connector's crtc doesn't match encoder crtc\n" ); |
| 143 | } |
| 144 | |
| 145 | if (!found) |
| 146 | continue; |
| 147 | |
| 148 | INTEL_DISPLAY_STATE_WARN(display, !!encoder->base.crtc != enabled, |
| 149 | "encoder's enabled state mismatch (expected %i, found %i)\n" , |
| 150 | !!encoder->base.crtc, enabled); |
| 151 | |
| 152 | if (!encoder->base.crtc) { |
| 153 | bool active; |
| 154 | |
| 155 | active = encoder->get_hw_state(encoder, &pipe); |
| 156 | INTEL_DISPLAY_STATE_WARN(display, active, |
| 157 | "encoder detached but still enabled on pipe %c.\n" , |
| 158 | pipe_name(pipe)); |
| 159 | } |
| 160 | } |
| 161 | } |
| 162 | |
| 163 | static void |
| 164 | verify_crtc_state(struct intel_atomic_state *state, |
| 165 | struct intel_crtc *crtc) |
| 166 | { |
| 167 | struct intel_display *display = to_intel_display(state); |
| 168 | const struct intel_crtc_state *sw_crtc_state = |
| 169 | intel_atomic_get_new_crtc_state(state, crtc); |
| 170 | struct intel_crtc_state *hw_crtc_state; |
| 171 | struct intel_crtc *primary_crtc; |
| 172 | struct intel_encoder *encoder; |
| 173 | |
| 174 | hw_crtc_state = intel_crtc_state_alloc(crtc); |
| 175 | if (!hw_crtc_state) |
| 176 | return; |
| 177 | |
| 178 | drm_dbg_kms(display->drm, "[CRTC:%d:%s]\n" , crtc->base.base.id, |
| 179 | crtc->base.name); |
| 180 | |
| 181 | hw_crtc_state->hw.enable = sw_crtc_state->hw.enable; |
| 182 | |
| 183 | intel_crtc_get_pipe_config(crtc_state: hw_crtc_state); |
| 184 | |
| 185 | /* we keep both pipes enabled on 830 */ |
| 186 | if (display->platform.i830 && hw_crtc_state->hw.active) |
| 187 | hw_crtc_state->hw.active = sw_crtc_state->hw.active; |
| 188 | |
| 189 | INTEL_DISPLAY_STATE_WARN(display, |
| 190 | sw_crtc_state->hw.active != hw_crtc_state->hw.active, |
| 191 | "crtc active state doesn't match with hw state (expected %i, found %i)\n" , |
| 192 | sw_crtc_state->hw.active, hw_crtc_state->hw.active); |
| 193 | |
| 194 | INTEL_DISPLAY_STATE_WARN(display, crtc->active != sw_crtc_state->hw.active, |
| 195 | "transitional active state does not match atomic hw state (expected %i, found %i)\n" , |
| 196 | sw_crtc_state->hw.active, crtc->active); |
| 197 | |
| 198 | primary_crtc = intel_primary_crtc(crtc_state: sw_crtc_state); |
| 199 | |
| 200 | for_each_encoder_on_crtc(display->drm, &primary_crtc->base, encoder) { |
| 201 | enum pipe pipe; |
| 202 | bool active; |
| 203 | |
| 204 | active = encoder->get_hw_state(encoder, &pipe); |
| 205 | INTEL_DISPLAY_STATE_WARN(display, active != sw_crtc_state->hw.active, |
| 206 | "[ENCODER:%i] active %i with crtc active %i\n" , |
| 207 | encoder->base.base.id, active, |
| 208 | sw_crtc_state->hw.active); |
| 209 | |
| 210 | INTEL_DISPLAY_STATE_WARN(display, active && primary_crtc->pipe != pipe, |
| 211 | "Encoder connected to wrong pipe %c\n" , |
| 212 | pipe_name(pipe)); |
| 213 | |
| 214 | if (active) |
| 215 | intel_encoder_get_config(encoder, crtc_state: hw_crtc_state); |
| 216 | } |
| 217 | |
| 218 | if (!sw_crtc_state->hw.active) |
| 219 | goto destroy_state; |
| 220 | |
| 221 | intel_pipe_config_sanity_check(crtc_state: hw_crtc_state); |
| 222 | |
| 223 | if (!intel_pipe_config_compare(current_config: sw_crtc_state, |
| 224 | pipe_config: hw_crtc_state, fastset: false)) { |
| 225 | INTEL_DISPLAY_STATE_WARN(display, 1, "pipe state doesn't match!\n" ); |
| 226 | intel_crtc_state_dump(crtc_state: hw_crtc_state, NULL, context: "hw state" ); |
| 227 | intel_crtc_state_dump(crtc_state: sw_crtc_state, NULL, context: "sw state" ); |
| 228 | } |
| 229 | |
| 230 | destroy_state: |
| 231 | intel_crtc_destroy_state(crtc: &crtc->base, state: &hw_crtc_state->uapi); |
| 232 | } |
| 233 | |
| 234 | void intel_modeset_verify_crtc(struct intel_atomic_state *state, |
| 235 | struct intel_crtc *crtc) |
| 236 | { |
| 237 | const struct intel_crtc_state *new_crtc_state = |
| 238 | intel_atomic_get_new_crtc_state(state, crtc); |
| 239 | |
| 240 | if (!intel_crtc_needs_modeset(crtc_state: new_crtc_state) && |
| 241 | !intel_crtc_needs_fastset(crtc_state: new_crtc_state)) |
| 242 | return; |
| 243 | |
| 244 | intel_wm_state_verify(state, crtc); |
| 245 | verify_connector_state(state, crtc); |
| 246 | verify_crtc_state(state, crtc); |
| 247 | intel_dpll_state_verify(state, crtc); |
| 248 | intel_mpllb_state_verify(state, crtc); |
| 249 | intel_cx0pll_state_verify(state, crtc); |
| 250 | intel_lt_phy_pll_state_verify(state, crtc); |
| 251 | } |
| 252 | |
| 253 | void intel_modeset_verify_disabled(struct intel_atomic_state *state) |
| 254 | { |
| 255 | verify_encoder_state(state); |
| 256 | verify_connector_state(state, NULL); |
| 257 | intel_dpll_verify_disabled(state); |
| 258 | } |
| 259 | |