@@ -4717,6 +4717,9 @@ void intel_ddi_init(struct drm_i915_private *dev_priv, enum port port)
intel_encoder = &intel_dig_port->base;
encoder = &intel_encoder->base;
+ mutex_init(&intel_dig_port->hdcp_mutex);
+ intel_dig_port->num_hdcp_streams = 0;
+
drm_encoder_init(&dev_priv->drm, encoder, &intel_ddi_funcs,
DRM_MODE_ENCODER_TMDS, "DDI %c", port_name(port));
@@ -1309,6 +1309,11 @@ struct intel_digital_port {
enum phy_fia tc_phy_fia;
u8 tc_phy_fia_idx;
+ /* protects num_hdcp_streams reference count */
+ struct mutex hdcp_mutex;
+ /* the number of pipes using HDCP signalling out of this port */
+ unsigned int num_hdcp_streams;
+
void (*write_infoframe)(struct intel_encoder *encoder,
const struct intel_crtc_state *crtc_state,
unsigned int type,
@@ -7535,6 +7535,8 @@ bool intel_dp_init(struct drm_i915_private *dev_priv,
intel_encoder = &intel_dig_port->base;
encoder = &intel_encoder->base;
+ mutex_init(&intel_dig_port->hdcp_mutex);
+
if (drm_encoder_init(&dev_priv->drm, &intel_encoder->base,
&intel_dp_enc_funcs, DRM_MODE_ENCODER_TMDS,
"DP %c", port_name(port)))
@@ -849,6 +849,7 @@ struct intel_connector *intel_hdcp_to_connector(struct intel_hdcp *hdcp)
static void intel_hdcp_update_value(struct intel_connector *connector,
u64 value, bool update_property)
{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
struct intel_hdcp *hdcp = &connector->hdcp;
WARN_ON(!mutex_is_locked(&hdcp->mutex));
@@ -856,6 +857,15 @@ static void intel_hdcp_update_value(struct intel_connector *connector,
if (hdcp->value == value)
return;
+ WARN_ON(!mutex_is_locked(&intel_dig_port->hdcp_mutex));
+
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+ if (!WARN_ON(intel_dig_port->num_hdcp_streams == 0))
+ intel_dig_port->num_hdcp_streams--;
+ } else if (value == DRM_MODE_CONTENT_PROTECTION_ENABLED) {
+ intel_dig_port->num_hdcp_streams++;
+ }
+
hdcp->value = value;
if (update_property)
schedule_work(&hdcp->prop_work);
@@ -872,6 +882,8 @@ static int intel_hdcp_check_link(struct intel_connector *connector)
int ret = 0;
mutex_lock(&hdcp->mutex);
+ mutex_lock(&intel_dig_port->hdcp_mutex);
+
cpu_transcoder = hdcp->cpu_transcoder;
/* Check_link valid only when HDCP1.4 is enabled */
@@ -923,6 +935,7 @@ static int intel_hdcp_check_link(struct intel_connector *connector)
}
out:
+ mutex_unlock(&intel_dig_port->hdcp_mutex);
mutex_unlock(&hdcp->mutex);
return ret;
}
@@ -1994,6 +2007,7 @@ int intel_hdcp_init(struct intel_connector *connector,
int intel_hdcp_enable(struct intel_connector *connector, u8 content_type)
{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
struct intel_hdcp *hdcp = &connector->hdcp;
unsigned long check_link_interval = DRM_HDCP_CHECK_PERIOD_MS;
int ret = -EINVAL;
@@ -2002,6 +2016,7 @@ int intel_hdcp_enable(struct intel_connector *connector, u8 content_type)
return -ENOENT;
mutex_lock(&hdcp->mutex);
+ mutex_lock(&intel_dig_port->hdcp_mutex);
WARN_ON(hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED);
hdcp->content_type = content_type;
@@ -2031,12 +2046,14 @@ int intel_hdcp_enable(struct intel_connector *connector, u8 content_type)
true);
}
+ mutex_unlock(&intel_dig_port->hdcp_mutex);
mutex_unlock(&hdcp->mutex);
return ret;
}
int intel_hdcp_disable(struct intel_connector *connector)
{
+ struct intel_digital_port *intel_dig_port = conn_to_dig_port(connector);
struct intel_hdcp *hdcp = &connector->hdcp;
int ret = 0;
@@ -2044,17 +2061,33 @@ int intel_hdcp_disable(struct intel_connector *connector)
return -ENOENT;
mutex_lock(&hdcp->mutex);
+ mutex_lock(&intel_dig_port->hdcp_mutex);
- if (hdcp->value != DRM_MODE_CONTENT_PROTECTION_UNDESIRED) {
- intel_hdcp_update_value(connector,
- DRM_MODE_CONTENT_PROTECTION_UNDESIRED,
- false);
- if (hdcp->hdcp2_encrypted)
- ret = _intel_hdcp2_disable(connector);
- else if (hdcp->hdcp_encrypted)
- ret = _intel_hdcp_disable(connector);
+ if (hdcp->value == DRM_MODE_CONTENT_PROTECTION_UNDESIRED)
+ goto out;
+
+ intel_hdcp_update_value(connector,
+ DRM_MODE_CONTENT_PROTECTION_UNDESIRED, false);
+
+ /*
+ * If there are other connectors on this port using HDCP, don't disable
+ * it. Instead, toggle the HDCP signalling off on that particular
+ * connector/pipe and exit.
+ */
+ if (intel_dig_port->num_hdcp_streams > 0) {
+ ret = hdcp->shim->toggle_signalling(connector, false);
+ if (ret)
+ DRM_DEBUG_KMS("Failed to disable HDCP signalling\n");
+ goto out;
}
+ if (hdcp->hdcp2_encrypted)
+ ret = _intel_hdcp2_disable(connector);
+ else if (hdcp->hdcp_encrypted)
+ ret = _intel_hdcp_disable(connector);
+
+out:
+ mutex_unlock(&intel_dig_port->hdcp_mutex);
mutex_unlock(&hdcp->mutex);
cancel_delayed_work_sync(&hdcp->check_work);
return ret;
@@ -3243,6 +3243,8 @@ void intel_hdmi_init(struct drm_i915_private *dev_priv,
intel_encoder = &intel_dig_port->base;
+ mutex_init(&intel_dig_port->hdcp_mutex);
+
drm_encoder_init(&dev_priv->drm, &intel_encoder->base,
&intel_hdmi_enc_funcs, DRM_MODE_ENCODER_TMDS,
"HDMI %c", port_name(port));