static void vc4_hdmi_cec_update_clk_div(struct vc4_hdmi *vc4_hdmi) {}
 #endif
 
-static void vc4_hdmi_enable_scrambling(struct drm_encoder *encoder);
+static int reset_pipe(struct drm_crtc *crtc,
+                       struct drm_modeset_acquire_ctx *ctx)
+{
+       struct drm_atomic_state *state;
+       struct drm_crtc_state *crtc_state;
+       int ret;
+
+       state = drm_atomic_state_alloc(crtc->dev);
+       if (!state)
+               return -ENOMEM;
+
+       state->acquire_ctx = ctx;
+
+       crtc_state = drm_atomic_get_crtc_state(state, crtc);
+       if (IS_ERR(crtc_state)) {
+               ret = PTR_ERR(crtc_state);
+               goto out;
+       }
+
+       crtc_state->connectors_changed = true;
+
+       ret = drm_atomic_commit(state);
+out:
+       drm_atomic_state_put(state);
+
+       return ret;
+}
+
+static int vc4_hdmi_reset_link(struct drm_connector *connector,
+                              struct drm_modeset_acquire_ctx *ctx)
+{
+       struct drm_device *drm = connector->dev;
+       struct vc4_hdmi *vc4_hdmi = connector_to_vc4_hdmi(connector);
+       struct drm_encoder *encoder = &vc4_hdmi->encoder.base;
+       struct drm_connector_state *conn_state;
+       struct drm_crtc_state *crtc_state;
+       struct drm_crtc *crtc;
+       bool scrambling_needed;
+       u8 config;
+       int ret;
+
+       if (!connector)
+               return 0;
+
+       ret = drm_modeset_lock(&drm->mode_config.connection_mutex, ctx);
+       if (ret)
+               return ret;
+
+       conn_state = connector->state;
+       crtc = conn_state->crtc;
+       if (!crtc)
+               return 0;
+
+       ret = drm_modeset_lock(&crtc->mutex, ctx);
+       if (ret)
+               return ret;
+
+       crtc_state = crtc->state;
+       if (!crtc_state->active)
+               return 0;
+
+       if (!vc4_hdmi_supports_scrambling(encoder))
+               return 0;
+
+       scrambling_needed = vc4_hdmi_mode_needs_scrambling(&vc4_hdmi->saved_adjusted_mode,
+                                                          vc4_hdmi->output_bpc,
+                                                          vc4_hdmi->output_format);
+       if (!scrambling_needed)
+               return 0;
+
+       if (conn_state->commit &&
+           !try_wait_for_completion(&conn_state->commit->hw_done))
+               return 0;
+
+       ret = drm_scdc_readb(connector->ddc, SCDC_TMDS_CONFIG, &config);
+       if (ret < 0) {
+               drm_err(drm, "Failed to read TMDS config: %d\n", ret);
+               return 0;
+       }
+
+       if (!!(config & SCDC_SCRAMBLING_ENABLE) == scrambling_needed)
+               return 0;
+
+       /*
+        * HDMI 2.0 says that one should not send scrambled data
+        * prior to configuring the sink scrambling, and that
+        * TMDS clock/data transmission should be suspended when
+        * changing the TMDS clock rate in the sink. So let's
+        * just do a full modeset here, even though some sinks
+        * would be perfectly happy if were to just reconfigure
+        * the SCDC settings on the fly.
+        */
+       return reset_pipe(crtc, ctx);
+}
 
 static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
+                                   struct drm_modeset_acquire_ctx *ctx,
                                    enum drm_connector_status status)
 {
        struct drm_connector *connector = &vc4_hdmi->connector;
         * .adap_enable, which leads to that funtion being called with
         * our mutex held.
         *
+        * A similar situation occurs with
+        * drm_atomic_helper_connector_hdmi_reset_link() that will call
+        * into our KMS hooks if the scrambling was enabled.
+        *
         * Concurrency isn't an issue at the moment since we don't share
         * any state with any of the other frameworks so we can ignore
         * the lock for now.
        cec_s_phys_addr_from_edid(vc4_hdmi->cec_adap, edid);
        kfree(edid);
 
-       vc4_hdmi_enable_scrambling(&vc4_hdmi->encoder.base.base);
+       vc4_hdmi_reset_link(connector, ctx);
 }
 
 static int vc4_hdmi_connector_detect_ctx(struct drm_connector *connector,
                        status = connector_status_connected;
        }
 
-       vc4_hdmi_handle_hotplug(vc4_hdmi, status);
+       vc4_hdmi_handle_hotplug(vc4_hdmi, ctx, status);
        pm_runtime_put(&vc4_hdmi->pdev->dev);
 
        return status;