[PATCH] drm/i915/hdcp: Fail fast when HDCP Type 1 is unsupported via MST
From: George D. Sworo
Date: Thu Sep 24 2026 - 23:52:24 EST
HDCP Content Type 1 requires HDCP 2.x support from both the source and
the downstream MST sink. A request for a sink that supports HDCP 1.x but
cannot use HDCP 2.x currently passes atomic validation and fails later in
the HDCP enable path.
Query the downstream sink through its MST remote AUX channel during HDCP
initialization. Cache the result only when the sink reports HDCP support
and either the source or sink lacks HDCP 2.x. Treat a missing callback,
probe failure, or no reported HDCP support as unknown and non-fatal.
Use the cached result to reject unsupported Content Type 1 requests with
-EOPNOTSUPP in intel_hdcp_atomic_check(), and propagate the error through
the digital connector atomic check. This keeps remote AUX transactions
out of the atomic check path.
Signed-off-by: George D. Sworo <george.d.sworo@xxxxxxxxx>
---
drivers/gpu/drm/i915/display/intel_atomic.c | 6 ++-
.../drm/i915/display/intel_display_types.h | 2 +
drivers/gpu/drm/i915/display/intel_hdcp.c | 50 +++++++++++++++++--
drivers/gpu/drm/i915/display/intel_hdcp.h | 6 +--
4 files changed, 55 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/i915/display/intel_atomic.c b/drivers/gpu/drm/i915/display/intel_atomic.c
index b66c2d4ba2b3..c6383e27e62b 100644
--- a/drivers/gpu/drm/i915/display/intel_atomic.c
+++ b/drivers/gpu/drm/i915/display/intel_atomic.c
@@ -123,8 +123,12 @@ int intel_digital_connector_atomic_check(struct drm_connector *conn,
struct intel_digital_connector_state *old_conn_state =
to_intel_digital_connector_state(old_state);
struct drm_crtc_state *crtc_state;
+ /* Propagate HDCP capability failures during atomic validation. */
+ int ret;
- intel_hdcp_atomic_check(conn, old_state, new_state);
+ ret = intel_hdcp_atomic_check(conn, old_state, new_state);
+ if (ret)
+ return ret;
if (!new_state->crtc)
return 0;
diff --git a/drivers/gpu/drm/i915/display/intel_display_types.h b/drivers/gpu/drm/i915/display/intel_display_types.h
index 79f30660c2b6..1c169849e502 100644
--- a/drivers/gpu/drm/i915/display/intel_display_types.h
+++ b/drivers/gpu/drm/i915/display/intel_display_types.h
@@ -605,6 +605,8 @@ struct intel_connector {
struct {
struct drm_dp_mst_port *port;
struct intel_dp *dp;
+ /* Used to reject unsupported HDCP Type1 requests during atomic check. */
+ bool type1_unsupported;
} mst;
struct {
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c
index e56df337dc6d..7490fc0fd96a 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.c
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.c
@@ -312,6 +312,26 @@ static void intel_hdcp_get_remote_capability(struct intel_connector *connector,
*hdcp2_capable = false;
}
+/* Determine whether atomic check should reject Type1 for this MST sink. */
+static bool
+intel_hdcp_mst_type1_is_unsupported(struct intel_connector *connector,
+ const struct intel_hdcp_shim *shim)
+{
+ struct intel_hdcp *hdcp = &connector->hdcp;
+ bool hdcp_capable = false, hdcp2_capable = false;
+ int ret;
+
+ if (!shim->get_remote_hdcp_capability)
+ return false;
+
+ ret = shim->get_remote_hdcp_capability(connector, &hdcp_capable,
+ &hdcp2_capable);
+ if (ret)
+ return false;
+
+ return hdcp_capable && (!hdcp->hdcp2_supported || !hdcp2_capable);
+}
+
static bool intel_hdcp_in_use(struct intel_display *display,
enum transcoder cpu_transcoder, enum port port)
{
@@ -2421,6 +2441,10 @@ int intel_hdcp_init(struct intel_connector *connector,
if (is_hdcp2_supported(display))
intel_hdcp2_init(connector, dig_port, shim);
+ /* Cache the remote capability before atomic checks can run. */
+ connector->mst.type1_unsupported =
+ intel_hdcp_mst_type1_is_unsupported(connector, shim);
+
ret = drm_connector_attach_content_protection_property(&connector->base,
hdcp->hdcp2_supported);
if (ret) {
@@ -2684,10 +2708,11 @@ void intel_hdcp_cleanup(struct intel_connector *connector)
mutex_unlock(&hdcp->mutex);
}
-void intel_hdcp_atomic_check(struct drm_connector *connector,
- struct drm_connector_state *old_state,
- struct drm_connector_state *new_state)
+int intel_hdcp_atomic_check(struct drm_connector *connector,
+ struct drm_connector_state *old_state,
+ struct drm_connector_state *new_state)
{
+ struct intel_connector *intel_connector = to_intel_connector(connector);
u64 old_cp = old_state->content_protection;
u64 new_cp = new_state->content_protection;
struct drm_crtc_state *crtc_state;
@@ -2700,7 +2725,20 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
if (old_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)
new_state->content_protection =
DRM_MODE_CONTENT_PROTECTION_DESIRED;
- return;
+ return 0;
+ }
+
+ /*
+ * Fail fast if userspace asks for Type1 but neither the platform nor
+ * the downstream sink can do HDCP 2.x, instead of only discovering
+ * this once intel_hdcp_enable() is reached.
+ */
+ if (new_cp == DRM_MODE_CONTENT_PROTECTION_DESIRED &&
+ new_state->hdcp_content_type == DRM_MODE_HDCP_CONTENT_TYPE1 &&
+ intel_connector->mst.type1_unsupported) {
+ drm_dbg_kms(connector->dev,
+ "HDCP Type1 requested without HDCP 2.x support\n");
+ return -EOPNOTSUPP;
}
crtc_state = drm_atomic_get_new_crtc_state(new_state->state,
@@ -2725,10 +2763,12 @@ void intel_hdcp_atomic_check(struct drm_connector *connector,
new_cp == DRM_MODE_CONTENT_PROTECTION_ENABLED)) {
if (old_state->hdcp_content_type ==
new_state->hdcp_content_type)
- return;
+ return 0;
}
crtc_state->mode_changed = true;
+
+ return 0;
}
/* Handles the CP_IRQ raised from the DP HDCP sink */
diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.h b/drivers/gpu/drm/i915/display/intel_hdcp.h
index efe86808e17e..34661b074784 100644
--- a/drivers/gpu/drm/i915/display/intel_hdcp.h
+++ b/drivers/gpu/drm/i915/display/intel_hdcp.h
@@ -22,9 +22,9 @@ struct intel_hdcp_shim;
struct seq_file;
enum port;
-void intel_hdcp_atomic_check(struct drm_connector *connector,
- struct drm_connector_state *old_state,
- struct drm_connector_state *new_state);
+int intel_hdcp_atomic_check(struct drm_connector *connector,
+ struct drm_connector_state *old_state,
+ struct drm_connector_state *new_state);
int intel_hdcp_init(struct intel_connector *connector,
struct intel_digital_port *dig_port,
const struct intel_hdcp_shim *hdcp_shim);
base-commit: 999838292166407bbe911a41dee23112c49e9d44
--
2.43.0